Appearance
Logging, Diagnostics, and Metrics
Wolverine logs through the standard .NET ILogger
abstraction, and there's nothing special you need to do to enable that logging other than using one of the standard approaches for bootstrapping a .NET application using IHostBuilder
. Wolverine is logging all messages sent, received, and executed inline.
Log Message Execution Start
Wolverine is absolutely meant for "grown up development," so there's a few options for logging and instrumentation. While Open Telemetry logging is built in and will always give you the activity span for message execution start and finish, you may want the start of each message execution to be logged as well. Rather than force your development teams to write repetitive logging statements for every single message handler method, you can ask Wolverine to do that for you:
cs
using var host = await Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
// Opt into having Wolverine add a log message at the beginning
// of the message execution
opts.Policies.LogMessageStarting(LogLevel.Information);
}).StartAsync();
With only the defaults, Wolverine is logging the type of message and the message id. As shown in the next section, you can also add additional context to these log messages.
In conjunction with the "audited members" that are added to these logging statements, all the logging in Wolverine is using structural logging for better searching within your logs.
Contextual Logging with Audited Members
WARNING
Be cognizant of the information you're writing to log files or Open Telemetry data and whether or not that data is some kind of protected data like personal data identifiers.
Wolverine gives you the ability to mark public fields or properties on message types as "audited members" that will be part of the logging messages at the beginning of message execution described in the preview section, and also in the Open Telemetry support described in the next section.
To explicitly mark members as "audited", you can use attributes within your message types (and these are inherited) like so:
cs
public class AuditedMessage
{
[Audit]
public string Name { get; set; }
[Audit("AccountIdentifier")] public int AccountId;
}
Or if you are okay using a common message interface for common identification like "this message targets an account/organization/tenant/client" like the IAccountCommand
shown below:
cs
// Marker interface
public interface IAccountMessage
{
public int AccountId { get; }
}
// A possible command that uses our marker interface above
public record DebitAccount(int AccountId, decimal Amount) : IAccountMessage;
You can specify audited members through this syntax:
cs
// opts is WolverineOptions inside of a UseWolverine() call
opts.Policies.Audit<IAccountMessage>(x => x.AccountId);
Open Telemetry
Wolverine also supports the Open Telemetry standard for distributed tracing. To enable the collection of Open Telemetry data, you need to add Wolverine as a data source as shown in this code sample:
cs
// builder.Services is an IServiceCollection object
builder.Services.AddOpenTelemetryTracing(x =>
{
x.SetResourceBuilder(ResourceBuilder
.CreateDefault()
.AddService("OtelWebApi")) // <-- sets service name
.AddJaegerExporter()
.AddAspNetCoreInstrumentation()
// This is absolutely necessary to collect the Wolverine
// open telemetry tracing information in your application
.AddSource("Wolverine");
});
Message Correlation
TODO -- Soon. This is going to be tedious
Metrics
TODO -- there's quite a bit built in that's published through System.Diagnostics.Metrics that should be available through open telemetry exports, but some more experimentation and actual docs are forthcoming.