- 5 minutes to read

2.0.x Blob Storage Examples

New 2.0.14

Info

This document applies to all Nodinite Serillog sink versions >= 2.0.14. and assumes you are using the isolated model working with Azure functions using .NET 8.

Required NuGet packages

  • Microsoft.Extensions.Configuration.Json
  • Nodinite.Serilog.AzureBlobStorageSink | Nodinite.Serilog.EventHubSink | Nodinite.Serilog.ServiceBusSink | Nodinite.Serilog.FileSink | Nodinite.Serilog.ApiSink
  • Serilog.Settings.Configuration

There are three 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.

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="Microsoft.Azure.Functions.Worker" Version="1.20.0" />
      <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
      <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.16.2" />
      <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.21.0" />
      <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.0.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
      <PackageReference Include="Nodinite.Serilog.AzureBlobStorageSink" Version="2.0.14" />
      <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 (Using the default Managed Identity)

{
  "Serilog": {
    "Using": ["Nodinite.Serilog.AzureBlobStorageSink"],
    "WriteTo": [
      {
        "Name": "AzureBlobStorageSink",
        "Args": {
          "StorageAccountName": "acme",
          "ContainerName": "logevents",
          "Settings": {
            "LogAgentValueId": 503,
            "EndPointName": "Nodinite.Serilog.Sink.Tests",
            "EndPointUri": "Nodinite.Serilog.Sink.Tests.Serilog",
            "EndPointDirection": 0,
            "EndPointTypeId": 0,
            "OriginalMessageTypeName": "Serilog.LogEvent",
            "ProcessingUser": "NODINITE",
            "ProcessName": "Nodinite.Serilog.Sink.Tests",
            "ProcessingMachineName": "NODINITE-DEV",
            "ProcessingModuleName": "DOTNETCORE.TESTS",
            "ProcessingModuleType": "DOTNETCORE.TESTPROJECT"
          }
        }
      }
    ]
  }
}

appsettings.json (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": {
            "LogAgentValueId": 503,
            "EndPointName": "Nodinite.Serilog.Sink.Tests",
            "EndPointUri": "Nodinite.Serilog.Sink.Tests.Serilog",
            "EndPointDirection": 0,
            "EndPointTypeId": 0,
            "OriginalMessageTypeName": "Serilog.LogEvent",
            "ProcessingUser": "NODINITE",
            "ProcessName": "Nodinite.Serilog.Sink.Tests",
            "ProcessingMachineName": "NODINITE-DEV",
            "ProcessingModuleName": "DOTNETCORE.TESTS",
            "ProcessingModuleType": "DOTNETCORE.TESTPROJECT"
          }
        }
      }
    ]
  }
}

appsettings.json (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": "AzureBlobStorageSinkWithConnectionString",
        "Args": {
          "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=acme;AccountKey=xyz;EndpointSuffix=core.windows.net",
          "ContainerName": "logevents",
          "Settings": {
            "LogAgentValueId": 503,
            "EndPointName": "Nodinite.Serilog.Sink.Tests",
            "EndPointUri": "Nodinite.Serilog.Sink.Tests.Serilog",
            "EndPointDirection": 0,
            "EndPointTypeId": 0,
            "OriginalMessageTypeName": "Serilog.LogEvent",
            "ProcessingUser": "NODINITE",
            "ProcessName": "Nodinite.Serilog.Sink.Tests",
            "ProcessingMachineName": "NODINITE-DEV",
            "ProcessingModuleName": "DOTNETCORE.TESTS",
            "ProcessingModuleType": "DOTNETCORE.TESTPROJECT"
          }
        }
      }
    ]
  }
}