We are using Serilog along with Seq for logging against one of our Asp.Net Core applications and wanted to be able to stand up the Seq docker image automatically when launching the application, including downloading the docker image if it doesn’t already exist.
To demonstrate how to do this I will bootstrap a new Asp.Net Core web application and configure Serilog and a Seq sink. If you wish to add Serilog to your existing app, you can follow the instructions found here https://nblumhardt.com/2019/10/serilog-in-aspnetcore-3 ) then just patch in the necessary code to your Program.cs
You can also find a minimal sample application here
Create a new empty web application and install the Serilog and Seq packages by running the following commands from your terminal
dotnet new web -o AutoLaunchSeq
cd AutoLaunchSeq
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Seq
dotnet build
If everything builds ok open your project and navigate to the Program.cs file
Replace the contents of this file with the following
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
namespace AutoLaunchSeq
{
public class Program
{
public static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq("http://localhost:5341")
.CreateLogger();
await StartSeq();
try
{
Log.Information("Starting up");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application start-up failed");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
private static async Task StartSeq() =>
await RunStartupProcess(Environment.CurrentDirectory, "docker", "run --rm -it -e ACCEPT_EULA=Y -p 5341:80 datalust/seq");
public static async Task ConsumeAsync(StreamReader reader, StringBuilder lines)
{
await Task.Yield();
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
lines.AppendLine(line);
Console.WriteLine(line);
}
}
public static async Task<bool> RunStartupProcess(string workingDirectory, string command, string args)
{
var psi = new ProcessStartInfo(command, args)
{
WorkingDirectory = workingDirectory,
UseShellExecute = false,
CreateNoWindow = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var p = new Process();
try
{
p.StartInfo = psi;
p.Start();
var output = new StringBuilder();
var errors = new StringBuilder();
var outputTask = ConsumeAsync(p.StandardOutput, output);
var errorTask = ConsumeAsync(p.StandardError, errors);
return true;
}
finally
{
p.Dispose();
}
}
}
}
To summarise the key things happening in the code above, we first configure Serilog and add logging to Seq under the url http://localhost5341
.WriteTo.Seq("http://localhost:5341")
Register Serilog with Asp.Net Core
.UseSerilog()
We then call the ‘StartSeq’ method that invokes the docker command to download then run the Seq docker container under the specified port.
await StartSeq();
The ‘StartSeq’ method then invokes a ‘RunStartupProcess’ method with the docker arguments which uses ProcessStartInfo to run the docker process. We have one additional helper method ‘ConsumeAsync’ to write the output from the docker process back to our terminal
private static async Task StartSeq() =>
await RunStartupProcess(Environment.CurrentDirectory, "docker", "run --rm -it -e ACCEPT_EULA=Y -p 5341:80 datalust/seq");
public static async Task ConsumeAsync(StreamReader reader, StringBuilder lines)
{
await Task.Yield();
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
lines.AppendLine(line);
Console.WriteLine(line);
}
}
public static async Task<bool> RunStartupProcess(string workingDirectory, string command, string args)
{
var psi = new ProcessStartInfo(command, args)
{
WorkingDirectory = workingDirectory,
UseShellExecute = false,
CreateNoWindow = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var p = new Process();
try
{
p.StartInfo = psi;
p.Start();
var output = new StringBuilder();
var errors = new StringBuilder();
var outputTask = ConsumeAsync(p.StandardOutput, output);
var errorTask = ConsumeAsync(p.StandardError, errors);
return true;
}
finally
{
p.Dispose();
}
}
Next open Startup.cs and add the Serilog using statement to the top then find the following code
await context.Response.WriteAsync("Hello World!");
Wrap this with some logging, for example
Log.Information("Beginning Request: Hello World!");
await context.Response.WriteAsync("Hello World!");
Log.Information("Completed Request: Hello World!");
We are now ready to try out app and see if Seq stands up correctly in docker and starts capturing logs.
Return to your terminal and run the following
dotnet run
If you havent already Seq downloaded as a docker container, this may take a few minutes the first time it runs. Once completed and running, you should be able to access your application under the default port https://localhost:5001
You can also check if Seq has launched correctly by checking your running docker containers
docker container ls
You should hopefully see Seq is running and when you hit your application endpoint in the browser you should see ‘Hello World!’
You can now access Seq in your browser via the following url http://localhost:5341 and if everything has worked correctly you should see your application logs