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.");
}
}
我有以下 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.");
}
}