Using serilog in a class library referenced by other projects

Viewed 2805

I have a solution containing multiple .NET Core API and windows services. How can I integrate Serilog in such a way that I will not be required to make changes at several different places for adding a column or changing some property?

I'm thinking of adding Serilog in a common library and use that custom library in all other projects however how to invoke starting point of the serilog as we see in below code in Program.cs, any code reference will help.

 .UseSerilog((hostingContext, loggerConfiguration) =>
                {
                    loggerConfiguration.MinimumLevel.Debug()
                            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                            .Enrich.FromLogContext()
                            .WriteTo.File(path: Path.Combine(Environment.CurrentDirectory, "Logs", "log.txt"),
                                rollOnFileSizeLimit: true,
                                retainedFileCountLimit: 20,
                                rollingInterval: RollingInterval.Day,
                                fileSizeLimitBytes: 10000
                                )
                            .WriteTo.Console();
                })

================ Updated ===================

For windows service, I have the code in common library.
public class LogManager : ILogManager
    {
       public LogManager()
            {
                Log.Logger = new LoggerConfiguration()
                             .MinimumLevel.Verbose()
                             .Enrich.FromLogContext()
                             //  .WriteTo.Console(LogEventLevel.Debug, OutputTemplate, theme: AnsiConsoleTheme.Code)
                             .WriteTo.RollingFile(@"C:\Users\hnq6ww\Documents\Important\logs.txt",
                                                        LogEventLevel.Verbose,
                                                        // OutputTemplate,
                                                        retainedFileCountLimit: (int?)RollingInterval.Day,
                                                        buffered: true, fileSizeLimitBytes: 10000)
                             .CreateLogger();
            }
}

In Program.cs

  static void Main(string[] args)
        {
            ILogManager log = new LogManager();
            log.WriteLog(Serilog.Events.LogEventLevel.Information, "Testing");
        }
3 Answers

In your common library you can create an extension method that will inject Serilog into your multiple .NET Web API projects

So you can have something like this in your common class lib

public static class SerilogDi
    {
        public static IHostBuilder InjectSerilog(this IHostBuilder hostBuilder)
        {
            hostBuilder.UseSerilog((hostingContext, loggerConfiguration) =>
            {
                loggerConfiguration.MinimumLevel.Debug()
                        .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                        .Enrich.FromLogContext()
                        .WriteTo.File(path: Path.Combine(Environment.CurrentDirectory, "Logs", "log.txt"),
                            rollOnFileSizeLimit: true,
                            retainedFileCountLimit: 20,
                            rollingInterval: RollingInterval.Day,
                            fileSizeLimitBytes: 10000
                            )
                        .WriteTo.Console();
            });

            return hostBuilder;
        }
    }

and then you can add project reference of this project into your .NET Web API projects and inject Serilog in them, something like this

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .InjectSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

Also, you can make InjectSerilog() parameterized to further configure it on per project basis.

UPDATE Ok according to your update I think you only want to configuration to be common. So what you can do is create a static method in your common library which return a configured logger.

public static class MySerilog
    {
        public static Logger GetInstance()
        {
            return new LoggerConfiguration()
                             .MinimumLevel.Verbose()
                             .Enrich.FromLogContext()
                             //  All my settings here
                             .CreateLogger();
        }
    }

and then in your other projects you can use this instance

public static int Main(string[] args)
{
    Log.Logger = MySerilog.GetInstance();
}

And in your CreateHostBuilder method you can simply do

Host.CreateDefaultBuilder(args)
                .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

This will work for both types of your project and you can make GetInstance() parameterized as well!

You can further refer here https://github.com/serilog/serilog-aspnetcore

If you want to move logging logic into separate library, then you need to do the following steps.

The first step is to create logic of logging in your class library:

public interface ILoggerService
{
    void Info(Serilog.Events.LogEventLevel.Information info, Message message );
}

public class LoggerService : ILoggerService
{   
    public void Write(LogEventLevel level, Message message) =>
        Serilog.Log.Write(serilogEvents[level], JsonConvert.SerializeObject(message));
        
}

Then you need to create an extension method in your class library to register your service in other projects:

public static class LoggingServiceExtensions
{
    public static IServiceCollection RegisterMyLogger(this IServiceCollection services, 
        IConfiguration configuration)
    {
        Log.Logger = new LoggerConfiguration().ReadFrom
            .Configuration(configuration).CreateLogger();

        services.AddSingleton<ILoggerService, LoggerService>();

        return services;
    }
}

If your are going to use this library in ASP.NET Core MVC or Web API, then just register your library:

public void ConfigureServices(IServiceCollection services)
{
    services.ConfigBaseServiceSettings<AppSettings>(Configuration);
    services.RegisterMyLogger(Configuration);
}

Do not forget to install the following dependencies through NuGet in your class library:

Serilog 
Serilog.Settings.Configuration

And other libraries if you want:

Serilog.Sinks.Console
Serilog.Sinks.Http
Serilog.Sinks.MSSqlServer

I have just a usabillity recommendation based on the answer from honey_ramgarhia

I wraped the serilog usage in a common helper library. This way only the main program (Service, Webapp, whatever) and the common library needs to reference the serilog nuget package.

public static class LogHelper
{
    public static Logger GetInstance()
    {
        return new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .Enrich.FromLogContext()
                .WriteTo.File(GetLogFilePath())
                .CreateLogger();
    }

    public static void LogText(string text)
    {
        Log.Information(text);
    }

    public static void LogException(Exception ex)
    {
        Log.Fatal(ex.Message + ";" + ex.InnerException + "Stacktrace:" + ex.StackTrace, Encoding.Default);
    }

    public static void End()
    {
        Log.CloseAndFlush();
    }    

}
Related