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 ILogger, please navigate to here.
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.
- 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
You must add a NuGet package reference to the following:
- Microsoft.Extensions.Configuration.Json
- Nodinite.Serilog.AzureBlobStorageSink
- 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="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.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": {
...
}
}
}
]
}
}