如何使用 NLog 和结构化日志有条件地呈现属性? 扩展方法用法NLog 布局为简洁起见删除了大部分属性

问题描述

我继承了一个使用 NLog 进行日志记录的 ASP.NET 应用程序。记录的属性之一是硬编码为“核心”的命名模块。这根本没有用,因为应用程序的所有部分(例如健康检查、各种业务环境)都推送相同的值。

在某些情况下,我尝试使用结构化日志来覆盖此值(我现在无法重构整个日志记录)。

扩展方法

public static void LogInfoWithModule<T>(this ILogger<T> logger,string message,string module)
{
    var escapedMessage = message.Replace("{","{{");
    escapedMessage = escapedMessage.Replace("}","}}");
    logger.Log(LogLevel.information,"[{module}] " + escapedMessage,module);
}

用法

logger.LogInfoWithModule($"Health check response = {response}",Constants.LoggingModules.Health);

NLog 布局(为简洁起见删除了大部分属性

<target name="logJson" xsi:type="File">
  <layout xsi:type="JsonLayout" includeAllProperties="true">
    <attribute name="module" layout="${when:when='${event-properties:item=module}'=='':Core" />
  </layout>
</target>

我想要,那就是所有现有的调用都不会提供模块,它们会像以前一样工作。任何通过 LogInfoWithModule 的日志记录都将覆盖认值。

但是,我觉得这个解决方案非常混乱,因为:

  • 实际消息包含模块名称
  • 不能使用结构化日志
  • 不能延迟字符串格式化(通过向 Log 函数提供参数)

我尝试了建议的解决方here(和 here),但 ${mdlc:item=module} 不包含任何内容

解决方法

简单的方法,但会影响性能(注意 ${mdlc:item=module} 区分大小写)

    public static void LogInfoWithModule<T>(this ILogger<T> logger,string message,string module)
    {
        using (logger.BeginScope(new [] { new KeyValuePair<string,object>("module",module) }))
        {
           logger.Log(LogLevel.Information,message);
        }
    }

advanced way for injecting properties(注意 ${event-properties:module} 区分大小写):

    public static void LogInfoWithModule<T>(this ILogger<T> logger,string module)
    {
        logger.Log(LogLevel.Information,default(EventId),new ModuleLogEvent(message,module),default(Exception),ModuleLogEvent.Formatter);
    }

    class ModuleLogEvent : IReadOnlyList<KeyValuePair<string,object>>
    {
        public static Func<ModuleLogEvent,Exception,string> Formatter { get; } = (l,ex) => l.Message;
    
        public string Message { get; }
    
        public string Module { get; }
                   
        public MyLogEvent(string message,string module)
        {
            Message = message;
            Module = module;
        }
    
        public override string ToString() => Message;
    
        // IReadOnlyList-interface
        public int Count => 1;
    
        public KeyValuePair<string,object> this[int index] => new KeyValuePair<string,Module);
    
        public IEnumerator<KeyValuePair<string,object>> GetEnumerator() => yield return new KeyValuePair<string,Module);
    
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

请注意上面的示例代码中可能包含拼写错误和编译错误。

您可能会在这里找到幸福的另一种选择:https://github.com/viktor-nikolaev/XeonApps.Extensions.Logging.WithProperty

public static class LoggingExtensions
{
    public static NLog.Logger WithModule(this NLog.Logger logger,object propertyValue) =>
        logger.WithProperty("module",propertyValue);

    public static ILogger WithModule(this ILogger logger,propertyValue);
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...