﻿# Logging


NServiceBus endpoints integrate with the standard .NET logging infrastructure. [Microsoft.Extensions.Logging](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/) provides rich filtering, structured logging, and [works seamlessly with ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/), the Generic Host, and many third-party providers.


## Using `ILogger<T>` in handlers

The recommended way to write log entries inside message handlers is to inject `ILogger<T>` through the constructor:

<!-- snippet: InjectingILoggerInterface -->

```cs
public class MyHandler(ILogger<MyHandler> logger) : IHandleMessages<MyMessage>
{
    public Task Handle(MyMessage message, IMessageHandlerContext context)
    {
        logger.LogInformation("Handling message");
        return Task.CompletedTask;
    }
}
```

<!-- endsnippet -->

`ILogger<T>` is automatically wired into the dependency injection container when hosting with the .NET Generic Host.

## Configuring the default rolling file logger

When no external logging providers are registered, NServiceBus supplies backward-compatible defaults: a colored console provider and a rolling file provider. Consider registering standard [Microsoft logging providers](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/providers) such as Console, Debug, or EventLog for new applications.

If continuing with the built-in NServiceBus providers, configure the rolling file logger through `RollingLoggerProviderOptions`:

<!-- snippet: ConfiguringRollingFileLogger -->

```cs
void ConfigureHost()
{
    var builder = Host.CreateApplicationBuilder();

    builder.Services.Configure<RollingLoggerProviderOptions>(options =>
    {
        options.Directory = @"C:\logs";
        options.LogLevel = LogLevel.Debug;
        options.NumberOfArchiveFilesToKeep = 10;
        options.MaxFileSizeInBytes = 10L * 1024 * 1024;
    });
}
```

<!-- endsnippet -->

When external logging providers are added to the host, the built-in providers automatically disable themselves so the external configuration takes over without any manual opt-out.

## Using custom logging providers

Register custom logging frameworks directly with the `Microsoft.Extensions.Logging` infrastructure on the host:

```csharp
var builder = Host.CreateApplicationBuilder();

builder.Logging.AddSerilog();
```

## High-performance logging

For scenarios that require minimal allocations, use the [`LoggerMessage`](https://learn.microsoft.com/en-us/dotnet/core/extensions/loggermessage-generator) source generator:

<!-- snippet: UsingLoggerMessageSourceGenerator -->

```cs
[LoggerMessage(
    EventId = 0,
    Level = LogLevel.Information,
    Message = "Processing message {MessageId}")]
static partial void LogProcessingMessage(ILogger logger, string messageId);
```

<!-- endsnippet -->

> [!WARNING]
> The NServiceBus-specific logging APIs described in the next section below are still functional but produce obsolete warnings starting in version 10.2 and will be removed in version 12. Use the `Microsoft.Extensions.Logging` patterns shown earlier for new development.

## Enriching logs outside the message pipeline

Logs written from inside the message-processing pipeline automatically include the endpoint name and identifier as structured log properties. Logs written from code outside the pipeline, such as a hosted background task or a custom timer callback, do not include this context by default.

To attach endpoint context to those logs, resolve the `EndpointLoggingScope` from the DI container and use the `BeginEndpointScope` extension method:

<!-- snippet: UsingEndpointLoggingScope -->

```cs
public class MyBackgroundService(
    ILogger<MyBackgroundService> logger,
    EndpointLoggingScope endpointScope) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using (logger.BeginEndpointScope(endpointScope))
        {
            logger.LogInformation("Doing background work");
        }

        await Task.CompletedTask;
    }
}
```

<!-- endsnippet -->

The `EndpointLoggingScope` instance is scoped to the endpoint and carries the endpoint name and identifier with it. The `BeginEndpointScope` extension method detects when the call occurs inside the message-processing pipeline and returns a no-op scope. This avoids duplicate scope entries when the pattern is used from message handlers.


## Built-in logging

When no external logging providers are configured, NServiceBus provides a built-in logging mechanism that does not depend on any external libraries. While limited in terms of available log targets, this built-in mechanism is production-ready and offers defaults that are reasonable for most deployments. The built-in framework is available and used as default in all NServiceBus hosting modes. Regardless of whether the built-in logging or a custom logging library is used under the hood, the NServiceBus logging abstractions can be used for writing log messages in user code. By default NServiceBus has three log targets configured:

### Console

All `Info` (and above) messages are written to the current console if one is available in the hosting environment.

Errors will be written with `ConsoleColor.Red`. Warnings will be written with `ConsoleColor.DarkYellow`. All other messages will be written with `ConsoleColor.White`.

### Trace

All `Warn` (and above) messages are written to `Trace.WriteLine` and therefore can be forwarded to any [trace listener](https://learn.microsoft.com/en-us/dotnet/framework/debug-trace-profile/trace-listeners).

### Rolling file

All `Info` (and above) messages are written to file. NServiceBus maintains up to 10 log files, each up to 10 MB in size. When the current file becomes full, NServiceBus automatically switches to the next one. If all ten files are full, the oldest file is overwritten.

The default logging directory is `HttpContext.Current.Server.MapPath("~/App_Data/")` for websites and `AppDomain.CurrentDomain.BaseDirectory` for all other processes.

The default file name is `nsb_log_yyyy-MM-dd_N.txt`, where `N` is a sequence number for when the log file reaches the max size.

### Changing the defaults

The built-in logging mechanism allows customizing the logging directory and applying a global filter/threshold for log entries.

#### Changing the logging level

Each log entry is associated with a _level_ that describes how important and critical that entry is. The built-in levels are following (in order of increasing importance)

 * Debug
 * Info
 * Warn
 * Error
 * Fatal

Configuring the global threshold to one of the levels described above means that all messages below that level are discarded. For example setting the threshold value to `Warn` means that only `Warn`, `Error` and `Fatal` messages are written.

The `LogManager` class is the entry point for the logging configuration. If needed, it allows using custom logging integrations (see below). It also allows customization of the default built-in logging. The `Use` generic method returns the `LoggingFactoryDefinition`-derived object that provides the customization APIs.

Configuring the global threshold to one of the levels described above means that all messages below that level are discarded. For example setting the threshold value to `Warn` means that only `Warn`, `Error` and `Fatal` messages are written.

The `LogManager` class is the entry point for the logging configuration. If needed, it allows using custom logging integrations (see below). It also allows customization of the default built-in logging. The `Use` generic method returns the `LoggingFactoryDefinition`-derived object that provides the customization APIs.

<!-- snippet: OverrideLoggingLevelInCode -->

```cs
var defaultFactory = LogManager.Use<DefaultFactory>();
defaultFactory.Level(LogLevel.Debug);
```

<!-- endsnippet -->


#### Changing the log path

<!-- snippet: OverrideLoggingDirectoryInCode -->

```cs
var defaultFactory = LogManager.Use<DefaultFactory>();
defaultFactory.Directory("pathToLoggingDirectory");
```

<!-- endsnippet -->

## Custom logging


For custom logging it is recommended to use the [Microsoft.Extensions.Logging](extensions-logging.md) package with a supported provider.


## When to configure logging

Logging must be configured *as early as possible* at application startup, before any NServiceBus endpoint configuration is performed. This is required because the NServiceBus logging infrastructure is initialized in a static context.

For example:

 * At the start of the `Main` method in console applications or Windows services
 * During application startup configuration in ASP.NET Core applications

## Writing a log entry

In legacy endpoints, the NServiceBus logging abstraction is used for writing log messages in application code.

Set up a single static field to an `ILog` in the classes, and then use it in all methods:

<!-- snippet: UsingLogging -->

```cs
public class ClassUsingLogging
{
    static ILog log = LogManager.GetLogger<ClassUsingLogging>();
    readonly int times = 2;

    public void SomeMethod()
    {
        log.Warn("Something unexpected happened.");
        if (log.IsDebugEnabled)
        {
            log.Debug("Something expected happened.");
            log.DebugFormat("Also, this other thing happened {0} times.", times);
        }
    }
}
```

<!-- endsnippet -->

Make sure that logging is correctly initialized before resolving the `ILog` instance. Not doing so can result in a logger using an incorrect configuration.

Since `LogManager.GetLogger(..);` is a relatively expensive call, it is important that the field is `static` so that the call happens only once per class and has the best possible performance. In addition, wrapping logging messages in conditional statements prevents unnecessary processing when a given level of logging is not needed. For example, when writing debug messsages, ensure `log.IsDebugEnabled` before proceeding.

The `*Format` APIs pass the message template and its arguments to the underlying logging framework, so their behavior can vary depending on the framework being used. Some frameworks, such as NLog, support special formatting syntax that allows structured log entries to be created. Refer to the documentation for the specific logging framework for details. When using the built-in logger, the message is formatted using `string.Format` before it is written.

## Additional exception data

Starting from NServiceBus version 7.2, exceptions from message processing might contain additional error information in the `Exception.Data` property. While the default logger exposes this information automatically, other loggers might require additional configuration.

[Custom behaviors](/nservicebus/pipeline/manipulate-with-behaviors.md) can provide additional exception data by adding information to the `Exception.Data` property.


## Logging message contents

When NServiceBus sends a message, it writes the result of the `ToString()` method of the message class to the log. By default, this writes the name of the message type only. To write the full message contents to the log, override the `ToString()` method of the relevant message class:

<!-- snippet: MessageWithToStringLogged -->

```cs
public class MessageToLog :
    IMessage
{
    public Guid EventId { get; set; }
    public DateTime? Time { get; set; }
    public TimeSpan Duration { get; set; }

    public override string ToString()
    {
        return $"MyMessage: EventId={EventId}, Time={Time}, Duration={Duration}";
    }
}
```

<!-- endsnippet -->

NServiceBus only makes these calls at a log threshold of DEBUG or lower.

## Unit testing

Unit testing of logging is supported by [the `NServiceBus.Testing` library](/nservicebus/testing/index.md#testing-logging-behavior).
