How To Start Logging With Serilog
New 2.0.14
Info
This document applies to Nodinite SerilLog sinks versions >= 2.0.14. and assumes you are using the isolated worker model working with Azure functions using .NET 8 or later.
If you are interested in logging using the Microsoft.Extensions.Logging.ILogger, please navigate to here.
There are different settings examples available (appsettings.json); the rest of the code examples below are the same regardless of the way of working you opt for.
- Required NuGet packages
- Dependencies in Project (.csproj)
- Startup file (Program.cs)
- Log (Function.cs)
- appsettings.json
Info
The Settings section is documented on a shared page, read more here
Required NuGet packages
To enable SeriLog Logging, You must add some additional NuGet packages to your project. You can do this using the NuGet Package Manager in Visual Studio or by editing the .csproj file directly.:
- Nodinite.Serilog.AzureBlobStorageSink (if you are using the Blob sink, choose any other Nodinite sink you like)
- Serilog.Settings.Configuration
Dependencies in Project (.csproj)
The version should be properly updated when you update the NuGet packages. All versions below are only an example. Make sure to timely update to the latest.
<ItemGroup>
...
<PackageReference Include="Nodinite.Serilog.AzureBlobStorageSink" Version="2.0.27" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.2" />
</ItemGroup>
Startup file (Program.cs)
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Core;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices((context, services) =>
{
// Create configuration object and read settings
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
// Create the logger object
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
// Register the logger object as a singleton using dependency injection to achieve great performance when you write multiple logs
services.AddSingleton<Serilog.ILogger, Serilog.Core.Logger>(sp => logger);
})
.Build();
host.Run();
Log (Function.cs)
Inject the Logger inside the constructor instead on the method level.
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Serilog.Core.Enrichers;
namespace Nodinite.Demo.Azure.Functions
{
public class Function1
{
private readonly Serilog.ILogger _logger;
public Function1(Serilog.ILogger logger)
{
_logger = logger;
}
[Function("Function1")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
{
#region Nodinite Serilog sample code (should be refactored into a reusable component injected using dependency injection; all part of your documented asynchronous Logging Strategy)
string payload = await new StreamReader(req.Body).ReadToEndAsync();
string correlationId = Guid.NewGuid().ToString();
if (req.Headers.TryGetValue("x-ms-client-tracking-id", out Microsoft.Extensions.Primitives.StringValues id))
{
correlationId = id[0];
}
_logger.ForContext(new PropertyEnricher("ApplicationInterchangeId", $"{correlationId}"))
.Enrich.WithProperty("Color", "Yellow")
.ForContext(new PropertyEnricher("Body", JsonConvert.SerializeObject(payload)))
.ForContext(new PropertyEnricher("OriginalMessageType", "OrderMessage#1.0"))
.Information("Hello from Function");
#endregion
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
response.WriteString("Welcome to Azure Functions!");
return response;
}
}
}
appsettings.json
Depending on wich type of sink you are using, you must use an appropriate configuration using any of the following
- How To Start Logging With Serilog
Blob
Blob (Using the default Managed Identity)
{
"Serilog": {
"Using": ["Nodinite.Serilog.AzureBlobStorageSink"],
"WriteTo": [
{
"Name": "AzureBlobStorageSink",
"Args": {
"StorageAccountName": "acme",
"ContainerName": "logevents",
"Settings": {
...
}
}
}
]
}
}
Blob (Using an explicit Managed Identity)
New 2.0.16
{
"Serilog": {
"Using": ["Nodinite.Serilog.AzureBlobStorageSink"],
"WriteTo": [
{
"Name": "AzureBlobStorageSink",
"Args": {
"StorageAccountName": "acme",
"ContainerName": "logevents",
"ManagedIdentityClientId": "00000000-0000-0000-0000-000000000000",
"Settings": {
...
}
}
}
]
}
}
Blob (Using a Connection string)
Copy the ConnectionString from the Azure Portal (Access keys) for the Storage Account in use.
{
"Serilog": {
"Using": [ "Nodinite.Serilog.AzureBlobStorageSink" ],
"WriteTo": [
{
"Name": "NodiniteAzureBlobStorageSinkWithConnectionString",
"Args": {
"ConnectionString": "DefaultEndpointsProtocol=https;AccountName=acme;AccountKey=xyz;EndpointSuffix=core.windows.net",
"ContainerName": "logevents",
"Settings": {
...
}
}
}
]
}
}
Event Hub
Event Hub (Using Managed Identity)
{
"Serilog": {
"Using": ["Nodinite.Serilog.EventHubSink"],
"WriteTo": [
{
"Name": "NodiniteEventHubSink",
"Args": {
"eventHubNameSpace": "acme.servicebus.windows.net",
"eventHubName": "eventhub-01",
"Settings": {
...
}
}
}
]
}
}
Event Hub (Using a Connection string)
Use a dedicated Shared access policy to create a connectionstring including the EntityPath=%name%
.
{
"Serilog": {
"Using": [ "Nodinite.Serilog.EventHubSink" ],
"WriteTo": [
{
"Name": "NodiniteEventHubSink",
"Args": {
"ConnectionString": "Endpoint=sb://xyz.servicebus.windows.net/;SharedAccessKeyName=Default;SharedAccessKey=secretstuff;EntityPath=yourentityname
",
"Settings": {
...
}
}
}
]
}
}
File
{
"Serilog": {
"Using": [ "Nodinite.Serilog.FileSink" ],
"WriteTo": [
{
"Name": "NodiniteFileSink",
"Args": {
"Folder": "\\FancyUNCSrv01\\DaShare\\In",
"Settings": {
...
"FileExtension": ".json"
}
}
}
]
}
}
Log API
{
"Serilog": {
"Using": [ "<placeholder>" ],
"WriteTo": [
{
"Name": "NodiniteApiSink",
"Args": {
"NodiniteApiUrl": "https://fancysrv01/nodinite/dev/logapi/",
"Settings": {
...
}
}
}
]
}
}
SeriLog
SeriLog (Using the default Managed Identity)
{
"Serilog": {
"Using": ["Nodinite.Serilog.ServiceBusSink"],
"WriteTo": [
{
"Name": "NodiniteServiceBusSink",
"Args": {
"NamespaceName": "acme.servicebus.windows.net",
"QueueName": "sbq01",
"Settings": {
...
}
}
}
]
}
}
SeriLog (Using an explicit Managed Identity)
New 2.0.27
{
"Serilog": {
"Using": ["Nodinite.Serilog.ServiceBusSink"],
"WriteTo": [
{
"Name": "NodiniteServiceBusSink",
"Args": {
"NamespaceName": "acme.servicebus.windows.net",
"QueueName": "sbq01",
"ManagedIdentityClientId": "00000000-0000-0000-0000-000000000000",
"Settings": {
...
}
}
}
]
}
}
SeriLog (Using a Connection string)
Copy the ConnectionString from the Azure Portal (Access keys) for the Storage Account in use.
{
"Serilog": {
"Using": [ "Nodinite.Serilog.ServiceBusSink" ],
"WriteTo": [
{
"Name": "NodiniteServiceBusSinkWithConnectionString",
"Args": {
"ConnectionString": "DefaultEndpointsProtocol=https;AccountName=acme;AccountKey=xyz;EndpointSuffix=core.windows.net",
"QueueName": "sqb01",
"Settings": {
...
}
}
}
]
}
}
Advanced settings using multiple sinks
In this example multiple sinks are used. The example uses the Azure Blob Storage Sink and the Service Bus Sink. The settings for each sink are specified in the Settings section of the configuration file.
{
"Serilog": {
"Using": [ "Nodinite.Serilog.AzureBlobStorageSink", "Nodinite.Serilog.ServiceBusSink" ],
"WriteTo": [
{
"Name": "AzureBlobStorageSink",
"Args": {
"StorageAccountName": "acme",
"ContainerName": "logevents",
"Settings": {
...
}
}
},
{
"Name": "NodiniteServiceBusSink",
"Args": {
"NamespaceName": "acme.servicebus.windows.net",
"QueueName": "sbq01",
"Settings": {
...
}
}
},
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
]
}
}
Next Step
PickupService
Log Events
JSON Log Event