KoKo
KoKo

Reputation: 451

Mastransit with Serilog

MassTransit Version: 7.2.2 | Serilog version :2.10.0 | Serilog.Asp.net core version 4.1.0 | Asp.net core 5.0.
I set the follow config in my application, prior to configuring the bus.

LogContext.ConfigureCurrentLogContext(loggerFactory);

although, according by this document I don't need to set this config.

If you are using the new .AddMassTransit() configuration, combined with .AddBus(), then ILoggerFactory is automatically configured for you. In this case, the statement above is not required.

now, my Serilog configs:

"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} - {ApplicationName} - {SourceContext} - {Level:u3} - {CorrelationId} - {UserId} - {ClientVersion} => {Message:lj}{NewLine}{Exception}",

with this template in my Http services, I see the logs, for example:

2021-12-29 12:35:36.096 - xxxx- Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - INF - fcf97692-7af4-492c-9d1b-cf7456d1d371(corrId)- 11111(userId)- 1.2.1(ClientVer) , etc

but when logging in the consumers, didn't log my Serilog properties(CorrelationId, UserId, ClientVersion, etc).

2021-12-29 12:35:36.096 - xxxx- Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker - INF - - - , etc

Did I need other configs for resolve this issues?
I want also push my Serilog properties when throws exception into consumers. at this time it isn't log. as you can see:

2022-01-01 13:07:24.736 - xxx - MassTransit.ReceiveTransport - ERR - - - => R-FAULT "rabbitmq://localhost/xxx" "c8100000-568d-0050-fe74-08d9cd0a4d36" xxxx


the sample:
I pushed my properties in the MassTransit publish filter:

public class IntegrationEventPublishFilter<T> : IFilter<PublishContext<T>> where T : class
{
    private readonly ICurrentRequest _currentRequest;
    private readonly ICurrentUser _currentUser;


    private readonly ILogger _logger;

    public IntegrationEventPublishFilter(ICurrentRequest currentRequest, ILogger<IntegrationEventPublishFilter<T>> logger,
        ICurrentUser currentUser)
    {
        _currentRequest = currentRequest;
        _logger = logger;
        _currentUser = currentUser;
    }


    public Task Send(PublishContext<T> context, IPipe<PublishContext<T>> next)
    {

        LogContext.PushProperty("UserId", _currentUser.UserId);
        LogContext.PushProperty("ClientVersion", _currentRequest.ClientVersion);
        LogContext.PushProperty("CorrelationId", _currentRequest.ReuqestId);
        return next.Send(context);
    }

    public void Probe(ProbeContext context)
    {
    }
}

Serilog Setting Json file:

{  "Serilog": {
"Using": [
  "Serilog.Sinks.Async"
],
"MinimumLevel": {
  "Default": "Information",
},
"Properties": {
  "ApplicationName": "xxx
},
"WriteTo": [
  {
    "Name": "Async",
    "Args": {
      "configure": [
        {
          "Name": "File",
          "Args": {
            "path": "..\\..\\Logs\\log.log",
            "rollingInterval": "Hour",
            "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} - {ApplicationName} - {SourceContext} - {Level:u3} - {CorrelationId} - {UserId} - {ClientVersion} => {Message:lj}{NewLine}{Exception}",
          }
        }

      ]
    }
  },
  {
    "Name": "Debug"
  }
]  }}

program file:

   public static IHostBuilder CreateHostBuilder(string[] args)
    {
        IConfigurationRoot configurationRoot = null;

        return Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((HostBuilderContext context, IConfigurationBuilder builder) =>
            {
                var environment = context.HostingEnvironment;
                var environmentName = environment.IsProduction() ? "" : $".{environment.EnvironmentName}";

                builder.Sources.Clear();
                builder.SetBasePath(Environment.CurrentDirectory)
                    .AddJsonFile($"appsettings{environmentName}.json")
                    .AddJsonFile($"serilogsettings{environmentName}.json")
                    
                configurationRoot = builder.Build();
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>()
                    .UseSerilog((hostingContext, loggerConfiguration) =>
                    {
                        loggerConfiguration
                             .Enrich.FromLogContext()
                             .Enrich.With<BaseExceptionItemsEnricher>()
                             .ReadFrom.Configuration(configurationRoot, "Serilog")
                    });
            });
    }

Serilog enrichment filter(that registered in the startup and correctly worked in the http requests.):

public class LogEnrichmentFilter : IActionFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public LogEnrichmentFilter(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        httpContext.Request.Headers.TryGetValue("Request-Id", out StringValues requestIds);
        var userId = httpContext.User.Claims.FirstOrDefault(x => x.Type == "UserId")?.Value;
        httpContext.Request.Headers.TryGetValue("X-Client-Version", out StringValues clientVersions);

        LogContext.PushProperty("UserId", userId);
        LogContext.PushProperty("CorrelationId", requestIds.FirstOrDefault());
        LogContext.PushProperty("ClientVersion", clientVersions.FirstOrDefault());
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
}

}

my consumer:

public class MyConsumer : 
   IConsumer<myEvent>
{

    private readonly ILogger _logger;

    public MyConsumer (
        ILogger<MyConsumer> logger)
    {
      
        _logger = logger;
    }

    public async Task Consume(ConsumeContext<MyEvent> context)
    {
        

        _logger.LogInformation("LOGGING TEST"); } } }
         

result of logging :

2021-12-29 12:35:38.183 - xxx - xxx.MyConsumer - INF -  -  -  => LOGGING TEST

As you can see, don't log my pushed properties(correlationId, userId, etc).

Upvotes: 1

Views: 1175

Answers (1)

KoKo
KoKo

Reputation: 451

finally, I solved this problem. I added two filter: PublishContext filter and ConsumeContext filter.

in the PublishContext filter, I set Serilog Properties into MT headers:

 public class IntegrationEventPublishFilter<T> : IFilter<PublishContext<T>> where T : class
{
    private readonly ICurrentRequest _currentRequest;
    private readonly ICurrentUser _currentUser;

    public IntegrationEventPublishFilter(ICurrentRequest currentRequest, ICurrentUser currentUser)
    {
        _currentRequest = currentRequest;
        _currentUser = currentUser;
    }

    public Task Send(PublishContext<T> context, IPipe<PublishContext<T>> next)
    {

        context.Headers.Set("Language", _currentRequest.Language);
        context.Headers.Set("UId", _currentUser.UserId);
        context.Headers.Set("CorrelationId", _currentRequest.ReuqestId);
        context.Headers.Set("ClientVersion", _currentRequest.ClientVersion);

        return next.Send(context);
    }

    public void Probe(ProbeContext context)
    {
    }
}

and, in the ConsumeContext filter I got MT headers and pushed to Serilog LogContext:

    public class IntegrationEventConsumeFilter<T> : IFilter<ConsumeContext<T>> where T : class
{
    public Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
    {

        var acceptLanguage = context.Headers.Get<string>("Language");
        CultureInfo.CurrentUICulture = string.IsNullOrWhiteSpace(acceptLanguage) ?
            new CultureInfo("fa-IR") : new CultureInfo(acceptLanguage);


        context.Headers.TryGetHeader("UId", out object userId);
        context.Headers.TryGetHeader("ClientVersion", out object clientVersion);
        context.Headers.TryGetHeader("CorrelationId", out object correlationId);

        LogContext.PushProperty("UId", userId);
        LogContext.PushProperty("ClientVersion", clientVersion);
        LogContext.PushProperty("CorrelationId", correlationId);

        return next.Send(context);
    }

    public void Probe(ProbeContext context)
    {
    }
}

}

Upvotes: 0

Related Questions