- 8 minutes to read

How To Start Logging With Serilog

New 2.0.14

Info

This document applies to Nodinite Serilog sinks versions >= 2.0.14 and assumes you use the isolated worker model with Azure Functions using .NET 8 or later.

If you want to log using the Microsoft.Extensions.Logging.ILogger, see 'How To Start Logging With Microsoft.Extensions.Logging.ILogger in Azure Functions'.

You can use different settings examples (appsettings.json); the rest of the code examples below are the same regardless of which method you choose.

Info

The Settings section is documented on a shared page. Read more at here

Required NuGet packages

To enable Serilog Logging, add the required NuGet packages to your project. You can do this using the NuGet Package Manager in Visual Studio or by editing the .csproj file directly:

Dependencies in Project (.csproj)

Update the version when you update the NuGet packages. The versions below are examples. Always update to the latest version.

    <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 of the method level.

Important

Always set OriginalMessageType for searchable business data - The OriginalMessageType property determines which Search Field Expressions will extract business data (Order Numbers, Customer IDs, Invoice amounts, Transaction codes) from your logged messages. Without the correct Message Types, features like Non-Events Monitoring, Business Process Modeling (BPM), and business-data searches in Log Views won't work. Plan your Message Types before writing production code - they cannot be changed after logging.

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 the type of sink you use, apply the appropriate configuration using any of the following

Blob

đź’ˇAuthentication and connection options for for Azure Blob Storage (recommended option!).

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

đź’ˇAuthentication and connection options for for Azure 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

đź’ˇConnection options for for SMB Shares and Folders.

{
  "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": {
            ...
          }
        }
      }
    ]
  }
}

Service Bus

đź’ˇAuthentication and connection options for for Azure Service Bus.

Service Bus (Using the default Managed Identity)

{
  "Serilog": {
    "Using": ["Nodinite.Serilog.ServiceBusSink"],
    "WriteTo": [
      {
        "Name": "NodiniteServiceBusSink",
        "Args": {
          "NamespaceName": "acme.servicebus.windows.net",
          "QueueName": "sbq01",
          "Settings": {
            ...
          }
        }
      }
    ]
  }
}

Service Bus (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": {
            ...
          }
        }
      }
    ]
  }
}

Service Bus (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

Configure Message Types - Critical for making your Serilog data searchable and enabling business intelligence features

âť”'How To Start Logging With Microsoft.Extensions.Logging.ILogger in Azure Functions'

Pickup Log Events Service Logging Agent
Log Events
JSON Log Event

Logging
Message Types - Critical for Search Field extraction from Serilog events
Search Fields - Extract business data from logged messages
Search Field Expressions - Define extraction patterns for Order Numbers, Customer IDs, Invoice amounts
Log Views - Search logged data by business identifiers
Non-Events Monitoring - Track message volumes and detect missing messages
Business Process Modeling (BPM) - End-to-end transaction tracking across Azure Functions and other systems