-
Notifications
You must be signed in to change notification settings - Fork 1.4k
How to write a custom layout renderer
Layout renderers can capture, format and output additional context details that can be used by NLog Layouts. There are 2 ways to create a custom layout renderer.
- Register custom lambda-delegate-function with a symbol-name (Introduced with NLog v4.4)
- Register custom class that inherits from
LayoutRenderer
that also supports additional configuration options.
NLog 4.7 introduces a fluent registration API using LogManager.Setup()
, where you create a layout renderer with a lambda:
NLog.LogManager.Setup().SetupExtensions(s =>
s.RegisterLayoutRenderer("trace_id", (logevent) => CorrelationIdentifier.TraceId.ToString())
);
NLog 4.4 was the first edition to support lambda function will accept 1 or 2 parameters and should return a string
.
- 1 parameter: the
logEventInfo
. - 2 parameters:
logEventInfo
and the current NLog config.
Examples
//register ${text-fixed}
LayoutRenderer.Register("text-fixed", (logEvent) => "2");
//register ${trace-identifier}
LayoutRenderer.Register("trace-identifier", (logEvent) => HttpContext.Current.TraceIdentifier);
//Using logEventInfo, ${message-length}
LayoutRenderer.Register("message-length", (logEvent) => logEvent.FormattedMessage.Length);
//Using config, ${targetCount}
LayoutRenderer.Register("targetCount",(logEvent, config) => config.AllTargets.Count);
Need the HTTP-context (e.g. Request, Session etc) for ASP.NET or ASP.NET Core?
Include the NLog.Web (ASP.NET Classic) or NLog.Web.AspNetCore nuget-package.
And usage:
using NLog.Web.LayoutRenderers;
AspNetLayoutRendererBase.Register("SessionItem1",
(logEventInfo, httpContext, loggingConfiguration)
=> httpContext.Session["SessionItem"]); // usage ${SessionItem1}
Create a class that inherits from NLog.LayoutRenderers.LayoutRenderer
, set the [LayoutRenderer("your-name")]
on the class and override the Append(StringBuilder builder, LogEventInfo logEvent)
method.
Invoke in this method builder.Append(..)
to render your custom layout renderer.
We create a ${hello-world}
layout renderer, which renders..."hello world!".
[LayoutRenderer("hello-world")]
public class HelloWorldLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append("hello world!");
}
}
Just create public properties on the Layout Renderer. The properties could be decorated with the [RequiredParameter]
and [DefaultParameter]
attributes. The [DefaultParameter]
can be passed to the layout renderer without using the name.
for example:
[LayoutRenderer("hello-world")]
public class HelloWorldLayoutRenderer : LayoutRenderer
{
/// <summary>
/// I'm not required or default
/// </summary>
public bool OtherOption { get; set; }
/// <summary>
/// I'm required, and will fail to initialize when not specified
/// </summary>
[RequiredParameter]
public string RequiredOption { get; set; }
/// <summary>
/// I'm the default parameter, and can be assigned without specifying option-name
/// </summary>
[DefaultParameter]
public string DefaultPlanet { get; set; }
Example usages
-
${hello-world}
- raises exception: required parameterRequiredOption
isn't set -
${hello-world:RequiredOption=abc}
- OK,RequiredOption
property set -
${hello-world:Earth:RequiredOption=abc}
- Default parameterDefaultPlanet
set toEarth
-
${hello-world:Earth:RequiredOption=abc:OtherOption=true}
- All 3 properties set
NLog will automatically capture relevant context state, when using AsyncWrapper-target to perform actual writing on background-thread (to avoid logging objects after they have been disposed). The following class-attributes can used for the LayoutRenderer that enables additional performance:
-
[ThreadAgnostic]
LayoutRenderer does not capture state from the application-thread logging. Ex.
${threadid}
cannot be[ThreadAgnostic]
.For LayoutRenderer marked as
[ThreadAgnostic]
then NLog can skip the overhead of capturing state.If just a single LayoutRenderer in a Layout is not marked as
[ThreadAgnostic]
, then NLog introduces the overhead of state capture. -
[ThreadSafe]
Introduced with NLog 4.5.3, and made obsolete with NLog 5.0 that expects all to be threadsafe.
LayoutRenderer will render correct output regardless of the number of application-threads running inside.
For LayoutRenderer marked as
[ThreadSafe]
then NLog will skip using "global" locks when capturing state, thus application-threads will not experience lock-congestion inside NLog.If just a single LayoutRenderer in a Layout is not marked as
[ThreadSafe]
, then NLog introduces the overhead of "global" lock when doing state capture.
Example of class-attributes for LayoutRenderer:
using NLog.Config;
[LayoutRenderer("hello-world")]
[ThreadAgnostic]
public class HelloWorldLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append("hello world!");
}
}
NLog 5.0 enables you to have multiple type-aliases for a single class. Before one had to inherit from the same class to provide additional type-names.
[LayoutRenderer("hello-world")] // ${hello-world}
[LayoutRenderer("hello-earth")] // ${hello-earth}
public class HelloWorldLayoutRenderer : LayoutRenderer
{
The type-alias can then be used when wanting to use the LayoutRenderer in NLog SimpleLayout.
Notice NLog 5.0 automatically ignores dashes -
in type-alias, so no extra alias is needed for this: Ex. ${helloworld}
- Troubleshooting Guide - See available NLog Targets and Layouts: https://nlog-project.org/config
- Getting started
- How to use structured logging
- Troubleshooting
- FAQ
- Articles about NLog
-
All targets, layouts and layout renderers
Popular: - Using NLog with NLog.config
- Using NLog with appsettings.json