|
|
@ -1,6 +1,7 @@ |
|
|
|
using Apewer.Internals; |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.IO; |
|
|
|
using System.Text; |
|
|
|
using System.Threading; |
|
|
|
|
|
|
@ -14,221 +15,207 @@ namespace Apewer |
|
|
|
private string _key = Guid.NewGuid().ToString("n"); |
|
|
|
private string _target = ""; |
|
|
|
private bool _enabled = true; |
|
|
|
private Func<string, bool> _preoutput = null; |
|
|
|
|
|
|
|
/// <summary>异常。</summary>
|
|
|
|
public event Event<Exception> ExceptionEvent; |
|
|
|
|
|
|
|
/// <summary>调试。</summary>
|
|
|
|
public event Event<string> DebugEvent; |
|
|
|
/// <summary>已启用。</summary>
|
|
|
|
public virtual bool Enabled { get; set; } |
|
|
|
|
|
|
|
/// <summary>文本。</summary>
|
|
|
|
public event Event<string> TextEvent; |
|
|
|
/// <summary>使用控制台输出。默认值:TRUE。</summary>
|
|
|
|
public virtual bool UseConsole { get; set; } = true; |
|
|
|
|
|
|
|
/// <summary>信息。</summary>
|
|
|
|
public event Event<string> InfomationEvent; |
|
|
|
/// <summary>使用日志文件。默认值:FALSE。</summary>
|
|
|
|
public virtual bool UseFile { get; set; } = false; |
|
|
|
|
|
|
|
/// <summary>注意。</summary>
|
|
|
|
public event Event<string> WarningEvent; |
|
|
|
/// <summary>输出前的检查,返回值将确认输出。</summary>
|
|
|
|
public virtual Func<string, bool> PreOutput { get; set; } |
|
|
|
|
|
|
|
/// <summary>错误。</summary>
|
|
|
|
public event Event<string> ErrorEvent; |
|
|
|
/// <summary>异常。设置处理方法以替代 UseConsole 和 UseFile。</summary>
|
|
|
|
public virtual Event<Exception> OnException { get; set; } |
|
|
|
|
|
|
|
/// <summary>自定义。</summary>
|
|
|
|
public event Event<object> CustomEvent; |
|
|
|
/// <summary>错误。设置处理方法以替代 UseConsole 和 UseFile。</summary>
|
|
|
|
public virtual Event<string> OnError { get; set; } |
|
|
|
|
|
|
|
/// <summary>已启用。</summary>
|
|
|
|
public bool Enabled { get { return _enabled; } set { _enabled = value; } } |
|
|
|
/// <summary>注意。设置处理方法以替代 UseConsole 和 UseFile。</summary>
|
|
|
|
public virtual Event<string> OnWarning { get; set; } |
|
|
|
|
|
|
|
/// <summary>唯一标识。</summary>
|
|
|
|
public string Key { get { return _key; } } |
|
|
|
/// <summary>文本。设置处理方法以替代 UseConsole 和 UseFile。</summary>
|
|
|
|
public virtual Event<string> OnText { get; set; } |
|
|
|
|
|
|
|
/// <summary>目标。</summary>
|
|
|
|
public string Target { get { return _target; } protected set { _target = value ?? ""; } } |
|
|
|
/// <summary>调试。设置处理方法以替代 UseConsole 和 UseFile。</summary>
|
|
|
|
public virtual Event<string> OnDebug { get; set; } |
|
|
|
|
|
|
|
internal void Invoke(LogItem item) |
|
|
|
private void Call<T>(string text, Event<T> defined, Action<string> action) |
|
|
|
{ |
|
|
|
if (item == null) return; |
|
|
|
switch (item.Type) |
|
|
|
var pre = PreOutput; |
|
|
|
if (pre != null) |
|
|
|
{ |
|
|
|
case LogType.Debug: |
|
|
|
if (DebugEvent != null) DebugEvent(this, item.Content); |
|
|
|
break; |
|
|
|
case LogType.Text: |
|
|
|
if (TextEvent != null) TextEvent(this, item.Content); |
|
|
|
break; |
|
|
|
case LogType.Infomation: |
|
|
|
if (InfomationEvent != null) InfomationEvent(this, item.Content); |
|
|
|
break; |
|
|
|
case LogType.Warning: |
|
|
|
if (WarningEvent != null) WarningEvent(this, item.Content); |
|
|
|
break; |
|
|
|
case LogType.Error: |
|
|
|
if (ErrorEvent != null) ErrorEvent(this, item.Content); |
|
|
|
break; |
|
|
|
case LogType.Exception: |
|
|
|
if (ExceptionEvent != null) ExceptionEvent(this, item.Exception); |
|
|
|
break; |
|
|
|
case LogType.Custom: |
|
|
|
if (CustomEvent != null) CustomEvent(this, item.Custom); |
|
|
|
break; |
|
|
|
var @continue = PreOutput(text); |
|
|
|
if (!@continue) return; |
|
|
|
} |
|
|
|
if (defined == null) |
|
|
|
{ |
|
|
|
if (UseConsole) ToConsole(text); |
|
|
|
if (UseFile) ToFile(text); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
action(text); |
|
|
|
} |
|
|
|
catch { } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>异常。</summary>
|
|
|
|
public void Exception(Exception value) |
|
|
|
/// <summary>记录异常。</summary>
|
|
|
|
public virtual void Exception(object sender, Exception exception) |
|
|
|
{ |
|
|
|
if (value == null) return; |
|
|
|
var item = new LogItem(LogType.Exception); |
|
|
|
item.Logger = this; |
|
|
|
item.Target = _target; |
|
|
|
try { item.Content = value.ToString(); } catch { } |
|
|
|
string text; |
|
|
|
if (exception == null) |
|
|
|
{ |
|
|
|
text = "无效异常。"; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
try { text = TextUtility.Join(" | ", exception.GetType().FullName, exception.Message); } |
|
|
|
catch { text = "获取异常时再次引发了异常。"; } |
|
|
|
} |
|
|
|
Call(MergeText(true, "EXCEPTION", text), OnException, (t) => OnException(sender, exception)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>自定义。</summary>
|
|
|
|
public void Custom(object value) |
|
|
|
/// <summary>记录错误。多个 Content 参数将以“ | ”分隔。</summary>
|
|
|
|
public virtual void Error(object sender, params object[] content) |
|
|
|
{ |
|
|
|
if (value == null) return; |
|
|
|
var item = new LogItem(LogType.Custom); |
|
|
|
item.Logger = this; |
|
|
|
item.Target = _target; |
|
|
|
item.Custom = value; |
|
|
|
Call(MergeText(true, "ERROR", content), OnError, (t) => OnError(sender, t)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>调试。</summary>
|
|
|
|
public void Debug(string value) |
|
|
|
/// <summary>记录警告。多个 Content 参数将以“ | ”分隔。</summary>
|
|
|
|
public virtual void Warning(object sender, params object[] content) |
|
|
|
{ |
|
|
|
if (value == null) return; |
|
|
|
var item = new LogItem(LogType.Debug); |
|
|
|
item.Logger = this; |
|
|
|
item.Target = _target; |
|
|
|
item.Content = value; |
|
|
|
Call(MergeText(true, "WARNING", content), OnWarning, (t) => OnWarning(sender, t)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>文本。</summary>
|
|
|
|
public void Text(string value) |
|
|
|
/// <summary>记录文本。多个 Content 参数将以“ | ”分隔。</summary>
|
|
|
|
public virtual void Text(object sender, params object[] content) |
|
|
|
{ |
|
|
|
if (value == null) return; |
|
|
|
var item = new LogItem(LogType.Text); |
|
|
|
item.Logger = this; |
|
|
|
item.Target = _target; |
|
|
|
item.Content = value; |
|
|
|
Call(MergeText(true, "TEXT", content), OnText, (t) => OnText(sender, t)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>信息。</summary>
|
|
|
|
public void Infomation(string value) |
|
|
|
/// <summary>记录调试。多个 Content 参数将以“ | ”分隔。</summary>
|
|
|
|
public virtual void Debug(object sender, params object[] content) |
|
|
|
{ |
|
|
|
if (value == null) return; |
|
|
|
var item = new LogItem(LogType.Infomation); |
|
|
|
item.Logger = this; |
|
|
|
item.Target = _target; |
|
|
|
item.Content = value; |
|
|
|
Call(MergeText(true, "DEBUG", content), OnDebug, (t) => OnDebug(sender, t)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>注意。</summary>
|
|
|
|
public void Warning(string value) |
|
|
|
{ |
|
|
|
if (value == null) return; |
|
|
|
var item = new LogItem(LogType.Warning); |
|
|
|
item.Logger = this; |
|
|
|
item.Target = _target; |
|
|
|
item.Content = value; |
|
|
|
} |
|
|
|
#region 默认实列。
|
|
|
|
|
|
|
|
/// <summary>错误。</summary>
|
|
|
|
public void Error(string value) |
|
|
|
{ |
|
|
|
if (value == null) return; |
|
|
|
var item = new LogItem(LogType.Error); |
|
|
|
item.Logger = this; |
|
|
|
item.Target = _target; |
|
|
|
item.Content = value; |
|
|
|
} |
|
|
|
private static Logger _default = new Logger(); |
|
|
|
|
|
|
|
private Logger(string target) |
|
|
|
{ |
|
|
|
_target = target ?? ""; |
|
|
|
} |
|
|
|
/// <summary>默认的日志记录程序,将信息写入控制台。</summary>
|
|
|
|
public static Logger Default { get => _default; } |
|
|
|
|
|
|
|
private static Event<Exception> ExceptionDefaultCallback = null; |
|
|
|
private static Event<object> CustomDefaultCallback = null; |
|
|
|
private static Event<string> DebugDefaultCallback = null; |
|
|
|
private static Event<string> TextDefaultCallback = null; |
|
|
|
private static Event<string> InfomationDefaultCallback = null; |
|
|
|
private static Event<string> WarningDefaultCallback = null; |
|
|
|
private static Event<string> ErrorDefaultCallback = null; |
|
|
|
#endregion
|
|
|
|
|
|
|
|
private static void Logger_ExceptionEvent(object sender, Exception value) |
|
|
|
{ |
|
|
|
if (ExceptionDefaultCallback != null) ExceptionDefaultCallback(sender, value); |
|
|
|
} |
|
|
|
#region 输出。
|
|
|
|
|
|
|
|
private static void Logger_CustomEvent(object sender, object value) |
|
|
|
{ |
|
|
|
if (ExceptionDefaultCallback != null) CustomDefaultCallback(sender, value); |
|
|
|
} |
|
|
|
private static object FileLocker = new object(); |
|
|
|
private static object ConsoleLocker = new object(); |
|
|
|
|
|
|
|
private static void Logger_DebugEvent(object sender, string value) |
|
|
|
private static string MergeText(bool withClock, string tag, params object[] content) |
|
|
|
{ |
|
|
|
if (ExceptionDefaultCallback != null) DebugDefaultCallback(sender, value); |
|
|
|
var sb = new StringBuilder(); |
|
|
|
if (withClock) |
|
|
|
{ |
|
|
|
sb.Append(DateTimeUtility.NowLucid); |
|
|
|
sb.Append(" "); |
|
|
|
} |
|
|
|
if (!string.IsNullOrEmpty(tag)) |
|
|
|
{ |
|
|
|
sb.Append(DateTimeUtility.NowLucid); |
|
|
|
sb.Append(tag); |
|
|
|
sb.Append(" "); |
|
|
|
} |
|
|
|
sb.Append(TextUtility.Join(" | ", content)); |
|
|
|
sb.Append("\r\n"); |
|
|
|
return sb.ToString(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void Logger_TextEvent(object sender, string value) |
|
|
|
/// <summary>向控制台输出文本。</summary>
|
|
|
|
public static void ToConsole(string text) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
lock (ConsoleLocker) |
|
|
|
{ |
|
|
|
if (string.IsNullOrEmpty(text)) Console.WriteLine(); |
|
|
|
else Console.WriteLine(text); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static void Logger_InfomationEvent(object sender, string value) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
/// <summary>向日志文件输出文本,文件名按日期自动生成。输出失败时返回错误信息。</summary>
|
|
|
|
public static string ToFile(string text) => ToFile(text, false); |
|
|
|
|
|
|
|
private static void Logger_WarningEvent(object sender, string value) |
|
|
|
/// <summary>向日志文件输出文本,文件名按日期自动生成。输出失败时返回错误信息。</summary>
|
|
|
|
public static string ToFile(string text, bool withConsole) |
|
|
|
{ |
|
|
|
throw new NotImplementedException(); |
|
|
|
if (withConsole) ToConsole(text); |
|
|
|
lock (FileLocker) |
|
|
|
{ |
|
|
|
var path = GetFileDir(); |
|
|
|
if (string.IsNullOrEmpty(path)) |
|
|
|
{ |
|
|
|
var msg = "写入日志文件失败:无法获取日志文件路径。"; |
|
|
|
if (withConsole) ToConsole(msg); |
|
|
|
return msg; |
|
|
|
} |
|
|
|
|
|
|
|
var bytes = TextUtility.ToBinary(TextUtility.Merge(text, "\r\n")); |
|
|
|
if (!StorageUtility.AppendFile(path, bytes)) |
|
|
|
{ |
|
|
|
var msg = "写入日志文件失败。"; |
|
|
|
if (withConsole) ToConsole(msg); |
|
|
|
return msg; |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
private static void Logger_ErrorEvent(object sender, string value) |
|
|
|
/// <summary>获取日志文件路径发生错误时返回 NULL 值。</summary>
|
|
|
|
/// <remarks>d:\app\log\2020-02-02.log</remarks>
|
|
|
|
/// <remarks>d:\www\app_data\log\2020-02-02.log</remarks>
|
|
|
|
public static string GetFileDir() |
|
|
|
{ |
|
|
|
// 找到 App_Data 目录。
|
|
|
|
var appDir = KernelUtility.ApplicationPath; |
|
|
|
var dataDir = Path.Combine(appDir, "app_data"); |
|
|
|
if (StorageUtility.DirectoryExists(dataDir)) appDir = dataDir; |
|
|
|
|
|
|
|
} |
|
|
|
// 检查 Log 目录,不存在时创建,创建失败时返回。
|
|
|
|
var logDir = Path.Combine(appDir, "log"); |
|
|
|
if (!StorageUtility.AssureDirectory(logDir)) return null; |
|
|
|
|
|
|
|
private Logger(Type target) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
if (target != null) |
|
|
|
{ |
|
|
|
_target = target.FullName; |
|
|
|
} |
|
|
|
} |
|
|
|
catch { } |
|
|
|
// 文件不存在时创建新文件,无法创建时返回。
|
|
|
|
var date = DateTime.Now.ToLucid(true, false, false, false); |
|
|
|
var filePath = Path.Combine(logDir, date + ".log"); |
|
|
|
StorageUtility.CreateFile(filePath, 0, false); |
|
|
|
if (!StorageUtility.FileExists(filePath)) return null; |
|
|
|
|
|
|
|
// 返回 log 文件路径。
|
|
|
|
return filePath; |
|
|
|
} |
|
|
|
|
|
|
|
private Logger(object target) |
|
|
|
/// <summary>使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。</summary>
|
|
|
|
public static void Write(params object[] content) |
|
|
|
{ |
|
|
|
try |
|
|
|
var console = Default.UseConsole; |
|
|
|
var file = Default.UseFile; |
|
|
|
if (console || file) |
|
|
|
{ |
|
|
|
if (target != null) |
|
|
|
{ |
|
|
|
_target = target.GetType().FullName; |
|
|
|
} |
|
|
|
var text = MergeText(true, null, content); |
|
|
|
if (console) ToConsole(text); |
|
|
|
if (file) ToFile(text); |
|
|
|
} |
|
|
|
catch { } |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>默认的日志记录程序。</summary>
|
|
|
|
public static Logger Default(object target) |
|
|
|
{ |
|
|
|
var logger = new Logger(target); |
|
|
|
logger.CustomEvent += Logger_CustomEvent; |
|
|
|
logger.ExceptionEvent += Logger_ExceptionEvent; |
|
|
|
logger.DebugEvent += Logger_DebugEvent; |
|
|
|
logger.TextEvent += Logger_TextEvent; |
|
|
|
logger.InfomationEvent += Logger_InfomationEvent; |
|
|
|
logger.WarningEvent += Logger_WarningEvent; |
|
|
|
logger.ErrorEvent += Logger_ErrorEvent; |
|
|
|
return logger; |
|
|
|
} |
|
|
|
#endregion
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|