Reputation: 765
.NET Framework 4.8, NLog 4.7.6
I have implemented two simple LayoutRenderer
subclasses which just write a Guid (fetched from Web context or a static Stack<T>
). These are working great in two Web projects, but they are not working in a third Web app nor in a couple of console apps/services. Configuration is consistent between apps, but some work and some don't.
Example:
[LayoutRenderer("request-guid")]
public class RequestGuidLayoutRenderer:
LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
var guid = RequestIdentityService.Current.RequestGuid?.ToString() ?? Constants.NullSession;
builder.Append(guid);
}
}
There is also a session-guid
layout renderer that is almost identical, except for getting the SessionGuid
instead of the RequestGuid
.
My config section looks like this, where MyAssemblyName contains the layout renderers:
<nlog autoReload="true">
<extensions>
<assembly name="MyAssemblyName" />
</extensions>
<variable name="brief" value="${longdate} ${session-guid} ${request-guid} ${level} ${replace:searchFor=^.*\.([^.]+)$:replaceWith=$1:regex=true:inner=${logger}} ${message}${onexception:inner= ${exception:format=toString,Data}}" />
<variable name="verbose" value="${longdate} | ${machinename} | ${processid} | ${processname} | ${session-guid} ${request-guid} | ${level} | ${logger} | ${message}${onexception:inner= ${exception:format=toString,Data}}" />
<targets>
<target name="all_in" type="File" layout="${verbose}" fileName="C:/log/appx/current.txt" archiveFileName="C:/log/appx/archive/{#}.txt" archiveNumbering="DateAndSequence" archiveAboveSize="10485760" archiveDateFormat="yyyyMMdd" keepFileOpen="false" maxArchiveFiles="100" encoding="iso-8859-2" />
<target name="LogStreaming" type="Trace" layout="${verbose}" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="all_in" />
<logger name="*" minlevel="Error" writeTo="LogStreaming" />
</rules>
</nlog>
To try to ensure that these layout renderers are registered, I am also registering them at the beginning of Application_Start()
for the Web apps, and in Main()
or equivalent in other app types.
public static void Register()
{
LayoutRenderer.Register<RequestGuidLayoutRenderer>("request-guid");
LayoutRenderer.Register<SessionGuidLayoutRenderer>("session-guid");
LogManager.GetCurrentClassLogger().Error($"Registered NLog LayoutRenderers request-guid and session-guid.");
}
For two Web apps this is working flawlessly, but for a third it is producing no output for these layout renderers. Internal logs confirm that it's having trouble finding them, or perhaps it's trying to use them before they have been registered.
Error parsing layout session-guid will be ignored. Exception: System.ArgumentException: LayoutRenderer cannot be found: 'session-guid'
at NLog.Config.Factory`2.CreateInstance(String itemName)
at NLog.Layouts.LayoutParser.GetLayoutRenderer(ConfigurationItemFactory configurationItemFactory, String name, Nullable`1 throwConfigExceptions)
Error parsing layout request-guid will be ignored. Exception: System.ArgumentException: LayoutRenderer cannot be found: 'request-guid'
at NLog.Config.Factory`2.CreateInstance(String itemName)
at NLog.Layouts.LayoutParser.GetLayoutRenderer(ConfigurationItemFactory configurationItemFactory, String name, Nullable`1 throwConfigExceptions)
I discovered accidentally that if I set throwConfigExceptions="true"
in config then it suddenly WORKS in the Web app where it otherwise doesn't! But why? And is there an alternative way to fix it so that I don't have to risk runtime errors from logging breaking our apps?
Upvotes: 1
Views: 1230
Reputation: 36750
For some reason you are registering too late.
Maybe there is a static invocation that parses the config before registering.
You can reload the config after registering:
LogManager.Configuration = LogManager.Configuration.Reload();
LogManager.ReconfigExistingLoggers();
Upvotes: 1