增加日志
工单连接增加日志
This commit is contained in:
1 parent
1b0aa81263
commit
c999a80048
2 files changed
+278
-1
No files matched your search
@@ -6,6 +6,7 @@ using OrderSupportTicket.Models.Api;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using WxOrder.Framework.Helpers;
|
||||
|
||||
namespace OrderSupportTicket.Services;
|
||||
|
||||
@@ -76,6 +77,7 @@ public sealed class EventSourceTicketRealtimeService
|
||||
{
|
||||
try
|
||||
{
|
||||
NLogger.Instance.Info("工单服务连接成");
|
||||
await source.StartAsync();
|
||||
lock (_sync)
|
||||
{
|
||||
@@ -86,7 +88,7 @@ public sealed class EventSourceTicketRealtimeService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogSave.Add("工单服务连接失败:" + ex.Message);
|
||||
NLogger.Instance.Error("工单服务连接失败:" + ex.Message);
|
||||
lock (_sync)
|
||||
{
|
||||
if (!_running || !ReferenceEquals(_eventSource, source))
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace WxOrder.Framework.Helpers
|
||||
{
|
||||
/// <summary> 日志消息级别。 </summary>
|
||||
public enum LogMessageLevel
|
||||
{
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warn = 3,
|
||||
Error = 4,
|
||||
Fatal = 5,
|
||||
None = 6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调试输出桥接。默认输出到 System.Diagnostics.Debug,
|
||||
/// 可在外部重新赋值 DebugPrint 以接管控件台/UI 输出。
|
||||
/// </summary>
|
||||
public static class DebugLog
|
||||
{
|
||||
/// <summary> 调试输出回调。默认写到 Debug 输出窗口。 </summary>
|
||||
public static Action<string>? DebugPrint { get; set; }
|
||||
= msg => System.Diagnostics.Debug.WriteLine(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 默认日志逻辑类(不依赖任何第三方日志库)。
|
||||
/// 日志文件按日期分目录存放,并在跨天时自动切换:
|
||||
/// {logDirectory}/yyyy-MM/yyyy-MM-dd.log,例如 log/2022-12/2022-12-12.log
|
||||
/// </summary>
|
||||
public sealed class NLogger : IDisposable
|
||||
{
|
||||
private const string DefaultName = "Default";
|
||||
|
||||
private static readonly object _lock = new object();
|
||||
private static NLogger? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// 同一进程内所有实例共用的写入锁。
|
||||
/// 多个实例(不同 Name)会写入同一个按日期命名的文件,需要串行化写入。
|
||||
/// </summary>
|
||||
private static readonly object _fileLock = new object();
|
||||
|
||||
/// <summary> 获取 NLogger 的唯一单例实例(线程安全)。 </summary>
|
||||
public static NLogger Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = new NLogger(DefaultName);
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> 静态工厂:根据名称创建日志实例。 </summary>
|
||||
public static NLogger Create(string name, string? logDirectory = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
throw new ArgumentException("日志名称不能为空。", nameof(name));
|
||||
|
||||
return new NLogger(name, logDirectory);
|
||||
}
|
||||
|
||||
/// <summary> 静态工厂:创建使用默认名称的日志实例。 </summary>
|
||||
public static NLogger Create() => new NLogger(DefaultName);
|
||||
|
||||
/// <summary> 日志名称(写入每行日志中,用于区分来源,不再决定文件名)。 </summary>
|
||||
public string Name { get; }
|
||||
|
||||
public bool IsDebugEnabled { get; set; } = true;
|
||||
|
||||
public bool IsDebugWriteLineEnabled { get; set; } = true;
|
||||
|
||||
public LogMessageLevel MinimumLevel { get; set; } = LogMessageLevel.Trace;
|
||||
|
||||
/// <summary> 日志根目录。 </summary>
|
||||
public string LogDirectory { get; }
|
||||
|
||||
private string? _currentFilePath;
|
||||
private StreamWriter? _writer;
|
||||
private bool _disposed;
|
||||
|
||||
private NLogger(string name, string? logDirectory = null)
|
||||
{
|
||||
Name = name;
|
||||
LogDirectory = logDirectory ?? Path.Combine(AppContext.BaseDirectory, "log");
|
||||
// 文件在第一次写入时按当前日期惰性创建,构造阶段不打开文件。
|
||||
}
|
||||
|
||||
#region 日志方法
|
||||
|
||||
public void Trace(string message, Exception? exception = null)
|
||||
=> Execute(LogMessageLevel.Trace, message, exception);
|
||||
|
||||
public void Debug(string message, Exception? exception = null)
|
||||
=> Execute(LogMessageLevel.Debug, message, exception);
|
||||
|
||||
public void Info(string message, Exception? exception = null)
|
||||
=> Execute(LogMessageLevel.Info, message, exception);
|
||||
|
||||
public void Warn(string message, Exception? exception = null)
|
||||
=> Execute(LogMessageLevel.Warn, message, exception);
|
||||
|
||||
public void Error(string message, Exception? exception = null)
|
||||
=> Execute(LogMessageLevel.Error, message, exception);
|
||||
|
||||
public void Fatal(string message, Exception? exception = null)
|
||||
=> Execute(LogMessageLevel.Fatal, message, exception);
|
||||
|
||||
public void Log(string message, LogMessageLevel level, Exception? exception = null)
|
||||
=> Execute(level, message, exception);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部逻辑
|
||||
|
||||
private bool CheckEnabled(LogMessageLevel level)
|
||||
{
|
||||
if (level == LogMessageLevel.None)
|
||||
return false;
|
||||
|
||||
if (level < MinimumLevel)
|
||||
return false;
|
||||
|
||||
if (level == LogMessageLevel.Trace || level == LogMessageLevel.Debug)
|
||||
return IsDebugEnabled;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Execute(LogMessageLevel level, string message, Exception? exception)
|
||||
{
|
||||
if (CheckEnabled(level))
|
||||
{
|
||||
WriteToFile(level, message, exception);
|
||||
}
|
||||
|
||||
if (IsDebugWriteLineEnabled)
|
||||
{
|
||||
string logOutput = "[" + level + "] " + message;
|
||||
if (exception != null)
|
||||
{
|
||||
logOutput += Environment.NewLine + exception;
|
||||
}
|
||||
|
||||
Action<string>? handler = DebugLog.DebugPrint;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(logOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> 根据日期生成日志文件路径:{根目录}/yyyy-MM/yyyy-MM-dd.log </summary>
|
||||
private string GetLogFilePath(DateTime time)
|
||||
{
|
||||
return Path.Combine(
|
||||
LogDirectory,
|
||||
time.ToString("yyyy-MM"),
|
||||
time.ToString("yyyy-MM-dd") + ".log");
|
||||
}
|
||||
|
||||
/// <summary> 写入当天的日志文件,跨天时自动切换到新文件。 </summary>
|
||||
private void WriteToFile(LogMessageLevel level, string message, Exception? exception)
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append('[')
|
||||
.Append(now.ToString("yyyy-MM-dd HH:mm:ss.fff"))
|
||||
.Append("] [")
|
||||
.Append(level)
|
||||
.Append("] [")
|
||||
.Append(Name)
|
||||
.Append("] ")
|
||||
.AppendLine(message);
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
sb.AppendLine(exception.ToString());
|
||||
}
|
||||
|
||||
string line = sb.ToString();
|
||||
|
||||
lock (_fileLock)
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
try
|
||||
{
|
||||
EnsureWriter(GetLogFilePath(now));
|
||||
_writer!.Write(line);
|
||||
_writer.Flush();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 日志写入失败不应影响主流程,这里吞掉异常。
|
||||
// 重置写入器,下次写入时重新尝试打开文件。
|
||||
CloseWriter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> 确保写入器指向指定路径;路径变化(跨天/跨月)时关闭旧文件并打开新文件。 </summary>
|
||||
private void EnsureWriter(string path)
|
||||
{
|
||||
if (_writer != null && string.Equals(_currentFilePath, path, StringComparison.Ordinal))
|
||||
return;
|
||||
|
||||
CloseWriter();
|
||||
|
||||
string? dir = Path.GetDirectoryName(path);
|
||||
if (!string.IsNullOrEmpty(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
var fs = new FileStream(
|
||||
path,
|
||||
FileMode.Append,
|
||||
FileAccess.Write,
|
||||
FileShare.ReadWrite);
|
||||
|
||||
_writer = new StreamWriter(fs, new UTF8Encoding(false))
|
||||
{
|
||||
AutoFlush = false
|
||||
};
|
||||
_currentFilePath = path;
|
||||
}
|
||||
|
||||
private void CloseWriter()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_writer != null)
|
||||
{
|
||||
_writer.Flush();
|
||||
_writer.Dispose();
|
||||
}
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
finally
|
||||
{
|
||||
_writer = null;
|
||||
_currentFilePath = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_fileLock)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
CloseWriter();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user