【问题标题】:Including logging as part of my domain model将日志记录作为我的域模型的一部分
【发布时间】:2019-08-04 22:41:33
【问题描述】:

我正在编写一个应用程序,其中日志记录是我实际域模型的一部分。这是一个自动化和批处理工具,最终用户将能够在实际应用程序中查看批处理作业的日志,而不仅仅是文本日志文件。

所以我的域模型包含一个LogMessage 类:

public sealed class LogMessage
{
    public string Message { get; }
    public DateTime TimestampUtc { get; }
    public LogLevel Level { get; }
}

public enum LogLevel
{
    Fatal = 5,
    Error = 4,
    Warn = 3,
    Info = 2,
    Debug = 1,
    Trace = 0
}

我还有一个Result 类,它的集合属性为LogMessages。最终用户可以使用我的应用程序将结果保存到文件中并从文件中打开。

public class Result
{
    public bool Succeeded {get; set;}
    public string StatusMessage {get; set;}
    public IList<LogMessage> LogMessages {get; set;}
}

我的应用程序还支持第三方开发人员使用插件扩展应用程序,这些插件也可以写入日志消息。所以我为插件开发者定义了一个通用的ILogger接口。

public interface ILogger
{
    void Debug(string message);
    void Error(string message);
    void Fatal(string message);
    void Info(string message);
    void Log(LogLevel level, string message);
    void Trace(string message);
    void Warn(string message);
}

我向写入Result.LogMessages 的插件提供ILogger 的实例。

public interface IPlugIn
{
    Output DoSomeThing(Input in, ILogger logger);
}

我显然也希望能够从我自己的内部代码进行日志记录,并最终希望Result.LogMessages 包含我的内部日志消息和来自插件的日志消息的混合。因此,遇到问题的最终用户可以向我发送一个结果文件,其中包含来自我的内部代码和使用的任何插件的调试日志。

目前,我有一个使用自定义 NLog 目标的解决方案。

public class LogResultTarget : NLog.Targets.Target
{
    public static Result CurrentTargetResult { get; set; }

    protected override void Write(NLog.LogEventInfo logEvent)
    {
        if (CurrentTargetResult != null)
        {
            //Convert NLog logEvent to LogMessage
            LogLevel level = (LogLevel)Enum.Parse(typeof(LogLevel), logEvent.Level.Name);
            LogMessage lm = new LogMessage(logEvent.TimeStamp.ToUniversalTime(), level, logEvent.Message);
            CurrentTargetResult.LogMessages.Add(lm);
        }
    }

    protected override void Write(NLog.Common.AsyncLogEventInfo logEvent)
    {
        Write(logEvent.LogEvent);
    }
}

此类将消息转发到分配给静态LogResultTarget.CurrentTargetResult 属性的Result。我的内部代码记录到 NLog 记录器,并且我有一个 ILogger 的实现,它也记录到 NLog.Logger

这是有效的,但感觉真的很脆弱。如果CurrentTargetResult 设置不正确或未设置回null,我最终可能会将日志消息存储到它们不适用的结果中。另外,因为只有一个静态CurrentTargetResult,所以我无法支持同时处理多个结果。

有没有不同/更好的方法可以解决这个问题?还是我试图做的事情根本上是错误的?

【问题讨论】:

  • 我不完全理解CurrentTargetResult 属性,您当前的目标中没有使用它? (只有空检查?)Result 类型到底长什么样?
  • 啊,我的示例代码中好像有错字,我有“Target”而不是“CurrentTargetResult”。我还添加了“结果”类的示例。这有帮助吗?
  • 是的,添加了答案:)

标签: c# logging nlog error-logging


【解决方案1】:

认为static CurrentTargetResult 确实有点脆弱,但总体方法很好。

我提出以下更改:

  1. 取消静态CurrentTargetResult,并始终使其初始化,

    类似这样的:

    public class LogResultTarget : NLog.Targets.Target
    {
        public Result CurrentTargetResult { get; } = new Result();
    
        protected override void Write(NLog.LogEventInfo logEvent)
        {
            //Convert NLog logEvent to LogMessage
            LogLevel level = (LogLevel)Enum.Parse(typeof(LogLevel), logEvent.Level.Name);
            LogMessage lm = new LogMessage(logEvent.TimeStamp.ToUniversalTime(), level, logEvent.Message);
            CurrentTargetResult.LogMessages.Add(lm);
        }
    
        protected override void Write(NLog.Common.AsyncLogEventInfo logEvent)
        {
            Write(logEvent.LogEvent);
        }
    }
    
  2. 始终初始化 LogMessage:

    public class Result
    {
        public bool Succeeded {get; set;}
        public string StatusMessage {get; set;}
        public IList<LogMessage> LogMessages {get; set;} = new List<LogMessage>();
    }
    
  3. 在需要时使用以下调用检索消息:

    // find target by name
    var logResultTarget1 = LogManager.Configuration.FindTargetByName<LogResultTarget>("target1");
    var results = logResultTarget1.CurrentTargetResult;
    
    // or multiple targets
    var logResultTargets = LogManager.Configuration.AllTargets.OfType<LogResultTarget>();
    var allResults = logResultTargets.Select(t => t.CurrentTargetResult);
    

PS:你也可以在LogResultTarget中覆盖InitializeTarget来初始化目标

【讨论】:

    【解决方案2】:

    我认为您的方法是正确的,但是您可以通过使用已经为您完成此抽象的库来节省精力。 Common Logging 库就是您所追求的。

    您的域代码将仅依赖于 Common Logging 中的 ILogger 接口。仅当您的域被运行时使用时,例如Web API,然后您是否配置要使用的日志记录提供程序。

    有许多预构建的提供程序可用作单独的 nuget 包:

    Common.Logging 提供了支持 .NET 中所有以下流行日志记录目标/框架的适配器:

    • Lo​​g4Net (v1.2.9 - v1.2.15)
    • NLog (v1.0 - v4.4.1)
    • SeriLog (v1.5.14)
    • Microsoft 企业库日志记录应用程序块 (v3.1 - v6.0)
    • Microsoft AppInsights (2.4.0)
    • Microsoft Windows 事件跟踪 (ETW)
    • 登录到 STDOUT
    • 登录到调试输出

    我已经使用了很多年了,让您的域/库代码在另一个上下文中被重用,但不必对日志框架有固定的依赖关系(我已经从企业库迁移),这真是太好了到 log4net,到最后到 NLog ...轻而易举)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-16
      • 2023-02-06
      • 2019-11-30
      • 1970-01-01
      • 2017-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多