简体   繁体   English

如何使用 NLog 和结构化日志有条件地呈现属性?

[英]How to conditionally render an attribute with NLog and structured logging?

I have inherited an ASP.NET application which is using NLog for logging.我继承了使用 NLog 进行日志记录的 ASP.NET 应用程序。 One of the logged attributes is named module which is hardcoded to "Core".记录的属性之一是命名模块,它被硬编码为“核心”。 This is not useful at all, because all parts of the application (eg health check, various business contexts) push the same value.这根本没有用,因为应用程序的所有部分(例如健康检查、各种业务上下文)都推送相同的值。

I am trying to use structured logging to override this value in some cases (I cannot afford to refactor the entire logging now).在某些情况下,我尝试使用结构化日志记录来覆盖此值(我现在无法重构整个日志记录)。

Extension method扩展方法

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);
}

Usage用法

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

NLog layout (most of the attributes removed for brevity) 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>

I get want want, that is all existing calls will not provide the module and they will work as before.我得到想要的,那就是所有现有的调用都不会提供模块,它们会像以前一样工作。 Any logging passing through LogInfoWithModule will override the default value.通过LogInfoWithModule传递的任何日志记录都将覆盖默认值。

However, I find this solution very messy because:但是,我发现这个解决方案非常混乱,因为:

  • the actual message is containing the module name实际消息包含模块名称
  • structured logging cannot be used无法使用结构化日志记录
  • cannot delay string formatting (by providing params to Log function)不能延迟字符串格式化(通过向 Log 函数提供参数)

I have tried the solution suggested here (and here ), but ${mdlc:item=module} does not contain anything.我已经尝试过这里(和这里)建议的解决方案,但${mdlc:item=module}不包含任何内容。

The easy way, but will have a performance hit (Notice ${mdlc:item=module} is case-sensitive)简单的方法,但会影响性能(注意${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);
        }
    }

The advanced way for injecting properties (Notice ${event-properties:module} is case-sensitive): 注入属性的高级方法(注意${event-properties:module}区分大小写):

    public static void LogInfoWithModule<T>(this ILogger<T> logger, string message, 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,object>("module", Module);
    
        public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => yield return new KeyValuePair<string,object>("module", Module);
    
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

Beware typos and compile-errors might be included in the above example code.请注意上述示例代码中可能包含拼写错误和编译错误。

Alternative you might find happiness here: https://github.com/viktor-nikolaev/XeonApps.Extensions.Logging.WithProperty :或者你可能会在这里找到快乐: 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, object propertyValue) =>
        logger.WithProperty("module", propertyValue);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM