I am trying to write a custom LayoutRenderer that logs data read from an object, but it seems that NLog is not working properly with Dependency Injection.
Here is my 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);
}
}
It is using this RequestContext object:
public class RequestContext
{
public string CustomValue { get; set; } = "Valid custom value";
}
I am also wiring up DI, configuring NLog and registering my LayoutRenderer in Startup.cs:
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");
// ...
}
I am then trying to use my ${custom-value}
in nlog.config, but I am getting an error on the AddNLog()
call:
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) ...
The reason why I am trying this is that I would like to log some information accessible only from the controller (like the TraceIdentifier, parts of the URL, and some request-specific custom stuff). The values in RequestContext would be set by the controller when it gets a request.
The following Renderer works as expected, which makes me think this is a dependency injection problem:
[LayoutRenderer("custom-value")]
public class CustomLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append("Hello, World!");
}
}
I did see this NLog bug but it's marked as fixed now, which is why I am posting here rather than there.
And for the sake of completeness, here is what I have added to my project.json
:
"dependencies": {
...
"NLog.Extensions.Logging": "1.0.0-*",
"NLog.Web.AspNetCore": "4.3.0"
},
Two methodes:
You could make NLog DI aware. Add to your 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
};
This is a more generic approach.
Or, override from AspNetMvcLayoutRendererBase
(NLog.Web.AspNetCore) and use HttpContextAccessor?.HttpContext?.TryGetRequest()
and don't add the constructor.
This only works when needing 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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With