为NLog自定义LayoutRenderer

2019-12-07 来源: nodotnet 发布在  https://www.cnblogs.com/JulianHuang/p/12000310.html

长话短说

  前文《解剖HttpClientFactory,自由扩展HttpMessageHandler》主要想讲如何扩展HttpMessageHandler,  示例为在每个Http请求中的日志中显示TraceId,

写入日志的代码拷贝自 Extension.Logging 源码, 这个日志源码其实无法 完成我课后的小作业: 将TraceId显示到Nlog的LayoutRenderer上。

本次重新实现一个流畅简单的  LoggingHttpMessageHandler, 并添加到 nlog layoutrenderer。

什么是Layout Renderer?

nlog 日志上显示的特定字段,便于检索和分类。

头脑风暴

先给出自定义Renderer,定义名为eqid的自定义Renderer

# 截取自 nlog.config配置文件
<variable name="format1" value="${date:format=yy/MM/dd HH\:mm\:ss} [${level}].[${logger}].[${threadid}}].[${aspnet-request-url:IncludeScheme=false:IncludeHost=false}].[${eqid}]${newline}${message} ${exception:format=tostring}" />

<target name="bce-request"
           xsi:type="File"
           layout="${format1}"
           fileName="${logDir}/bce-request.log"
           encoding="utf-8"/>

-------------------------------------1----------------------------

Nlog 添加自定义LayOutRenderer, https://github.com/NLog/NLog/wiki/How-to-write-a-custom-layout-renderer

  有简单的lambda方式,这里我们采用稍微灵活的自定义类方式:

 [LayoutRenderer("eqid")]
    public class EqidLayoutRenderer : LayoutRenderer
    {
        protected override void Append(StringBuilder builder, LogEventInfo logEvent)
        {
            builder.Append(logEvent.Properties["EventId_Name"].ToString());
        }
    }

关键点是实现 LayoutRenderer 的抽象方法 Append,  关键参数 logevent是写入Logging时候的附带参数

以上我打算从 logEventInfo的某个property中存储 该Renderer值,所以上如上取值方式。

---------------------------------2---------------------

配合着,我们在写入日志时, 也在该property 中写入该Renderer的值:

public class AttachTraceIdScopeHttpMessageHandler : DelegatingHandler
    {
        private readonly ILogger _logger;

        public AttachTraceIdScopeHttpMessageHandler(ILogger logger)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var stopwatch = Stopwatch.StartNew();
            var eventName = request.RequestUri.LocalPath.Split('/').LastOrDefault();

            _logger.Log(LogLevel.Information, , eventName),
                $"Start processing HTTP request {request.RequestUri} {request.Method}");
            var response = await base.SendAsync(request, cancellationToken);
            stopwatch.Stop();
            _logger.Log(LogLevel.Information, , eventName),
                $"End processing HTTP request after {stopwatch.Elapsed.TotalMilliseconds}ms - {response.StatusCode}");
            return response;
        }
    }

EventId 中的Name属性,最后在 nlog的 EventLogInfo中被认定为 Property[EventId_Name], 所以我们有以上操作。

按照上文方式,添加到 CustomHttpMessageHandlerFilter,并注册为 IHttpMessageHandlerFilter 实现。

-----------------------------------------------------

按照文档的要求,尽量早点注册自定义Nlog LayoutRenderer,

因此我在 main函数开始的时候就注册了该Renderer, 注册名称是TraceId

public static void Main(string[] args)
{
            LayoutRenderer.Register<EqidLayoutRenderer>("eqid");
......
}

最终输出如下:

// :: [Info].[System.Net.Http.HttpClient.bce-request.LogicalHandler].[}].[].[125aa91f0011426c000000045dea5ea0]
Start processing HTTP request http://localhost:5000/v1/eqid/125aa91f0011426c000000045dea5ea0 GET
// :: [Info].[System.Net.Http.HttpClient.bce-request.LogicalHandler].[}].[].[125aa91f0011426c000000045dea5ea0]
End processing HTTP request after .9971ms - OK 

ok, 阅读本文,请务必参阅 《解剖HttpClientFactory,自由扩展HttpMessageHandler》思路,解剖HttpClientFactory之后,再结合Nlog LayoutRenderer.

相关文章