To elaborate on the comment from @pinkfloydx33: you can do it via
_logger.BeginScope( < your state here > )
{
// All log methods here include state, regardless
// of which ILogger object is used.
}
or by using
System.Diagnostics.Activity.Current.AddBaggage()
This works without additional configuration (e.g. scopes are already enabled by default on AI).
For example, here's a middleware class to log tenant information that shows both methods:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyApp
{
public static class StoreTenantForLoggingMiddlewareExtensions
{
/// <summary>
/// Register StoreTenantForLoggingMiddleware as middleware.
/// Call this from Configure() in Startup, as:
/// app.UseStoreTenantForLogging()
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IApplicationBuilder UseStoreTenantForLogging(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<StoreTenantForLoggingMiddleware>();
}
}
/// <summary>
/// Middleware to log the Tenant's domain to Application
/// Insights as a customDimension
/// </summary>
public class StoreTenantForLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<StoreTenantForLoggingMiddleware> _logger;
public StoreTenantForLoggingMiddleware(RequestDelegate next,
ILogger<StoreTenantForLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
// Here TenantContext is my own class that gets the state
// I want to be logged. You'd replace with your own object
// or just call a method on httpContext.
public async Task InvokeAsync(HttpContext httpContext, TenantContext tenantContext)
{
// Example 1: Add data to current activity. AI will pick this
// up and add as a customDimension in traces logs.
var currentActivity = System.Diagnostics.Activity.Current;
if (currentActivity != null)
{
currentActivity.AddBaggage("TenantDomain1", tenantContext?.Domain);
}
// Example 2: Use a scope.
// If you go with option 1, remove this 'using' but still
// call await _next(httpContext);
using ( var scope = _logger.BeginScope(new Dictionary<string, object>()
{ { "TenantDomain2", tenantContext?.Domain } }))
{
await _next(httpContext);
}
}
}
}
I'm not sure which is best. The Activity
one appeals slightly more to me, plus I'm guessing the data might persist a bit later in the pipeline.
For bonus points if you use nlog and want to be able to log the property there you can add this line at the start of Invoke()
above and then use ${mdlc:item=TenantDomain}
within your nlog.config
file.
NLog.MappedDiagnosticsLogicalContext.Set("TenantDomain", tenantContext?.Domain);
You can probably use https://github.com/NLog/NLog.DiagnosticSource as an alternative but I've not tried.
Activity.Current.AddBaggage()
worked butAddTag()
did not. – Zettazeugma