using Apewer.Internals; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; namespace Apewer { /// 日志记录程序。 public class Logger { private string _key = Guid.NewGuid().ToString("n"); private string _target = ""; private bool _enabled = true; private Func _preoutput = null; /// 已启用。 public virtual bool Enabled { get; set; } /// 使用控制台输出。默认值:TRUE。 public virtual bool UseConsole { get; set; } = true; /// 使用日志文件。默认值:FALSE。 public virtual bool UseFile { get; set; } = false; /// 输出前的检查,返回值将确认输出。 public virtual Func PreOutput { get; set; } /// 异常。设置处理方法以替代 UseConsole 和 UseFile。 public virtual Event OnException { get; set; } /// 错误。设置处理方法以替代 UseConsole 和 UseFile。 public virtual Event OnError { get; set; } /// 注意。设置处理方法以替代 UseConsole 和 UseFile。 public virtual Event OnWarning { get; set; } /// 文本。设置处理方法以替代 UseConsole 和 UseFile。 public virtual Event OnText { get; set; } /// 调试。设置处理方法以替代 UseConsole 和 UseFile。 public virtual Event OnDebug { get; set; } private void Call(string text, Event defined, Action action) { var pre = PreOutput; if (pre != null) { var @continue = PreOutput(text); if (!@continue) return; } if (defined == null) { if (UseConsole) ToConsole(text); if (UseFile) ToFile(text); } else { try { action(text); } catch { } } } /// 记录异常。 public virtual void Exception(object sender, Exception exception) { 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)); } /// 记录错误。多个 Content 参数将以“ | ”分隔。 public virtual void Error(object sender, params object[] content) { Call(MergeText(true, "ERROR", content), OnError, (t) => OnError(sender, t)); } /// 记录警告。多个 Content 参数将以“ | ”分隔。 public virtual void Warning(object sender, params object[] content) { Call(MergeText(true, "WARNING", content), OnWarning, (t) => OnWarning(sender, t)); } /// 记录文本。多个 Content 参数将以“ | ”分隔。 public virtual void Text(object sender, params object[] content) { Call(MergeText(true, "TEXT", content), OnText, (t) => OnText(sender, t)); } /// 记录调试。多个 Content 参数将以“ | ”分隔。 public virtual void Debug(object sender, params object[] content) { Call(MergeText(true, "DEBUG", content), OnDebug, (t) => OnDebug(sender, t)); } #region 默认实列。 private static Logger _default = new Logger(); /// 默认的日志记录程序,将信息写入控制台。 public static Logger Default { get => _default; } #endregion #region 输出。 private static object FileLocker = new object(); private static object ConsoleLocker = new object(); private static string MergeText(bool withClock, string tag, params object[] content) { 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(); } /// 向控制台输出文本。 public static void ToConsole(string text) { lock (ConsoleLocker) { if (string.IsNullOrEmpty(text)) Console.WriteLine(); else Console.WriteLine(text); } } /// 向日志文件输出文本,文件名按日期自动生成。输出失败时返回错误信息。 public static string ToFile(string text) => ToFile(text, false); /// 向日志文件输出文本,文件名按日期自动生成。输出失败时返回错误信息。 public static string ToFile(string text, bool withConsole) { 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; } /// 获取日志文件路径发生错误时返回 NULL 值。 /// d:\app\log\2020-02-02.log /// d:\www\app_data\log\2020-02-02.log 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; // 文件不存在时创建新文件,无法创建时返回。 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; } /// 使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。 public static void Write(params object[] content) { var console = Default.UseConsole; var file = Default.UseFile; if (console || file) { var text = MergeText(true, null, content); if (console) ToConsole(text); if (file) ToFile(text); } } #endregion } }