Browse Source

Logger,支持根据 Name 拆分为不同的日志文件。

master
王厅 1 month ago
parent
commit
5f73758039
  1. 2
      Apewer/BytesUtility.cs
  2. 2
      Apewer/CronAttribute.cs
  3. 243
      Apewer/Logger.cs
  4. 61
      Apewer/Models/LogItem.cs
  5. 2
      Apewer/RuntimeUtility.cs
  6. 2
      Apewer/Web/HttpListener.cs
  7. 20
      Apewer/_Extensions.cs

2
Apewer/BytesUtility.cs

@ -757,7 +757,7 @@ namespace Apewer
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Internals.Exception($"{nameof(BytesUtility)}.{nameof(Write)}", ex); Logger.Internals.Exception(ex, $"{nameof(BytesUtility)}.{nameof(Write)}");
} }
return total; return total;
} }

2
Apewer/CronAttribute.cs

@ -181,7 +181,7 @@ namespace Apewer
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Exception(sender, ex.InnerException); _logger.Exception(ex.InnerException, sender);
} }
RuntimeUtility.Dispose(instance); RuntimeUtility.Dispose(instance);
} }

243
Apewer/Logger.cs

@ -1,14 +1,9 @@
using Apewer.Internals; using Apewer.Models;
using Apewer.Models;
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading;
using static System.ConsoleColor;
namespace Apewer namespace Apewer
{ {
@ -23,6 +18,9 @@ namespace Apewer
private int _reserved = -1; private int _reserved = -1;
private LogCollector _collector = null; private LogCollector _collector = null;
private string _lastdate = null; private string _lastdate = null;
private Action<LogItem>[] _actions = new Action<LogItem>[0];
private List<Action<LogItem>> _actions_list = new List<Action<LogItem>>();
private object _locker = new object();
/// <summary>当前日志记录器的名称。</summary> /// <summary>当前日志记录器的名称。</summary>
public string Name { get; set; } public string Name { get; set; }
@ -52,6 +50,19 @@ namespace Apewer
} }
} }
/// <summary>使用自定义方法。</summary>
public void UseAction(Action<LogItem> action)
{
if (action != null)
{
lock (_actions_list)
{
_actions_list.Add(action);
_actions = _actions_list.ToArray();
}
}
}
/// <summary>日志文件的保留天数(不包含今天)。指定 <see cref="FilePathGetter"/> 时此属性无效。</summary> /// <summary>日志文件的保留天数(不包含今天)。指定 <see cref="FilePathGetter"/> 时此属性无效。</summary>
/// <remarks>默认值:-1,保留所有日志。</remarks> /// <remarks>默认值:-1,保留所有日志。</remarks>
public int FileReserved { get => _reserved; set => _reserved = value; } public int FileReserved { get => _reserved; set => _reserved = value; }
@ -60,66 +71,67 @@ namespace Apewer
/// <remarks>默认值:NULL,删除文件。</remarks> /// <remarks>默认值:NULL,删除文件。</remarks>
public LogCollector Collector { get => _collector; set => _collector = value; } public LogCollector Collector { get => _collector; set => _collector = value; }
private void Invoke(Action action) void Inbackground(Action action)
{ {
if (Background) if (Background)
{ {
RuntimeUtility.InBackground(action, true); RuntimeUtility.InBackground(() =>
{
lock (_locker)
{
action.Invoke();
}
}, true);
return; return;
} }
lock (_locker)
{
try { action.Invoke(); } catch { } try { action.Invoke(); } catch { }
} }
}
internal void Colorful(object sender, string tag, Nullable<ConsoleColor> color, object[] content, Exception exception = null) internal void Output(string tag, object[] content, Exception exception = null)
{ {
if (!Enabled) return; if (!Enabled) return;
Invoke(() => Inbackground(() =>
{ {
var item = new LogItem(); var item = new LogItem();
item.Sender = sender; item.Logger = this;
item.Tag = tag; item.Tag = tag;
item.Color = color; item.Content = content;
item.Content = MergeContent(content);
item.Exception = exception; item.Exception = exception;
if (UseConsole) ToConsole(item); var text = (UseConsole || UseFile) ? item.ToString() : null;
if (UseConsole)
{
lock (ConsoleLocker)
{
System.Console.WriteLine(text);
}
}
if (UseFile) if (UseFile)
{ {
var plain = ToText(item);
lock (_cache_locker) lock (_cache_locker)
{ {
if (_cache_capacity > 0) if (_cache_capacity > 0)
{ {
_cache_array[_cache_count] = plain; _cache_array[_cache_count] = text;
_cache_count += 1; _cache_count += 1;
if (_cache_count == _cache_capacity) Flush(); if (_cache_count == _cache_capacity) Flush();
} }
else else
{ {
ToFile(plain, this); ToFile(text, this);
}
} }
} }
});
} }
/// <summary>记录异常。</summary> var actions = _actions;
internal void InnerException(object sender, Exception exception) foreach (var action in actions)
{
if (!Enabled) return;
var type = null as string;
var content = null as string;
if (exception != null)
{
try
{ {
type = exception.GetType().Name; action.Invoke(item);
content = MergeContent(new object[] { type, exception.Message });
}
catch { }
} }
if (content == null) content = "无效的 Exception 实例。"; });
Colorful(sender, "Exception", DarkMagenta, new object[] { type, content }, exception);
} }
/// <summary>创建新实例。</summary> /// <summary>创建新实例。</summary>
@ -183,94 +195,6 @@ namespace Apewer
/// <summary>获取用于保存日志文件的路径。</summary> /// <summary>获取用于保存日志文件的路径。</summary>
public static Func<Logger, string> FilePathGetter { get; set; } public static Func<Logger, string> 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) private static string ToFile(string plain, Logger logger, bool crlf = true)
{ {
@ -287,7 +211,7 @@ namespace Apewer
/// <summary>获取日志文件路径发生错误时返回 NULL 值。</summary> /// <summary>获取日志文件路径发生错误时返回 NULL 值。</summary>
/// <remarks>默认例:<br/>d:\app\log\1970-01-01.log<br/>d:\www\app_data\log\1970-01-01.log</remarks> /// <remarks>默认例:<br/>d:\app\log\1970-01-01.log<br/>d:\www\app_data\log\1970-01-01.log</remarks>
public static string GetFilePath(Logger logger = null) static string GetFilePath(Logger logger = null)
{ {
var getter = FilePathGetter; var getter = FilePathGetter;
if (getter != null) try { return getter.Invoke(logger); } catch { } if (getter != null) try { return getter.Invoke(logger); } catch { }
@ -304,7 +228,10 @@ namespace Apewer
// 文件不存在时创建新文件,无法创建时返回。 // 文件不存在时创建新文件,无法创建时返回。
var now = DateTime.Now; var now = DateTime.Now;
var date = now.Lucid(true, false, false, false); 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)) if (!StorageUtility.FileExists(filePath))
{ {
StorageUtility.WriteFile(filePath, TextUtility.Bom); StorageUtility.WriteFile(filePath, TextUtility.Bom);
@ -314,6 +241,7 @@ namespace Apewer
// 检查过期文件。 // 检查过期文件。
if (logger != null && getter == null && logger._reserved > -1) if (logger != null && getter == null && logger._reserved > -1)
{ {
// 每天执行一次。
if (date != logger._lastdate) if (date != logger._lastdate)
{ {
logger._lastdate = date; logger._lastdate = date;
@ -336,14 +264,30 @@ namespace Apewer
var paths = StorageUtility.GetSubFiles(logDir); var paths = StorageUtility.GetSubFiles(logDir);
foreach (var path in paths) foreach (var path in paths)
{ {
var fileName = Path.GetFileNameWithoutExtension(path); // 文件名后缀
var fileExt = Path.GetExtension(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[4] != '-') continue;
if (fileName[7] != '-') continue; if (fileName[7] != '-') continue;
if (fileExt != FileExt) continue; if (fileName[10] != '.' && fileName[10] != '_') continue;
var dt = ClockUtility.ParseLucid(fileName); // 确定文件名是属于当前的 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.Substring(0, 10));
if (dt == null || !dt.HasValue) continue; if (dt == null || !dt.HasValue) continue;
var days = Convert.ToInt32(Convert.ToInt64((today - dt.Value).TotalMilliseconds) / 86400000L); var days = Convert.ToInt32(Convert.ToInt64((today - dt.Value).TotalMilliseconds) / 86400000L);
@ -359,14 +303,14 @@ namespace Apewer
#region 默认实列。 #region 默认实列。
private static Logger _default = new Logger("Apewer.Logger.Default", true, false, true); private static Logger _default = new Logger(null, true, false, true);
private static Logger _console = new Logger("Apewer.Logger.Console", true, false, true); private static Logger _console = new Logger(null, true, false, true);
private static Logger _web = new Logger("Apewer.Logger.Web", true, false, true); private static Logger _web = new Logger(null, true, false, true);
#if DEBUG #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 #else
internal static Logger _internals = new Logger("Apewer.Logger.Internals", true, false, false); internal static Logger _internals = new Logger(null, true, false, false);
#endif #endif
/// <summary>内部的日志记录程序。</summary> /// <summary>内部的日志记录程序。</summary>
@ -388,16 +332,10 @@ namespace Apewer
const string FileExt = ".log"; const string FileExt = ".log";
/// <summary>使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。</summary> /// <summary>使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。</summary>
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);
/// <summary>使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。</summary> /// <summary>使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。</summary>
public static void Write<T>(params object[] content) => Default.Colorful(typeof(T), null, null, content, null); public static void Write(Exception exception, params object[] content) => Default.Output(null, content, exception);
/// <summary>使用 Logger.Default 写入日志,自动添加时间和日期。</summary>
public static void Write(Exception exception) => Default.InnerException(null, exception);
/// <summary>使用 Logger.Default 写入日志,自动添加时间和日期。</summary>
public static void Write<T>(Exception exception) => Default.InnerException(typeof(T), exception);
/// <summary>压缩日志文件的内容,另存为 ZIP 文件,并删除原日志文件。</summary> /// <summary>压缩日志文件的内容,另存为 ZIP 文件,并删除原日志文件。</summary>
public static void CollectToZip(string path) public static void CollectToZip(string path)
@ -438,6 +376,29 @@ namespace Apewer
#endregion #endregion
#region public
/// <summary>记录异常。</summary>
public void Exception<T>(T exception, params object[] content) where T : Exception => Output("Exception", content, exception);
/// <summary>记录错误。多个 Content 参数将以“ | ”分隔。</summary>
public void Error(params object[] content) => Output("Error", content, null);
/// <summary>记录警告。多个 Content 参数将以“ | ”分隔。</summary>
public void Warning(params object[] content) => Output("Warning", content, null);
/// <summary>记录警告。多个 Content 参数将以“ | ”分隔。</summary>
public void Info(params object[] content) => Output("Info", content, null);
/// <summary>记录文本。多个 Content 参数将以“ | ”分隔。</summary>
public void Text(params object[] content) => Output(null, content, null);
/// <summary>记录调试。多个 Content 参数将以“ | ”分隔。</summary>
[Conditional("DEBUG")]
public void Debug(params object[] content) => Output("Debug", content, null);
#endregion
} }
} }

61
Apewer/Models/LogItem.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Text;
namespace Apewer.Models namespace Apewer.Models
{ {
@ -8,27 +9,71 @@ namespace Apewer.Models
public sealed class LogItem public sealed class LogItem
{ {
/// <summary>自定义日志条目格式化程序。</summary>
public static Func<LogItem, string> Formatter { get; set; }
/// <summary>日志记录程序。</summary>
public Logger Logger { get; internal set; }
/// <summary>记录日志的时间。</summary> /// <summary>记录日志的时间。</summary>
public string Clock { get; private set; } public DateTime Clock { get; private set; }
/// <summary>日志的标签。</summary> /// <summary>日志的标签。</summary>
public string Tag { get; internal set; } public string Tag { get; internal set; }
internal Nullable<ConsoleColor> Color { get; set; }
/// <summary>日志的文本内容。</summary> /// <summary>日志的文本内容。</summary>
public string Content { get; internal set; } public object[] Content { get; internal set; }
/// <summary>日志中包含的异常对象。</summary> /// <summary>日志中包含的异常对象。</summary>
public Exception Exception { get; internal set; } public Exception Exception { get; internal set; }
/// <summary>生成日志的对象。</summary>
public object Sender { get; internal set; }
/// <summary></summary> /// <summary></summary>
internal LogItem() internal LogItem()
{ {
Clock = ClockUtility.Lucid(DateTime.Now); Clock = DateTime.Now;
}
/// <summary></summary>
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();
} }
} }

2
Apewer/RuntimeUtility.cs

@ -954,7 +954,7 @@ namespace Apewer
catch (Exception ex) catch (Exception ex)
{ {
var ex2 = ex.InnerException ?? ex; var ex2 = ex.InnerException ?? ex;
Logger.Internals.Exception(typeof(RuntimeUtility), ex2); Logger.Internals.Exception(ex2, typeof(RuntimeUtility));
if (!catchException) throw ex2; if (!catchException) throw ex2;
} }

2
Apewer/Web/HttpListener.cs

@ -51,7 +51,7 @@ namespace Apewer.Web
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Web.Exception(this, ex); Logger.Web.Exception(ex, this);
Logger.Web.Info(this, $"在 {prefix} 启动失败。"); Logger.Web.Info(this, $"在 {prefix} 启动失败。");
return ex; return ex;
} }

20
Apewer/_Extensions.cs

@ -358,24 +358,8 @@ public static class Extensions
#region Logger #region Logger
/// <summary>记录异常。</summary> /// <summary>记录文本。多个 Content 参数将以“ | ”分隔,此方法等同于 <see cref="Logger.Text"/>。</summary>
public static void Exception(this Logger logger, object sender, Exception exception) => logger?.InnerException(sender, exception); public static void Write(this Logger logger, object sender, params object[] content) => logger?.Text(sender, null, content, null);
/// <summary>记录错误。多个 Content 参数将以“ | ”分隔。</summary>
public static void Error(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, "Error", ConsoleColor.DarkRed, content, null);
/// <summary>记录警告。多个 Content 参数将以“ | ”分隔。</summary>
public static void Warning(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, "Warning", ConsoleColor.DarkYellow, content, null);
/// <summary>记录警告。多个 Content 参数将以“ | ”分隔。</summary>
public static void Info(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, "Info", ConsoleColor.DarkBlue, content, null);
/// <summary>记录文本。多个 Content 参数将以“ | ”分隔。</summary>
public static void Text(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, null, null, content, null);
/// <summary>记录调试。多个 Content 参数将以“ | ”分隔。</summary>
[Conditional("DEBUG")]
public static void Debug(this Logger logger, object sender, params object[] content) => logger?.Colorful(sender, null, null, content, null);
#endregion #endregion

Loading…
Cancel
Save