Event Viewer Logs with .NET Core Workers as Windows Services
31 Jan 2020Back in the older classic windows only .NET Framework days, I would use a cool framework called TopShelf to help turn a console application during development into a running windows service in production.
Today instead I was able to install and run a windows service by modifying a .NET Core Worker project by just using .NET Core natively.
Also, I was able to add some logging to the Windows Event Viewer Application Log.
First, I created a .NET Core Worker project:
mkdir tempy && cd $_
dotnet new worker
Then I added some references:
dotnet add package Microsoft.Extensions.Hosting
dotnet add package Microsoft.Extensions.Hosting.WindowsServices
dotnet add package Microsoft.Extensions.Logging.EventLog
Next up I made changes to Program.cs, In my project I am adding a HttpClient to make external Web Requests to an API.
public static class Program
{
public static void Main(string[] args)
{
var builder = CreateHostBuilder(args);
builder.ConfigureServices(services =>
{
services.AddHttpClient();
});
var host = builder.Build();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureLogging((_, logging) => logging.AddEventLog())
.ConfigureServices((_, services) => services.AddHostedService<Worker>());
}
The key line for adding Windows Services support is :
.UseWindowsService()
Logging to Event Viewer
I also wanted to log to the Application Event Viewer log so notice the line:
.ConfigureLogging((_, logging) => logging.AddEventLog())
Now for a little gotcha, this will only log events Warning
and higher so the Worker template’s logger.LogInformation()
statements will display when debugging in the console but not when installed as a windows service.
To fix this make this change to appsettings.json, note the EventLog
section where the levels have been dialled back down to Information
level.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"EventLog": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
}
Publishing and managing the service
So with this done, I then needed to first publish, then install, start and have means to stop and uninstall the service.
I was able to manage all of this from the command line, using the SC tool (Sc.exe) that should already be installed on windows for you and be in your path already.
Publish:
cd C:\PathToSource\
dotnet publish -r win-x64 -c Release -o C:\PathToDestination
Install:
sc create "your service name" binPath=C:\PathToDestination\worker.exe
Start:
sc start "your service name"
Stop:
sc stop "your service name"
Uninstall:
sc delete "your service name"
Once I saw that all was well I was able to dial back the logging to the Event Viewer by making a change to appsettings.json, In the EventLog
section I changed the levels back up to Warning
level.
This means that anything important will indeed get logged to the Windows Event Viewer but most Information
level noise will not.
Success 🎉