Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将日志记录作为我的域模型的一部分_C#_Logging_Nlog_Error Logging - Fatal编程技术网

C# 将日志记录作为我的域模型的一部分

C# 将日志记录作为我的域模型的一部分,c#,logging,nlog,error-logging,C#,Logging,Nlog,Error Logging,我正在编写一个应用程序,其中日志记录是我实际域模型的一部分。它是一个自动化和批处理工具,最终用户将能够查看实际应用程序中批处理作业的日志,而不仅仅是文本日志文件 因此,我的域模型包括一个LogMessage类: public sealed class LogMessage { public string Message { get; } public DateTime TimestampUtc { get; } public LogLevel Level { get; }

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

因此,我的域模型包括一个
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;}
}
我向写入
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
属性的
结果。我的内部代码记录到NLog记录器,我有一个
ILogger
的实现,它也记录到一个
NLog.Logger

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


我有没有其他/更好的方法来解决这个问题?或者我试图做的根本错误吗?

我认为你的方法是正确的,但是你可以通过使用一个已经为你做了这个抽象的库来节省精力。图书馆就是你要找的

您的域代码将仅依赖于公共日志中的
ILogger
接口。只有当您的域被运行时(例如Web API)使用时,您才能配置要使用的日志记录提供程序

有许多预构建的提供程序作为单独的nuget软件包提供:

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

  • Log4Net(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)
  • 登录到标准输出
  • 登录以进行调试

我已经使用它很多年了,能够在另一个上下文中重用域/库代码非常好,但不必对日志框架有固定的依赖性(我已经从企业库转移到log4net,最后是NLog…这是一件轻而易举的事).

在我看来,
静态
CurrentTargetResult确实有点脆弱,但总体方法还不错

我建议作出以下修改:

  • 取消对CurrentTargetResult的静态设置,并始终将其初始化

    大概是这样的:

    公共类LogResultTarget:NLog.Targets.Target
    {
    公共结果CurrentTargetResult{get;}=新结果();
    受保护的重写无效写入(NLog.LogEventInfo logEvent)
    {
    //将NLog logEvent转换为LogMessage
    LogLevel=(LogLevel)Enum.Parse(typeof(LogLevel),logEvent.level.Name);
    LogMessage lm=新的LogMessage(logEvent.TimeStamp.ToUniversalTime(),级别,logEvent.Message);
    CurrentTargetResult.LogMessages.Add(lm);
    }
    受保护的重写无效写入(NLog.Common.AsyncLogEventInfo logEvent)
    {
    写入(logEvent.logEvent);
    }
    }
    
  • 始终初始化日志消息:

    公共类结果
    {
    公共布尔成功{get;set;}
    公共字符串状态消息{get;set;}
    公共IList日志消息{get;set;}=new List();
    }
    
  • 在需要时通过以下调用检索消息:

    //按名称查找目标
    var logresultarget1=LogManager.Configuration.FindTargetByName(“target1”);
    var结果=logResultTarget1.CurrentTargetResult;
    //或多个目标
    var logresultargets=LogManager.Configuration.AllTargets.OfType();
    var allResults=logresultargets.Select(t=>t.CurrentTargetResult);
    

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

    我不完全理解
    CurrentTargetResult
    属性,它没有在您当前的目标中使用?(只有空检查?)结果的确切外观是什么样的?
    Result
    类型的外观是什么样的?啊,看起来我的示例代码中有一个输入错误,我用了“Target”而不是“CurrentTargetResult”。我还添加了一个“Result”类的示例。这有帮助吗?是的,添加了一个答案:)
    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);
        }
    }