使用依赖注入的带有构造函数的自定义 NLog LayoutRenderer
Custom NLog LayoutRenderer with constructor using Dependency Injection
我正在尝试编写一个自定义 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";
}
我还在连接 DI,配置 NLog 并在 Startup.cs:
中注册我的 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 Error Parsing configuration from [project-folder]\bin\Debug\net452\win7-x64\NLog.config failed.
Exception: NLog.NLogConfigurationException: Exception when parsing [project-folder]\bin\Debug\net452\win7-x64\NLog.config.
NLog.NLogConfigurationException: Cannot access the constructor of type: ATest.CustomLayoutRenderer. Is the required permission granted?
at NLog.Internal.FactoryHelper.CreateInstance(Type 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"
},
正在查看 FactoryHelper
的 NLog 源代码。看来您必须提供默认构造函数。
您可能还想考虑使用 NLog.Web 包中的布局渲染。有几个可以获取request-specific信息。甚至还有一个选项可以使用 ${aspnet-Item}
布局
记录来自 HttpContext
的内容
如果这不适合您,也许 "stateful" 记录器会像我在此处给出的答案中那样工作:
两种方法:
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
}
}
我正在尝试编写一个自定义 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";
}
我还在连接 DI,配置 NLog 并在 Startup.cs:
中注册我的 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 Error Parsing configuration from [project-folder]\bin\Debug\net452\win7-x64\NLog.config failed. Exception: NLog.NLogConfigurationException: Exception when parsing [project-folder]\bin\Debug\net452\win7-x64\NLog.config. NLog.NLogConfigurationException: Cannot access the constructor of type: ATest.CustomLayoutRenderer. Is the required permission granted? at NLog.Internal.FactoryHelper.CreateInstance(Type 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"
},
正在查看 FactoryHelper
的 NLog 源代码。看来您必须提供默认构造函数。
您可能还想考虑使用 NLog.Web 包中的布局渲染。有几个可以获取request-specific信息。甚至还有一个选项可以使用 ${aspnet-Item}
布局
HttpContext
的内容
如果这不适合您,也许 "stateful" 记录器会像我在此处给出的答案中那样工作:
两种方法:
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
}
}