Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

serilog format SourceContext for showing only assembly name

I configured my project to use Serilog for logging using dependecy injection. I use the following schema in the classes constructor:

namespace FlickPopper.API.Controllers {

    public class ValuesController : Controller {
        private ILogger<ValuesController> _logger;
        public MyClass(ILogger<ValuesController> logger) {
           _logger = logger;
        }
    }
}

In this way, serilog creates the logger calling to Log.ForContext<classname>

My settings are:

  "Serilog": {
      "WriteTo": [
        {
          "Name": "RollingFile",
          "Args": {
            "pathFormat": "Logs\\log-{Date}.txt",
            "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}"
          }
        }
      ],
      "Enrich": [ "FromLogContext" ]
    }

So, the logs seems like this:

2018-01-26 22:20:08 [INF] [FlickPopper.API.Controllers.ValuesController] Get all values

It's any way to format the SourceContext property to show only the assembly name in the logs, somthing like this?

2018-01-26 22:20:08 [INF] [FlickPopper.API] Get all values
like image 397
mnieto Avatar asked Jan 26 '18 21:01

mnieto


1 Answers

When you use injected ILogger<T>, under the hood Serilog logger is created by SerilogLoggerProvider that implements ILoggerProvider interface. This interface has the only method:

public interface ILoggerProvider : IDisposable
{
    ILogger CreateLogger(string categoryName);
}

The passed categoryName is used as value for {SourceContext} property in message format. And ASP.NET Core passes it as fully-qualified name (e.g. FlickPopper.API.Controllers.ValuesController).

So this string value should be fixed not in Serilog code or configuration, but in ASP.NET Core logging infrastructure.

The responsible class for creation of that value in first place is Logger<T> class. Instances of Logger<T> are instantiated when you inject ILogger<T> into your classes. Here is source code of its constructor:

public class Logger<T> : ILogger<T>
{
    public Logger(ILoggerFactory factory)
    {
        if (factory == null)
        {
            throw new ArgumentNullException(nameof(factory));
        }

        _logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T)));
    }

    //  ...
}

That call to TypeNameHelper.GetTypeDisplayName(typeof(T)) returns fully-qualified name that then is passed to ILoggerFactory and eventually to SerilogLoggerProvider.

So if you want to change that behavior and adjust the categoryName passed to ILoggerFactory, you should have your own implementation of ILogger<T> that makes required call to ILoggerFactory.CreateLogger(). It's not that difficult, because Logger<T> class is very thin and is based on Microsoft.Extensions.Logging.Logger implementation. Here is the class that is a copy of Logger<T> except for one line producing logger category name:

public class LoggerEx<T> : ILogger<T>
{
    private readonly ILogger _logger;

    public LoggerEx(ILoggerFactory factory)
    {
        if (factory == null)
        {
            throw new ArgumentNullException(nameof(factory));
        }

        _logger = factory.CreateLogger(typeof(T).Assembly.GetName().Name);
    }

    IDisposable ILogger.BeginScope<TState>(TState state)
    {
        return _logger.BeginScope(state);
    }

    bool ILogger.IsEnabled(LogLevel logLevel)
    {
        return _logger.IsEnabled(logLevel);
    }

    void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        _logger.Log(logLevel, eventId, state, exception, formatter);
    }
}

You should also replace the standard implementation of ILogger<T> with this one in your services registration:

services.AddSingleton(typeof(ILogger<>), typeof(LoggerEx<>));

Now the instances of LoggerEx<T> will be injected to the controllers and {SourceContext} will have a value you built:

2018-01-27 09:54:21 [INF] [TestProject.TestApplication] Hello!

like image 136
CodeFuller Avatar answered Nov 20 '22 19:11

CodeFuller