Search code examples
c#asp.net-coreserilogopentracingjaeger

OpenTracing doesn't send logs with Serilog


I'm trying to use OpenTracing.Contrib.NetCore with Serilog. I need to send to Jaeger my custom logs. Now, it works only when I use default logger factory Microsoft.Extensions.Logging.ILoggerFactory

My Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddSingleton<ITracer>(sp =>
    {
        var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
        string serviceName = sp.GetRequiredService<IHostingEnvironment>().ApplicationName;

        var samplerConfiguration = new Configuration.SamplerConfiguration(loggerFactory)
            .WithType(ConstSampler.Type)
            .WithParam(1);

        var senderConfiguration = new Configuration.SenderConfiguration(loggerFactory)
            .WithAgentHost("localhost")
            .WithAgentPort(6831);

        var reporterConfiguration = new Configuration.ReporterConfiguration(loggerFactory)
            .WithLogSpans(true)
            .WithSender(senderConfiguration);

        var tracer = (Tracer)new Configuration(serviceName, loggerFactory)
            .WithSampler(samplerConfiguration)
            .WithReporter(reporterConfiguration)
            .GetTracer();

        //GlobalTracer.Register(tracer);
        return tracer;
    });
    services.AddOpenTracing();
}

and somewhere in controller:

[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
    private readonly ILogger<ValuesController> _logger;

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

    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        _logger.LogWarning("Get values by id: {valueId}", id);
        return "value";
    }
}

in a result I will able to see that log in Jaeger UI enter image description here

But when I use Serilog, there are no any custom logs. I've added UseSerilog() to WebHostBuilder, and all custom logs I can see in console but not in Jaeger. There is open issue in github. Could you please suggest how I can use Serilog with OpenTracing?


Solution

  • This is a limitation in the Serilog logger factory implementation; in particular, Serilog currently ignores added providers and assumes that Serilog Sinks will replace them instead.

    So, the solutions is implementaion a simple WriteTo.OpenTracing() method to connect Serilog directly to OpenTracing

    public class OpenTracingSink : ILogEventSink
    {
        private readonly ITracer _tracer;
        private readonly IFormatProvider _formatProvider;
    
        public OpenTracingSink(ITracer tracer, IFormatProvider formatProvider)
        {
            _tracer = tracer;
            _formatProvider = formatProvider;
        }
    
        public void Emit(LogEvent logEvent)
        {
            ISpan span = _tracer.ActiveSpan;
    
            if (span == null)
            {
                // Creating a new span for a log message seems brutal so we ignore messages if we can't attach it to an active span.
                return;
            }
    
            var fields = new Dictionary<string, object>
            {
                { "component", logEvent.Properties["SourceContext"] },
                { "level", logEvent.Level.ToString() }
            };
    
            fields[LogFields.Event] = "log";
    
            try
            {
                fields[LogFields.Message] = logEvent.RenderMessage(_formatProvider);
                fields["message.template"] = logEvent.MessageTemplate.Text;
    
                if (logEvent.Exception != null)
                {
                    fields[LogFields.ErrorKind] = logEvent.Exception.GetType().FullName;
                    fields[LogFields.ErrorObject] = logEvent.Exception;
                }
    
                if (logEvent.Properties != null)
                {
                    foreach (var property in logEvent.Properties)
                    {
                        fields[property.Key] = property.Value;
                    }
                }
            }
            catch (Exception logException)
            {
                fields["mbv.common.logging.error"] = logException.ToString();
            }
    
            span.Log(fields);
        }
    }
    
    public static class OpenTracingSinkExtensions
    {
        public static LoggerConfiguration OpenTracing(
                  this LoggerSinkConfiguration loggerConfiguration,
                  IFormatProvider formatProvider = null)
        {
            return loggerConfiguration.Sink(new OpenTracingSink(GlobalTracer.Instance, formatProvider));
        }
    }