Quantcast
Channel: Brian Pedersen's Sitecore and .NET Blog
Viewing all articles
Browse latest Browse all 285

.NET Core Worker Services with Application Insights and Serilog

$
0
0

The .NET Core Worker service is yet another tool in the .NET toolbox. They are perfect for background processing like reading from a queue or making health checks. They are cross-platform (of course) and they can run on docker containers, and even run on a locally hosted machine as a Windows service.

It took me quite some time to implement Application Insights and file logging (Serilog) in my worker service, so I thought I might write it down, in case I need it another time.

THE NUGET PACKAGES

First caveat was when I realized that you need a special Microsoft.ApplicationInsights.WorkerService NuGet package before Application Insights will work. Also, if you wish to run the worker service as a Windows service you need the Microsoft.Extensions.Hosting.WindowsServices package.

The packages I needed was:

Microsoft.ApplicationInsights.AspNetCore
Microsoft.ApplicationInsights.WorkerService
Microsoft.Extensions.Hosting
Microsoft.Extensions.Hosting.WindowsServices
Serilog
Serilog.AspNetCore
Serilog.Extensions.Hosting
Serilog.Extensions.Logging
Serilog.Settings.Configuration
Serilog.Sinks.Console
System.Configuration.ConfigurationManager

THE APPSETTINGS.JSON CONFIGURATION

The configuration is pretty straight forward. Only caveat is that Serilog is not configured under “Logging” but under it’s own namespace:

{
  "ApplicationInsights": {
    "InstrumentationKey": "the-appinsights-guid"
  },
  "Serilog": {
    "WriteTo": [
      {
        "Name": "File",
        "Args": {
          "path": "log_.txt",
          "rollingInterval": "Day"
        }
      }
    ]
  },
  "Logging": {
    "ApplicationInsights": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft": "Warning"
      }
    },
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Information",
      "Microsoft.Hosting.Lifetime": "Warning"
    }
  }
}

THE PROGRAM.CS

The examples online was too complex for me, so I simplified it and came up with this:

namespace MyWorkerService
{
  public class Program
  {
    public static void Main(string[] args)
    {
      // This will run the worker service
      CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args)
    {
      var host = Host.CreateDefaultBuilder(args);
      // Add this line to be able to run as a windows service
      // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-3.1&tabs=visual-studio
      host.UseWindowsService();
      // This will add the configuration to the application.
      // It will allow you to inject the configuration as 
      // IConfiguration configuration in your constructors 
      host.ConfigureAppConfiguration(
            (hostContext, config) =>
            {
              config.SetBasePath(Directory.GetCurrentDirectory());
              config.AddJsonFile("appsettings.json", false, true);
              config.AddCommandLine(args);
            }
      );
      // This will configure logging. It reads the log settings from 
      // the appsettings.json configuration file, and adds serilog,
      // allowing the application to write logs to file
      host.ConfigureLogging(
            loggingBuilder =>
            {
              var configuration = new ConfigurationBuilder()
                 .AddJsonFile("appsettings.json")
                 .Build();
              var logger = new LoggerConfiguration()
                  .ReadFrom.Configuration(configuration)
                  .CreateLogger();
              loggingBuilder.AddSerilog(logger, dispose: true);
            }
      );
      // The AddHostedServer adds the worker service to the application.
      // The AddApplicationInsightsTelemetryWorkerService is very important. Without this,
      // the Application Insights logging will not work.
      host.ConfigureServices((hostContext, services) =>
      {
        services.AddHostedService<Worker>();
        services.AddApplicationInsightsTelemetryWorkerService();
      });

      return host;
    }
  }
}

The worker itself is the “Worker” class and it looks like this:

using System.Threading;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;

namespace MyWorkerService
{
  public class Worker : BackgroundService
  {
    private readonly ILogger<Worker> _logger;
    private readonly TelemetryClient _telemetryClient;

    public Worker(ILogger<Worker> logger,
      TelemetryClient telemetryClient,
      IConfiguration configuration)
    {
      _logger = logger;
      _telemetryClient = telemetryClient;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
      while (!stoppingToken.IsCancellationRequested)
      {
        using (_telemetryClient.StartOperation<RequestTelemetry>("Execute Async"))
        {
          _logger.LogInformation("ExecuteAsync Started");
          DoWork();
        }
      }
      await Task.Delay(1000, stoppingToken);
    }
  }
}

The DoWork() is where you would do actual work.

MORE TO READ:


Viewing all articles
Browse latest Browse all 285

Trending Articles