Windows服务中如何初始化Serilog?

How to initialize Serilog in Windows Service?

我有以下 issue.I 希望能够在 windows 服务中使用 Serilog 但我不知道应该在哪里初始化它:

目前我在 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();
}

服务

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

因此,为了在运行服务集合的 Main 和目标服务内部都使用 Serilog,应该如何完成?

一个常见的做法是做你正在做的事情 - 即在一开始就初始化日志记录并将记录器存储在 Log.Logger - 然后,在你的服务中,获取上下文记录器, Log.ForContext<T>。例如

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.");
        }
    }
}

这使得访问 Serilog 的记录器变得非常简单,并且还有一个额外的好处,就是让您 contextual information 了解日志消息的来源,这很有用。

您可能会对阅读本文感兴趣:Context and correlation – structured logging concepts in .NET (5)


此外,不要忘记在您的服务停止之前调用 Log.CloseAndFlush(),以确保所有缓冲的消息都写入接收器。

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(); // <<<<<<<<<<<<<<<<<<<
        }
    }
}

您可以在文档中阅读更多相关信息:Lifecycle of Loggers

另一种方法是在 Windows 服务的构造函数中注入一个 ILogger 实例,它是从 Main 方法传递的。

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);
    }
}

并且在您的 Windows 服务中,您添加一个采用 ILogger 实例的构造函数。您仍然需要保留一个不接受任何参数的构造函数,以便您仍然可以在 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.");
    }
}