From 5f7375803961b51866cfa9b10275106e7b88329c Mon Sep 17 00:00:00 2001 From: Elivo Date: Wed, 23 Apr 2025 11:49:31 +0800 Subject: [PATCH] =?UTF-8?q?Logger=EF=BC=8C=E6=94=AF=E6=8C=81=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=20Name=20=E6=8B=86=E5=88=86=E4=B8=BA=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E7=9A=84=E6=97=A5=E5=BF=97=E6=96=87=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Apewer/BytesUtility.cs | 2 +- Apewer/CronAttribute.cs | 2 +- Apewer/Logger.cs | 245 ++++++++++++++++--------------------- Apewer/Models/LogItem.cs | 61 +++++++-- Apewer/RuntimeUtility.cs | 2 +- Apewer/Web/HttpListener.cs | 2 +- Apewer/_Extensions.cs | 20 +-- 7 files changed, 162 insertions(+), 172 deletions(-) diff --git a/Apewer/BytesUtility.cs b/Apewer/BytesUtility.cs index 4b76127..3ea3dfc 100644 --- a/Apewer/BytesUtility.cs +++ b/Apewer/BytesUtility.cs @@ -757,7 +757,7 @@ namespace Apewer } catch (Exception ex) { - Logger.Internals.Exception($"{nameof(BytesUtility)}.{nameof(Write)}", ex); + Logger.Internals.Exception(ex, $"{nameof(BytesUtility)}.{nameof(Write)}"); } return total; } diff --git a/Apewer/CronAttribute.cs b/Apewer/CronAttribute.cs index c487c36..4835fa4 100644 --- a/Apewer/CronAttribute.cs +++ b/Apewer/CronAttribute.cs @@ -181,7 +181,7 @@ namespace Apewer } catch (Exception ex) { - _logger.Exception(sender, ex.InnerException); + _logger.Exception(ex.InnerException, sender); } RuntimeUtility.Dispose(instance); } diff --git a/Apewer/Logger.cs b/Apewer/Logger.cs index e041174..1091b5d 100644 --- a/Apewer/Logger.cs +++ b/Apewer/Logger.cs @@ -1,14 +1,9 @@ -using Apewer.Internals; -using Apewer.Models; +using Apewer.Models; using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; -using System.Threading; - -using static System.ConsoleColor; namespace Apewer { @@ -23,6 +18,9 @@ namespace Apewer private int _reserved = -1; private LogCollector _collector = null; private string _lastdate = null; + private Action[] _actions = new Action[0]; + private List> _actions_list = new List>(); + private object _locker = new object(); /// 当前日志记录器的名称。 public string Name { get; set; } @@ -52,6 +50,19 @@ namespace Apewer } } + /// 使用自定义方法。 + public void UseAction(Action action) + { + if (action != null) + { + lock (_actions_list) + { + _actions_list.Add(action); + _actions = _actions_list.ToArray(); + } + } + } + /// 日志文件的保留天数(不包含今天)。指定 时此属性无效。 /// 默认值:-1,保留所有日志。 public int FileReserved { get => _reserved; set => _reserved = value; } @@ -60,66 +71,67 @@ namespace Apewer /// 默认值:NULL,删除文件。 public LogCollector Collector { get => _collector; set => _collector = value; } - private void Invoke(Action action) + void Inbackground(Action action) { if (Background) { - RuntimeUtility.InBackground(action, true); + RuntimeUtility.InBackground(() => + { + lock (_locker) + { + action.Invoke(); + } + }, true); return; } - try { action.Invoke(); } catch { } + lock (_locker) + { + try { action.Invoke(); } catch { } + } } - internal void Colorful(object sender, string tag, Nullable color, object[] content, Exception exception = null) + internal void Output(string tag, object[] content, Exception exception = null) { if (!Enabled) return; - Invoke(() => + Inbackground(() => { var item = new LogItem(); - item.Sender = sender; + item.Logger = this; item.Tag = tag; - item.Color = color; - item.Content = MergeContent(content); + item.Content = content; item.Exception = exception; - if (UseConsole) ToConsole(item); + var text = (UseConsole || UseFile) ? item.ToString() : null; + if (UseConsole) + { + lock (ConsoleLocker) + { + System.Console.WriteLine(text); + } + } if (UseFile) { - var plain = ToText(item); lock (_cache_locker) { if (_cache_capacity > 0) { - _cache_array[_cache_count] = plain; + _cache_array[_cache_count] = text; _cache_count += 1; if (_cache_count == _cache_capacity) Flush(); } else { - ToFile(plain, this); + ToFile(text, this); } } } - }); - } - /// 记录异常。 - internal void InnerException(object sender, Exception exception) - { - if (!Enabled) return; - var type = null as string; - var content = null as string; - if (exception != null) - { - try + var actions = _actions; + foreach (var action in actions) { - type = exception.GetType().Name; - content = MergeContent(new object[] { type, exception.Message }); + action.Invoke(item); } - catch { } - } - if (content == null) content = "无效的 Exception 实例。"; - Colorful(sender, "Exception", DarkMagenta, new object[] { type, content }, exception); + }); } /// 创建新实例。 @@ -183,94 +195,6 @@ namespace Apewer /// 获取用于保存日志文件的路径。 public static Func FilePathGetter { get; set; } - private static string MergeContent(object[] content) => TextUtility.Join(" | ", content); - - private static string FormatSender(object sender) - { - if (sender == null) return null; - if (sender is string) return sender as string; - if (sender is Type) return ((Type)sender).Name; - return sender.GetType().Name; - } - - // 向控制台输出。 - private static void ToConsole(LogItem item) - { - var hasTag = !string.IsNullOrEmpty(item.Tag); - var sender = FormatSender(item.Sender); - var colorful = item.Color != null; - lock (ConsoleLocker) - { - if (!colorful) - { - System.Console.WriteLine(ToText(item)); - return; - } - - System.Console.ResetColor(); - - System.Console.ForegroundColor = DarkGray; - System.Console.Write(item.Clock); - - System.Console.ResetColor(); - - if (hasTag) - { - System.Console.Write(" "); - if (item.Color != null) - { - System.Console.BackgroundColor = item.Color.Value; - System.Console.ForegroundColor = White; - } - System.Console.Write(" "); - System.Console.Write(item.Tag); - System.Console.Write(" "); - } - - System.Console.ResetColor(); - - if (!string.IsNullOrEmpty(sender)) - { - System.Console.Write(" <"); - System.Console.Write(sender); - System.Console.Write(">"); - } - - if (!string.IsNullOrEmpty(item.Content)) - { - System.Console.Write(" "); - System.Console.Write(item.Content); - } - - System.Console.WriteLine(); - } - } - - private static string ToText(LogItem item) - { - var sb = new StringBuilder(); - var sender = FormatSender(item.Sender); - sb.Append(item.Clock); - if (!string.IsNullOrEmpty(item.Tag)) - { - sb.Append(" ["); - sb.Append(item.Tag); - sb.Append("]"); - } - if (!string.IsNullOrEmpty(sender)) - { - sb.Append(" <"); - sb.Append(sender); - sb.Append(">"); - } - if (!string.IsNullOrEmpty(item.Content)) - { - sb.Append(" "); - sb.Append(item.Content); - } - return sb.ToString(); - } - // 向日志文件输出文本,文件名按日期自动生成。 private static string ToFile(string plain, Logger logger, bool crlf = true) { @@ -287,7 +211,7 @@ namespace Apewer /// 获取日志文件路径发生错误时返回 NULL 值。 /// 默认例:
d:\app\log\1970-01-01.log
d:\www\app_data\log\1970-01-01.log
- public static string GetFilePath(Logger logger = null) + static string GetFilePath(Logger logger = null) { var getter = FilePathGetter; if (getter != null) try { return getter.Invoke(logger); } catch { } @@ -304,7 +228,10 @@ namespace Apewer // 文件不存在时创建新文件,无法创建时返回。 var now = DateTime.Now; var date = now.Lucid(true, false, false, false); - var filePath = Path.Combine(logDir, date + FileExt); + var fileName = (logger == null || logger.Name.IsEmpty()) ? $"{date}{FileExt}" : $"{date}_{logger.Name}{FileExt}"; + + // 写入文件。 + var filePath = Path.Combine(logDir, fileName); if (!StorageUtility.FileExists(filePath)) { StorageUtility.WriteFile(filePath, TextUtility.Bom); @@ -314,6 +241,7 @@ namespace Apewer // 检查过期文件。 if (logger != null && getter == null && logger._reserved > -1) { + // 每天执行一次。 if (date != logger._lastdate) { logger._lastdate = date; @@ -336,14 +264,30 @@ namespace Apewer var paths = StorageUtility.GetSubFiles(logDir); foreach (var path in paths) { - var fileName = Path.GetFileNameWithoutExtension(path); + // 文件名后缀 var fileExt = Path.GetExtension(path); - if (fileName.Length != 10) continue; + if (fileExt != FileExt) continue; + + // 文件名格式 + var fileName = Path.GetFileName(path); if (fileName[4] != '-') continue; if (fileName[7] != '-') continue; - if (fileExt != FileExt) continue; + if (fileName[10] != '.' && fileName[10] != '_') continue; + + // 确定文件名是属于当前的 Logger + if (logger.Name.IsEmpty()) + { + if (fileName.Length != 14) continue; // 2008-08-08.log + } + else + { + if (fileName.Length < 16) continue; // 2008-08-08_a.log + var loggerName = fileName.Substring(11, fileName.Length - 15); + if (loggerName != logger.Name) continue; + } - var dt = ClockUtility.ParseLucid(fileName); + // 从文件名解析日期 + var dt = ClockUtility.ParseLucid(fileName.Substring(0, 10)); if (dt == null || !dt.HasValue) continue; var days = Convert.ToInt32(Convert.ToInt64((today - dt.Value).TotalMilliseconds) / 86400000L); @@ -359,14 +303,14 @@ namespace Apewer #region 默认实列。 - private static Logger _default = new Logger("Apewer.Logger.Default", true, false, true); - private static Logger _console = new Logger("Apewer.Logger.Console", true, false, true); - private static Logger _web = new Logger("Apewer.Logger.Web", true, false, true); + private static Logger _default = new Logger(null, true, false, true); + private static Logger _console = new Logger(null, true, false, true); + private static Logger _web = new Logger(null, true, false, true); #if DEBUG - internal static Logger _internals = new Logger("Apewer.Logger.Internals", true, false, true); + internal static Logger _internals = new Logger(null, true, false, true); #else - internal static Logger _internals = new Logger("Apewer.Logger.Internals", true, false, false); + internal static Logger _internals = new Logger(null, true, false, false); #endif /// 内部的日志记录程序。 @@ -388,16 +332,10 @@ namespace Apewer const string FileExt = ".log"; /// 使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。 - public static void Write(params object[] content) => Default.Colorful(null, null, null, content, null); + public static void Write(params object[] content) => Default.Output(null, content, null); /// 使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。 - public static void Write(params object[] content) => Default.Colorful(typeof(T), null, null, content, null); - - /// 使用 Logger.Default 写入日志,自动添加时间和日期。 - public static void Write(Exception exception) => Default.InnerException(null, exception); - - /// 使用 Logger.Default 写入日志,自动添加时间和日期。 - public static void Write(Exception exception) => Default.InnerException(typeof(T), exception); + public static void Write(Exception exception, params object[] content) => Default.Output(null, content, exception); /// 压缩日志文件的内容,另存为 ZIP 文件,并删除原日志文件。 public static void CollectToZip(string path) @@ -438,6 +376,29 @@ namespace Apewer #endregion + #region public + + /// 记录异常。 + public void Exception(T exception, params object[] content) where T : Exception => Output("Exception", content, exception); + + /// 记录错误。多个 Content 参数将以“ | ”分隔。 + public void Error(params object[] content) => Output("Error", content, null); + + /// 记录警告。多个 Content 参数将以“ | ”分隔。 + public void Warning(params object[] content) => Output("Warning", content, null); + + /// 记录警告。多个 Content 参数将以“ | ”分隔。 + public void Info(params object[] content) => Output("Info", content, null); + + /// 记录文本。多个 Content 参数将以“ | ”分隔。 + public void Text(params object[] content) => Output(null, content, null); + + /// 记录调试。多个 Content 参数将以“ | ”分隔。 + [Conditional("DEBUG")] + public void Debug(params object[] content) => Output("Debug", content, null); + + #endregion + } } diff --git a/Apewer/Models/LogItem.cs b/Apewer/Models/LogItem.cs index 1a34185..7759e65 100644 --- a/Apewer/Models/LogItem.cs +++ b/Apewer/Models/LogItem.cs @@ -1,4 +1,5 @@ using System; +using System.Text; namespace Apewer.Models { @@ -8,27 +9,71 @@ namespace Apewer.Models public sealed class LogItem { + /// 自定义日志条目格式化程序。 + public static Func Formatter { get; set; } + + /// 日志记录程序。 + public Logger Logger { get; internal set; } + /// 记录日志的时间。 - public string Clock { get; private set; } + public DateTime Clock { get; private set; } /// 日志的标签。 public string Tag { get; internal set; } - internal Nullable Color { get; set; } - /// 日志的文本内容。 - public string Content { get; internal set; } + public object[] Content { get; internal set; } /// 日志中包含的异常对象。 public Exception Exception { get; internal set; } - /// 生成日志的对象。 - public object Sender { get; internal set; } - /// internal LogItem() { - Clock = ClockUtility.Lucid(DateTime.Now); + Clock = DateTime.Now; + } + + /// + public override string ToString() + { + var formatter = Formatter; + if (formatter != null) return formatter.Invoke(this); + + var sb = new StringBuilder(); + var written = 0; + + sb.Append(ClockUtility.Lucid(Clock)); + written++; + + if (!string.IsNullOrEmpty(Tag)) + { + sb.Append(" ["); + sb.Append(Tag); + sb.Append("]"); + written++; + } + + if (Exception != null) + { + sb.Append(" <"); + sb.Append(Exception.GetType().Name); + sb.Append("> "); + sb.Append(Exception.Message()); + written++; + } + + if (Content != null && Content.Length > 0) + { + var content = TextUtility.Join(" | ", Content); + if (!string.IsNullOrEmpty(content)) + { + if(written > 1) sb.Append(" | "); + else sb.Append(" "); + sb.Append(content); + } + } + + return sb.ToString(); } } diff --git a/Apewer/RuntimeUtility.cs b/Apewer/RuntimeUtility.cs index 2841888..8d6a069 100644 --- a/Apewer/RuntimeUtility.cs +++ b/Apewer/RuntimeUtility.cs @@ -954,7 +954,7 @@ namespace Apewer catch (Exception ex) { var ex2 = ex.InnerException ?? ex; - Logger.Internals.Exception(typeof(RuntimeUtility), ex2); + Logger.Internals.Exception(ex2, typeof(RuntimeUtility)); if (!catchException) throw ex2; } diff --git a/Apewer/Web/HttpListener.cs b/Apewer/Web/HttpListener.cs index e206396..fd24e8a 100644 --- a/Apewer/Web/HttpListener.cs +++ b/Apewer/Web/HttpListener.cs @@ -51,7 +51,7 @@ namespace Apewer.Web } catch (Exception ex) { - Logger.Web.Exception(this, ex); + Logger.Web.Exception(ex, this); Logger.Web.Info(this, $"在 {prefix} 启动失败。"); return ex; } diff --git a/Apewer/_Extensions.cs b/Apewer/_Extensions.cs index 2fb8ae7..81bc6d7 100644 --- a/Apewer/_Extensions.cs +++ b/Apewer/_Extensions.cs @@ -358,24 +358,8 @@ public static class Extensions #region Logger - /// 记录异常。 - public static void Exception(this Logger logger, object sender, Exception exception) => logger?.InnerException(sender, exception); - - /// 记录错误。多个 Content 参数将以“ | ”分隔。 - public static void Error(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, "Error", ConsoleColor.DarkRed, content, null); - - /// 记录警告。多个 Content 参数将以“ | ”分隔。 - public static void Warning(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, "Warning", ConsoleColor.DarkYellow, content, null); - - /// 记录警告。多个 Content 参数将以“ | ”分隔。 - public static void Info(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, "Info", ConsoleColor.DarkBlue, content, null); - - /// 记录文本。多个 Content 参数将以“ | ”分隔。 - public static void Text(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, null, null, content, null); - - /// 记录调试。多个 Content 参数将以“ | ”分隔。 - [Conditional("DEBUG")] - public static void Debug(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, null, null, content, null); + /// 记录文本。多个 Content 参数将以“ | ”分隔,此方法等同于 + public static void Write(this Logger logger, object sender, params object[] content) => logger?.Text(sender, null, content, null); #endregion