For the complete documentation index, see llms.txt. This page is also available as Markdown.

Logging

Reference · Applies to Brighter V10 · Prerequisites: Basic Configuration

Brighter writes its diagnostics through Microsoft.Extensions.Logging, and takes its ILoggerFactory from a single static holder rather than from your container.

That one design decision explains everything else on this page, including why Brighter can appear to log nothing at all.

How Brighter Gets Its Logger

Every Brighter type that logs holds its logger in a static field, built once from Paramore.Brighter.Logging.ApplicationLogging:

using Microsoft.Extensions.Logging;
using Paramore.Brighter.Logging;

public class CommandProcessor
{
    private static readonly ILogger s_logger = ApplicationLogging.CreateLogger<CommandProcessor>();
    // ...
}

ApplicationLogging is a static holder with a settable factory:

using Microsoft.Extensions.Logging;

namespace Paramore.Brighter.Logging;

public static class ApplicationLogging
{
    public static ILoggerFactory LoggerFactory { get; set; } = new LoggerFactory();
    public static ILogger CreateLogger<T>() => LoggerFactory.CreateLogger<T>();
}

The default is a LoggerFactory with no providers, which accepts every call and writes nothing. Brighter is never silent because logging is switched off; it is silent because nothing has been given somewhere to write to.

Because CreateLogger<T>() is called with the type, the category name is the full type name — Paramore.Brighter.CommandProcessor, Paramore.Brighter.ServiceActivator.Dispatcher. Filtering on the Paramore.Brighter prefix therefore catches all of it.

Logging with Dependency Injection

If you use the DI extensions, there is nothing to do. Both AddBrighter and AddConsumers resolve ILoggerFactory from the container as they build, and hand it to ApplicationLogging:

The resolution is GetService<ILoggerFactory>(), not GetRequiredService, so a container with no factory registered is not an error — Brighter keeps the empty default and writes nothing.

Logging Without Dependency Injection

If you build a Command Processor by hand, assign the factory yourself:

Assign it before you build anything. Each type caches its logger in a static readonly field the first time that type is used, so a factory installed afterwards is never seen by the types that have already initialised.

What Brighter Logs

Log methods are source-generated with [LoggerMessage], so the messages are structured rather than interpolated:

{CommandType} and {Id} reach a structured sink as named fields, so you can query on them.

Across the Brighter source the levels divide roughly as:

Level
Used for

Trace

the finest pipeline detail

Debug

pipeline construction and message pump steps

Information

dispatch, handler counts, connection lifecycle

Warning

recoverable faults — a missing mapper, a retried delivery

Error

a failure Brighter could not handle for you

Information is a reasonable production level for Brighter's categories; Debug is verbose, because a pipeline logs as it is built for every request.

Logging Common Pitfalls

  • Nothing appears. No provider is registered, or the factory was assigned after a Brighter type had already initialised its static logger. Check the order.

  • Logs stop when you move off DI. The DI extensions were doing the assignment for you; the manual path does not.

  • Setting ApplicationLogging.LoggerFactory in a test affects the whole process. It is static and shared, so set it once in fixture setup rather than per test.

  • Logging is not tracing. For distributed traces across message boundaries, use Telemetry; Brighter's logs are not a substitute for spans.

Further Reading

Last updated

Was this helpful?