Bercovici Adrian
Bercovici Adrian

Reputation: 9360

How to initialize Serilog in Windows Service?

I have the following issue.I want to be able to use Serilog inside a windows service and i do not know where it should be initilized:

Currently i initialize it in my Main:

using Serilog;

public static void RunService() {
    Log.Logger = new LoggerConfiguration()
      .WriteTo.RollingFile([some path])
      .CreateLogger();

    MyService daemon = new MyService();
    Log.Information("Service initialized")

    ServiceBase[] services;
    services = new ServiceBase[] {
        service
    };
    Log.Information("Before running the service");
    ServiceBase.Run(services);
}

static void Main(string[] args) {
   RunService();
}

Service

public class MyService:ServiceBase{
   protected override void OnSessionChange(SessionChangeDescription changeDescription) {
    Log.Information("session changed");
   } 
   protected override void OnStart(string[] args) {
     Log.Information("Started service");
   }
}

So in order to use Serilog both in the Main that runs the collection of services and inside the target service how is it supposed to be done?

Upvotes: 2

Views: 7208

Answers (2)

C. Augusto Proiete
C. Augusto Proiete

Reputation: 27848

Another way to do it, is by injecting an ILogger instance in the constructor of your Windows Service, which you pass from the Main method.

static class Program
{
    static void Main()
    {
        Log.Logger = new LoggerConfiguration()
            .WriteTo.File(@"C:\SomePath\MyApp.log")
            .CreateLogger();

        var servicesToRun = new ServiceBase[]
        {
            new MyService(Log.Logger), // <<<<<<<<<<<<<<<<<<<
        };

        ServiceBase.Run(servicesToRun);
    }
}

And in your Windows Service, you add a constructor that takes an ILogger instance. You still need to keep a constructor that doesn't take any arguments, so that you still open the visual designer within Visual Studio:

public partial class MyService : ServiceBase
{
    private readonly ILogger _log;

    public MyService()
        : this(Log.Logger)
    {
        InitializeComponent();
    }

    public MyService(ILogger logger) // <<<<<<<<<<<<<<<<<<<
    {
        InitializeComponent();

        if (logger == null) throw new ArgumentNullException(nameof(logger));
        _log = logger.ForContext<MyService>();
    }

    protected override void OnStart(string[] args)
    {
        _log.Information("Service starting...");
        // ...

        _log.Information("Service started.");
    }

    protected override void OnStop()
    {
        _log.Information("Service stopping...");
        // ...

        _log.Information("Service stopped.");
    }
}

Upvotes: 0

C. Augusto Proiete
C. Augusto Proiete

Reputation: 27848

A common practice is to do what you're doing - i.e. Initialize logging at the very beginning and store the logger in Log.Logger - and then, within your service, get a hold of a context logger, Log.ForContext<T>. E.g.

using System.ServiceProcess;
using Serilog;

namespace WindowsServiceHost
{
    public partial class MyService : ServiceBase
    {
        private readonly ILogger _log = Log.ForContext<MyService>();

        public MyService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            _log.Information("Service starting...");
            // ...

            _log.Information("Service started.");
        }

        protected override void OnStop()
        {
            _log.Information("Service stopping...");
            // ...

            _log.Information("Service stopped.");
        }
    }
}

This makes it very simple to get access to Serilog's logger, and has the added benefit of giving you contextual information about where the log messages are coming from, which is useful.

You might be interested in reading this: Context and correlation – structured logging concepts in .NET (5)


Also, don't forget to call Log.CloseAndFlush() before your service stops, to make sure any buffered messages are written to the sink(s).

static class Program
{
    static void Main()
    {
        Log.Logger = new LoggerConfiguration()
            .WriteTo.File(@"C:\SomePath\MyApp.log")
            .CreateLogger();

        try
        {
            var servicesToRun = new ServiceBase[]
            {
                new MyService(),
            };

            ServiceBase.Run(servicesToRun);
        }
        catch(Exception ex)
        {
            Log.Fatal(ex, ex.Message);
            throw;
        }
        finally
        {
            Log.CloseAndFlush(); // <<<<<<<<<<<<<<<<<<<
        }
    }
}

You can read more about this in the documentation: Lifecycle of Loggers

Upvotes: 6

Related Questions