Reputation: 9360
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
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
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