The Atatus .NET APM Agent connects your logs with your traces and transactions, so you can jump from any log line to the request that produced it and back again.
How you get logs into Atatus depends on which logging framework you use. Pick your framework below:
- Microsoft.Extensions.Logging (ILogger): the agent sends your
ILoggerlogs to Atatus directly. You only enable one setting. - Serilog: send logs to Atatus with the
Atatus.Serilog.Sinkssink, or add correlation IDs to logs you ship yourself with theAtatus.SerilogEnricherenricher. - NLog: send logs to Atatus with the
Atatus.NLog.Targetstarget, or add correlation IDs to logs you ship yourself with theAtatus.NLoglayout renderers.
Using a different framework? See Correlate logs manually.
How log correlation works
Correlation adds a set of identifiers to each log record produced while a request is being traced:
| Field | Description |
|---|---|
trace.id |
Uniquely identifies a request across distributed services |
transaction.id |
Marks the primary transaction being traced |
span.id |
Refers to the specific operation within a transaction |
These IDs let the Atatus dashboard link a log line to its related trace and transaction, so you can move between logs and traces while debugging. Every integration on this page attaches them automatically.
Microsoft.Extensions.Logging (ILogger)
If your application uses Microsoft.Extensions.Logging.ILogger, the default logging abstraction in ASP.NET Core and modern .NET applications, the Atatus .NET Agent can send your logs to Atatus directly, with correlation IDs already attached. There is no enricher, sink, or extra NuGet package to add. It is built into the agent.
Enable it by setting CaptureLogs to true. It is disabled by default:
"Atatus": {
"AppName": "YOUR_APP_NAME",
"LicenseKey": "YOUR_LICENSE_KEY",
"CaptureLogs": true
}
You can also enable it with the environment variable ATATUS_CAPTURE_LOGS=true. See Customizing the agent for all configuration options.
Once enabled, the agent hooks into the ILogger pipeline and sends every log event to Atatus along with the active trace, transaction, and span IDs. As long as the agent is registered in your application (for example, via builder.Services.AddAllAtatus()), each ILogger call is captured and visible in the Atatus Logs dashboard:
public class OrdersController : ControllerBase
{
private readonly ILogger<OrdersController> _logger;
public OrdersController(ILogger<OrdersController> logger)
{
_logger = logger;
}
[HttpPost]
public IActionResult Create(Order order)
{
_logger.LogInformation("Creating order {OrderId}", order.Id);
// ...
return Ok();
}
}
Beyond enabling CaptureLogs, you do not need to add any enricher, layout renderer, sink, or manual ID injection. The agent handles capture, correlation, and delivery behind the scenes.
Logs of every level are sent to the Logs dashboard. Log events at Error or Critical level are also captured as APM errors under Error Tracking.
Serilog
For Serilog, you have two options: let the sink send logs to Atatus for you, or write logs yourself and add correlation IDs to them.
Send Serilog logs to Atatus
The Atatus.Serilog.Sinks package provides a sink that sends log events straight to Atatus, with correlation fields attached automatically. No enricher is needed.
This page assumes the Atatus .NET Agent is already running in the same process (for example via builder.Services.AddAllAtatus()). If it is not, see .NET Core Agent Installation first. The sink reuses the agent's license key, app name, and endpoint, so you do not set them.
Install the package:
$ dotnet add package Atatus.Serilog.Sinks
Add the sink when configuring your logger, with no arguments:
using Serilog;
using Atatus.Serilog.Sinks;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Atatus()
.CreateLogger();
Log.Information("something happened");
To send these logs under a different service name than the agent uses, pass appName: "my-worker" to .WriteTo.Atatus(...).
Add correlation IDs to Serilog logs you ship yourself
If you prefer to write Serilog logs to the console or a file yourself and let the Atatus Infra Agent forward them to Atatus, use the Atatus.SerilogEnricher enricher. It adds correlation IDs to each log line but does not send the logs itself. The Infra Agent handles delivery.
Enable it when configuring your logger. Add the using directive so the WithAtatusCorrelationInfo() extension method is in scope:
using Serilog;
using Atatus.SerilogEnricher;
var logger = new LoggerConfiguration()
.Enrich.WithAtatusCorrelationInfo()
.WriteTo.Console(outputTemplate: "[{AtatusTraceId} {AtatusTransactionId}] {Message:lj} {NewLine}{Exception}")
.CreateLogger();
The enricher sets these properties on log events created during a transaction, which you can reference from any Serilog sink:
AtatusTraceIdAtatusTransactionId
To get these logs into Atatus, write them to the console or a file and let the Atatus Infra Agent collect them. See Ship logs with the Atatus Infra Agent.
NLog
For NLog, you have two options: let the target send logs to Atatus for you, or write logs yourself and add correlation IDs to them.
Send NLog logs to Atatus
The Atatus.NLog.Targets package provides a target that sends log events straight to Atatus, with correlation fields attached automatically. No layout renderer is needed.
This page assumes the Atatus .NET Agent is already running in the same process (for example via builder.Services.AddAllAtatus()). If it is not, see .NET Core Agent Installation first. The target reuses the agent's license key, app name, and endpoint, so you do not set them.
Install the package:
$ dotnet add package Atatus.NLog.Targets
Register the target in code, with no credentials:
using NLog;
using NLog.Config;
using NLog.Targets;
var config = new LoggingConfiguration();
config.AddRuleForAllLevels(new AtatusTarget());
LogManager.Configuration = config;
var logger = LogManager.GetCurrentClassLogger();
logger.Info("something happened");
Or in nlog.config, with no attributes:
<nlog>
<extensions>
<add assembly="Atatus.NLog.Targets"/>
</extensions>
<targets>
<target name="atatus" type="Atatus" />
</targets>
<rules>
<logger name="*" minLevel="Information" writeTo="atatus" />
</rules>
</nlog>
To send these logs under a different service name than the agent uses, set AppName on the target (or the appName attribute in nlog.config).
Add correlation IDs to NLog logs you ship yourself
If you prefer to write NLog logs to the console or a file yourself and let the Atatus Infra Agent forward them to Atatus, use the Atatus.NLog layout renderers. They add correlation IDs to each log line but do not send the logs themselves. The Infra Agent handles delivery.
1. Register the extension in nlog.config
Add the package to the <extensions> section and reference the layout renderers from any target layout:
<nlog>
<extensions>
<add assembly="Atatus.NLog"/>
</extensions>
<targets>
<target type="file" name="logfile" fileName="myfile.log">
<layout type="jsonlayout">
<attribute name="trace.id" layout="${AtatusTraceId}" />
<attribute name="transaction.id" layout="${AtatusTransactionId}" />
<attribute name="span.id" layout="${AtatusSpanId}" />
</layout>
</target>
</targets>
<rules>
<logger name="*" minLevel="Trace" writeTo="logfile" />
</rules>
</nlog>
The following layout renderers are available:
${AtatusTraceId}: current trace ID${AtatusTransactionId}: current transaction ID${AtatusSpanId}: current span ID
2. Load the configuration in Program.cs
Add the required using directives and load the nlog.config file at application startup:
using NLog;
using NLog.Web;
using Atatus.NLog;
var builder = WebApplication.CreateBuilder(args);
// Load NLog configuration from nlog.config
NLog.LogManager.Setup().LoadConfigurationFromFile("nlog.config");
builder.Host.UseNLog();
The using Atatus.NLog; directive ensures the Atatus layout renderers are loaded into the NLog runtime alongside the assembly registration in nlog.config.
To get these logs into Atatus, write them to the console or a file and let the Atatus Infra Agent collect them. See Ship logs with the Atatus Infra Agent.
Correlate logs manually
If none of the built-in integrations fit your application, for example you use a different logging framework or want full control over how IDs are written, use the agent's public API to inject trace IDs manually.
There are two approaches, depending on whether your logs are structured or unstructured.
Structured logs
For structured logs, attach these fields directly to your log events:
trace.idtransaction.id
Use the Agent.Tracer.CurrentTransaction property anywhere in your code to read the active transaction's IDs:
using Atatus;
public (string traceId, string transactionId) GetTraceIds()
{
if (!Agent.IsConfigured) return default;
if (Agent.Tracer.CurrentTransaction == null) return default;
return (Agent.Tracer.CurrentTransaction.TraceId, Agent.Tracer.CurrentTransaction.Id);
}
When the agent is configured and a transaction is active, add the returned traceId and transactionId to your structured log events under the trace.id and transaction.id fields.
Unstructured logs
For unstructured (plain text) logs, such as Console.WriteLine or basic printf-style logging, embed the IDs directly into the log message string:
using Atatus;
var currentTransaction = Agent.Tracer.CurrentTransaction;
Console.WriteLine($"ERROR [trace.id={currentTransaction.TraceId} transaction.id={currentTransaction.Id}] an error occurred");
This produces output similar to:
ERROR [trace.id=cd04f33b9c0c35ae8abe77e799f126b7 transaction.id=cd04f33b9c0c35ae] an error occurred
Ship logs with the Atatus Infra Agent
Use this only if your app writes logs to the console or a file (with the enricher or layout renderers) instead of using the sink or target. The Atatus Infra Agent, running in the same environment as your service, collects those logs and forwards them to Atatus:
- Containers (Docker, Kubernetes): log to stdout or stderr and the agent picks them up automatically.
- VMs or bare metal: write logs to a file, then point the agent at it by following Custom Files.
The sink and target packages send logs to Atatus themselves, so they do not need the Infra Agent.
Delivery and troubleshooting
The sink and target batch logs in memory and flush every few seconds, so a hard crash can lose the last batch. For stronger delivery guarantees, write logs to a file and ship that file with the Atatus Infra Agent.
If no logs appear in Atatus:
- Confirm the Atatus .NET Agent is configured and running in the same process, since the sink and target rely on it for the license key, app name, and endpoint.
- For NLog, add
internalLogLevel="Warn" internalLogToConsole="true"to the<nlog>root element to surface otherwise-silent delivery errors.
+1-415-800-4104