I am working on creating an API using WebAPI and use NLog to log across the stack. My API solution has two main projects, including:
- Website level that implements controllers and web files
- A service level that implements async commands and handlers in CQRS mode.
What I'm trying to achieve is to automatically create a unique identifier that I can attach to the log statements so that any logs written while serving a single request, no matter what layer they come from, can be associated with this original request. I would also like this to work without passing a unique identifier, or with the statements of the journal itself, including it in its calls.
To this end, I began to study the possibility of writing a custom delegation handler to intercept each request (after this post for guidance) and add a unique identifier as a property in NLog. I ended up with the following:
public class InitializeLoggingMessageHandler : DelegatingHandler
{
private ILogger _logger;
public InitializeLoggingMessageHandler(ILogger logger)
{
_logger = logger;
}
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var uniqueId = Guid.NewGuid().ToString();
NLog.MappedDiagnosticsContext.Set("UniqueId", uniqueId);
var requestInfo = string.Format("{0} {1}", request.Method, request.RequestUri);
var requestMessage = await request.Content.ReadAsByteArrayAsync();
_logger.Info("Request: {0} - {1}", requestInfo, Encoding.UTF8.GetString(requestMessage));
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
With this code, I can then use the unique identifier in the log layouts like this:
<target xsi:type="Debugger" name="DebugLogger"
layout="${longdate} ${logger} ${mdc:item=UniqueId} ${message}" />
The problem with this approach is that I am using the NLog MappedDiagnosticsContext to try to save a unique identifier as a property that can be used in layouts (so my logging code doesn't have to know). This is a local-local mechanism for storing values, so it is interrupted when you have asynchronous code, because the thread that starts the request may not be the one that does all this.
, , , , , , . GlobalDiagnosticsContext NLog, , WebAPI , .
, , WebAPI, , ?