Configure NLog routing, structured output, and file retention
Set up NLog targets and rules, JSON properties, NLog 6 archiving, asynchronous buffers, and ASP.NET Core integration, then verify collection in Guance.
NLog separates two decisions that are often tangled together: how to write a record, and which records should reach that destination. Targets define output and formatting; rules match logger names and levels and route events to those targets.
That model works for a console service with one output stream and for an application with different routing requirements for payments, framework diagnostics, and errors. This guide uses current NLog configuration terminology and calls out the version boundary for NLog 6 file archiving.
Start with targets and rules
Install NLog for a standalone .NET application:
dotnet add package NLog
Save the following as nlog.config and ensure the deployed application loads that file. The xsi namespace declaration is required for the xsi:type attributes:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="logs/nlog-internal.txt">
<targets>
<target name="console" xsi:type="Console"
layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />
<target name="file" xsi:type="File"
fileName="${basedir}/logs/app-${shortdate}.log"
layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="console" />
<logger name="*" minlevel="Error" writeTo="file" />
</rules>
</nlog>
The first rule sends Info and higher to the console. The second also sends Error and Fatal to the file. Matching one rule does not automatically prevent later rules from receiving the same event.
NLog has six event severity levels: Trace, Debug, Info, Warn, Error, and Fatal. A standalone application can use them as follows:
var logger = NLog.LogManager.GetCurrentClassLogger();
logger.Trace("Detailed execution state");
logger.Debug("Loading configuration");
logger.Info("Application started");
logger.Warn("Configuration value missing; using default");
logger.Fatal("Required startup dependency unavailable");
Pass an exception as an exception argument when handling a failure:
try
{
ConnectToDatabase(); // Application-defined operation.
}
catch (Exception ex)
{
logger.Error(ex, "Database connection failed");
throw;
}
The target layout above renders the exception. Without an exception-aware layout, passing the exception does not guarantee the text output includes its full details.
NLog's internal log diagnoses NLog itself—configuration loading, routing, permissions, and target failures. It is separate from application events. Make its path writable and review it when application logs disappear. Configuration and internal logging
Use layout renderers for a stable event format
NLog layouts use ${...} renderers:
| Renderer | Purpose |
|---|---|
${longdate} / ${shortdate} |
Full or date-only local-time representation |
${level:uppercase=true} |
Uppercase severity |
${logger} |
Logger name, often the class name |
${message} |
Formatted message |
${exception:format=tostring} |
Exception text and stack information |
${machinename}, ${processid}, ${threadid} |
Execution environment identifiers |
A local timestamp without an offset can be ambiguous across hosts. Use an explicit UTC or offset-bearing timestamp in the collected schema, and test how the collector interprets it.
Message-template arguments can also be preserved as properties:
logger.Info(
"User {UserId} purchased {Item} for {Price}",
42, "Orange Soda", 100.00);
A text layout can reference one with ${event-properties:item=UserId}. A JSON layout can include the event properties as fields, keeping the value available for filtering instead of only embedding it in the message string.
Emit JSON with event properties
Add this target to the same configuration and route the desired logger rules to jsonfile:
<target name="jsonfile" xsi:type="File"
fileName="${basedir}/logs/app.json">
<layout xsi:type="JsonLayout" includeEventProperties="true">
<attribute name="time" layout="${date:format=o:universalTime=true}" />
<attribute name="level" layout="${level:uppercase=true}" />
<attribute name="logger" layout="${logger}" />
<attribute name="message" layout="${message}" />
<attribute name="exception" layout="${exception:format=tostring}" />
</layout>
</target>
For example, a rule of <logger name="*" minlevel="Info" writeTo="jsonfile" /> sends the application records there. includeEventProperties is the current property name; older examples may use includeAllProperties. Check the JsonLayout documentation against your installed version.
Inspect an ordinary event and an exception. Verify the timestamp, property types, and whether the exception stays inside a single JSON event. Also avoid collisions between application property names and fixed fields such as time or level.
In Guance, configure the DataKit log collector with the actual path, permissions, and parsing rules. Map the timestamp and severity to the expected fields, then search by service and logger. For containers, configure runtime log collection rather than assuming console output has already reached Guance.
Bound file growth and order routing rules
A date in fileName separates files by day, but does not by itself establish a retention limit. Configure archiving and cleanup deliberately.
For NLog 6, a size-based file target can use archiveSuffixFormat:
<target name="rollingFile" xsi:type="File"
fileName="${basedir}/logs/app.log"
archiveAboveSize="104857600"
archiveSuffixFormat="_{0:00}"
maxArchiveFiles="10"
layout="${longdate}|${level}|${logger}|${message} ${exception:format=tostring}" />
The threshold is 100 MiB, and the configuration keeps up to ten archive files. NLog 6 changed archive configuration, including the older {#} placeholder and numbering options. Do not combine an older archive example with newer settings without checking the file-target documentation.
Rules can also suppress routine framework events and route a subsystem separately:
<rules>
<logger name="Microsoft.*" maxlevel="Warn" final="true" />
<logger name="MyApp.Payments.*" minlevel="Info"
writeTo="paymentsFile" final="true" />
<logger name="*" minlevel="Info" writeTo="console" />
</rules>
Define the paymentsFile and console targets before using these rules. The first rule has no destination and stops matching framework events at Warn and below. Framework errors can continue to later rules. Warn is NLog's level name; Warning belongs to other logging APIs.
Order matters. A discard rule cannot undo a write that an earlier matching rule already performed. A named “blackhole” target is unnecessary for the no-destination rule above, and referencing one without defining it would be a configuration error. Decide whether payment events should also reach general output before using final="true".
Configure NLog in code or through ASP.NET Core
XML is optional. A standalone application can construct its configuration in code:
using NLog;
using NLog.Config;
using NLog.Targets;
var configuration = new LoggingConfiguration();
var console = new ConsoleTarget("console")
{
Layout = "${longdate}|${level}|${logger}|${message} ${exception:format=tostring}"
};
configuration.AddRule(LogLevel.Info, LogLevel.Fatal, console);
LogManager.Configuration = configuration;
try
{
LogManager.GetCurrentClassLogger().Info("Application started");
}
finally
{
LogManager.Shutdown();
}
For ASP.NET Core, install NLog.Web.AspNetCore and register the provider using the package's host integration:
using NLog.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Host.UseNLog();
var app = builder.Build();
app.MapGet("/", () => "Hello");
app.Run();
Application and framework calls through Microsoft.Extensions.Logging.ILogger can then use the NLog provider. Verify the provider's options and filtering for your versions; clearing providers removes the other destinations, so do it intentionally. Keep host-managed lifecycle and shutdown behavior in mind rather than installing a second competing logging setup.
Frequently asked questions
How should we choose between NLog and Serilog?
Compare the configuration and event model your team needs. NLog's routing rules, layout renderers, and file reloading can be useful in a deployment managed through configuration. Serilog's message templates and sink ecosystem provide another strong structured-logging workflow. Test framework integration, required destinations, property serialization, and operational ownership rather than choosing by age or a universal ranking.
Why did an nlog.config edit have no effect?
Confirm that the running process loaded the file you edited and that autoReload="true" is enabled. A project may copy a source file into the output directory, leaving the running copy unchanged when only the source is edited. Configure deployment copying appropriately and inspect NLog's internal log for reload errors. File watching and configuration reload must be tested in the actual deployment environment.
Do NLog failures always stay inside the internal log?
No. Exception behavior depends on settings. NLog normally handles many logging failures internally, but configuration errors can be surfaced with throwConfigExceptions, and throwExceptions changes runtime behavior for troubleshooting. Avoid making application reliability depend on a blanket assumption that logging can never throw. Consult the configuration reference.
How do we make a target asynchronous?
Wrap it with AsyncWrapper, for example:
<target name="asyncFile" xsi:type="AsyncWrapper"
queueLimit="10000" overflowAction="Block">
<target xsi:type="File" fileName="${basedir}/logs/async-app.log"
layout="${longdate}|${level}|${message}" />
</target>
Route rules to asyncFile. This example uses Block when the buffer is full, which can delay the application. The documented default overflow behavior is Discard, which can lose events under sustained pressure. Choose that trade-off explicitly, monitor it, and flush on orderly shutdown. A process crash can still lose buffered events. AsyncWrapper settings
Related reading
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan