使用依赖注入的构造函数自定义 NLog LayoutRenderer

问题描述 投票:0回答:3

我正在尝试编写一个自定义 LayoutRenderer 来记录从对象读取的数据,但 NLog 似乎无法与依赖注入正常工作。

这是我的 CustomLayoutRenderer:

[LayoutRenderer("custom-value")]
public class CustomLayoutRenderer : LayoutRenderer
{
    private readonly RequestContext _context;

    public CustomLayoutRenderer(RequestContext context)
    {
        _context = context;
    }

    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
        builder.Append(_context.CustomValue);
    }
}

它正在使用这个RequestContext对象:

public class RequestContext
{
    public string CustomValue { get; set; } = "Valid custom value";
}

我还在 Startup.cs 中连接 DI、配置 NLog 并注册我的 LayoutRenderer:

    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        services.AddScoped<RequestContext>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        LayoutRenderer.Register<CustomLayoutRenderer>("custom-value");

        loggerFactory.AddNLog();
        app.AddNLogWeb();
        env.ConfigureNLog("nlog.config");
        // ...
    }

然后我尝试在 nlog.config 中使用我的

${custom-value}
,但我在
AddNLog()
调用中收到错误:

2017-02-03 13:08:08.0284 从 [project-folder] in\Debug 解析配置时出错 et452\win7-x64\NLog.config 失败。 异常:NLog.NLogConfigurationException:在\Debug中解析[项目文件夹]时出现异常 et452\win7-x64\NLog.config。 NLog.NLogConfigurationException:无法访问类型的构造函数:ATest.CustomLayoutRenderer。是否获得了所需的许可? 在 NLog.Internal.FactoryHelper.CreateInstance(类型 t) ...

注释

我尝试这样做的原因是我想记录一些只能从控制器访问的信息(如 TraceIdentifier、部分 URL 和一些特定于请求的自定义内容)。 RequestContext 中的值将由控制器在收到请求时设置。

以下渲染器按预期工作,这让我认为这是一个依赖注入问题:

[LayoutRenderer("custom-value")]
public class CustomLayoutRenderer : LayoutRenderer
{
    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
        builder.Append("Hello, World!");
    }
}

我确实看到了这个 NLog bug 但它现在被标记为已修复,这就是为什么我在这里而不是在那里发帖。

为了完整起见,以下是我添加到我的

project.json
中的内容:

"dependencies": {
    ...
    "NLog.Extensions.Logging": "1.0.0-*",
    "NLog.Web.AspNetCore": "4.3.0"
},
c# asp.net-core nlog
3个回答
6
投票

两种方法:

1) DI 意识

您可以让 NLog DI 意识到。添加到您的startup.cs:

ConfigurationItemFactory.Default.CreateInstance = (Type type) =>
{ 
    // your custom target. Could be a better check ;)
    if(type == typeof(CustomLayoutRenderer))
      return new CustomLayoutRenderer(...); // TODO get RequestContext
    else
      return Activator.CreateInstance(type); //default
};

这是一种更通用的方法。

2)AspNetMvcLayoutRendererBase

或者,从

AspNetMvcLayoutRendererBase
(NLog.Web.AspNetCore) 重写并使用
HttpContextAccessor?.HttpContext?.TryGetRequest()
并且不添加构造函数。

这只在需要时才有效

HttpContext

[LayoutRenderer("custom-value")]
public class MyCustomRenderer : AspNetLayoutRendererBase
{
    protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
    {
        var httpRequest = HttpContextAccessor?.HttpContext?.TryGetRequest();

        if (httpRequest == null)
            return;


        builder.Append(httpRequest.Something); //TODO

    }
}

3
投票

查看 NLog 源代码

FactoryHelper
。看起来你必须提供一个默认构造函数。

您可能还想考虑使用 NLog.Web 包中的布局渲染。有几种可以获取特定于请求的信息。甚至还有一个选项可以使用

HttpContext
布局
 记录来自 
${aspnet-Item}

的内容

如果这不适合您,也许“有状态”记录器会像我在这里给出的答案中那样工作:https://stackoverflow.com/a/32857908/1289052


0
投票
LogManager.Setup().SetupExtensions(ext =>
{
     ext.RegisterServiceProvider(sp);
     ext.RegisterLayoutRenderer(() =>
     {
          return new TracingContextLayoutRenderer(sp.GetService<ITracingContextAccessor>()!, sp.GetService<HostConfiguration>()!);
     }, "tracing-context");
});
© www.soinside.com 2019 - 2024. All rights reserved.