- 8 minutes to read

Logging using SeriLog

Use one of the Nodinite SeriLog sinks with your full framework and .NET Core applications to enable end to end tracking. This feature also includes Microsoft Azure functions built with .NET (usually CSharp).

Develop Azure Functions using Visual Studio

Nodinite SeriLog Sinks
NuGet package manager example, filtered by Nodinite packages

The following Nodinite Serilog sinks exists:

Sink Reliable Messaging GitHub Nuget
Log API GitHub NuGet Version
File (SMB) GitHub NuGet Version
Azure Service Bus GitHub NuGet Version
Azure Event Hub GitHub NuGet Version

Configuration

You must provide the configuration in either code (explicit) or by using the configuration file appsettings.json (implicit)

  1. Code
  2. Appsettings.json

1. Code

string nodiniteApiUrl = "https://yourenv/nodinite/logapi/";

var settings = new NodiniteLogEventSettings()
{
    LogAgentValueId = 42,
    EndPointDirection = 0,
    EndPointTypeId = 0,
    EndPointUri = "Nodinite.Serilog.ApiSink.Tests.Serilog",
    EndPointName = "Nodinite.Serilog.ApiSink.Tests",
    ProcessingUser = "NODINITE",
    ProcessName = "Nodinite.Serilog.LogApiSink.Tests",
    ProcessingMachineName = "NODINITE-DEV",
    ProcessingModuleName = "DOTNETCORE.TESTS",
    ProcessingModuleType = "DOTNETCORE.TESTPROJECT"
};

ILogger log = new LoggerConfiguration()
   .WriteTo.NodiniteApiSink(nodiniteApiUrl, settings)
   .CreateLogger();

2. Appsettings.json

{
  "Serilog": {
    "Using": [ "Nodinite.Serilog.Apisink" ],
    "WriteTo": [
      {
        "Name": "NodiniteApiSink",
        "Args": {
          "NodiniteApiUrl": "",
          "Settings": {
            "LogAgentValueId": 42,
            "EndPointName": "Nodinite.Serilog.ApiSink.Tests",
            "EndPointUri": "Nodinite.Serilog.ApiSink.Tests.Serilog",
            "EndPointDirection": 0,
            "EndPointTypeId": 86,
            "OriginalMessageTypeName": "https://ACME.com/Customers/1.0#Batch",
            "ProcessingUser": "NODINITE",
            "ProcessName": "Nodinite.Serilog.ApiSink.Tests",
            "ProcessingMachineName": "NODINITE-DEV",
            "ProcessingModuleName": "DOTNETCORE.TESTS",
            "ProcessingModuleType": "DOTNETCORE.TESTPROJECT"
          }
        }
      }
    ]
  }
}
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

Logger log = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .CreateLogger();

1. Code

var folder = "{Your Folder}";

var settings = new NodiniteLogEventSettings()
{
    LogAgentValueId = 42,
    EndPointDirection = 0,
    EndPointTypeId = 0,
    EndPointUri = "Nodinite.Serilog.FileSink.Tests.Serilog",
    EndPointName = "Nodinite.Serilog.FileSink.Tests",
    ProcessingUser = "NODINITE",
    ProcessName = "Nodinite.Serilog.FileSink.Tests",
    ProcessingMachineName = "NODINITE-DEV",
    ProcessingModuleName = "DOTNETCORE.TESTS",
    ProcessingModuleType = "DOTNETCORE.TESTPROJECT"
};

ILogger log = new LoggerConfiguration()
    .WriteTo.NodiniteFileSink(folder, settings)
    .CreateLogger();

2. Appsettings.json

{
  "Serilog": {
    "Using": [ "Nodinite.Serilog.FileSink" ],
    "WriteTo": [
      {
        "Name": "NodiniteFileSink",
        "Args": {
          "Folder": "",
          "Settings": {
            "LogAgentValueId": 42,
            "EndPointName": "Nodinite.Serilog.FileSink.Tests",
            "EndPointUri": "Nodinite.Serilog.FileSink.Tests.Serilog",
            "EndPointDirection": 0,
            "EndPointTypeId": 86,
            "OriginalMessageTypeName": "https://ACME.com/Customers/1.0#Batch",
            "ProcessingUser": "NODINITE",
            "ProcessName": "Nodinite.Serilog.FileSink.Tests",
            "ProcessingMachineName": "NODINITE-DEV",
            "ProcessingModuleName": "DOTNETCORE.TESTS",
            "ProcessingModuleType": "DOTNETCORE.TESTPROJECT"
          }
        }
      }
    ]
  }
}
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

Logger log = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .CreateLogger();

1. Code

var connectionString = "{Your ServiceBus Connection String";
var queueName = "{Your ServiceBus Queue Name}";

var settings = new NodiniteLogEventSettings()
{
    LogAgentValueId = 42,
    EndPointDirection = 0,
    EndPointTypeId = 0,
    EndPointUri = "Nodinite.Serilog.ServiceBusSink.Tests.Serilog",
    EndPointName = "Nodinite.Serilog.ServiceBusSink.Tests",
    ProcessingUser = "NODINITE",
    ProcessName = "Nodinite.Serilog.ServiceBusSink.Tests",
    ProcessingMachineName = "NODINITE-DEV",
    ProcessingModuleName = "DOTNETCORE.TESTS",
    ProcessingModuleType = "DOTNETCORE.TESTPROJECT"
};

ILogger log = new LoggerConfiguration()
    .WriteTo.NodiniteServiceBusSink(connectionString, queueName, settings)
    .CreateLogger();

2. Appsettings.json

{
  "Serilog": {
    "Using": [ "Nodinite.Serilog.ServiceBusSink" ],
    "WriteTo": [
      {
        "Name": "NodiniteServiceBusSink",
        "Args": {
          "ConnectionString": "",
          "QueueName":  "",
          "Settings": {
            "LogAgentValueId": 42,
            "EndPointName": "Nodinite.Serilog.ServiceBusSink.Tests",
            "EndPointUri": "Nodinite.Serilog.ServiceBusSink.Tests.Serilog",
            "EndPointDirection": 0,
            "EndPointTypeId": 86,
            "OriginalMessageTypeName": "https://ACME.com/Customers/1.0#Batch",
            "ProcessingUser": "NODINITE",
            "ProcessName": "Nodinite.Serilog.ServiceBusSink.Tests",
            "ProcessingMachineName": "NODINITE-DEV",
            "ProcessingModuleName": "DOTNETCORE.TESTS",
            "ProcessingModuleType": "DOTNETCORE.TESTPROJECT"
          }
        }
      }
    ]
  }
}
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

Logger log = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .CreateLogger();

1. Code

var connectionString = "{Your EventHub Connection String";

var settings = new NodiniteLogEventSettings()
{
    LogAgentValueId = 42,
    EndPointDirection = 0,
    EndPointTypeId = 0,
    EndPointUri = "Nodinite.Serilog.EventHubSink.Tests.Serilog",
    EndPointName = "Nodinite.Serilog.EventHubSink.Tests",
    ProcessingUser = "NODINITE",
    ProcessName = "Nodinite.Serilog.EventHubSink.Tests",
    ProcessingMachineName = "NODINITE-DEV",
    ProcessingModuleName = "DOTNETCORE.TESTS",
    ProcessingModuleType = "DOTNETCORE.TESTPROJECT"
};

ILogger log = new LoggerConfiguration()
    .WriteTo.NodiniteEventHubSink(connectionString, settings)
    .CreateLogger();

2. Appsettings.json

{
  "Serilog": {
    "Using": [ "Nodinite.Serilog.EventHubSink" ],
    "WriteTo": [
      {
        "Name": "NodiniteEventHubSink",
        "Args": {
          "ConnectionString": "",
          "Settings": {
            "LogAgentValueId": 42,
            "EndPointName": "Nodinite.Serilog.EventHubSink.Tests",
            "EndPointUri": "Nodinite.Serilog.EventHubSink.Tests.Serilog",
            "EndPointDirection": 0,
            "EndPointTypeId": 86,
            "OriginalMessageTypeName": "https://ACME.com/Customers/1.0#Batch",
            "ProcessingUser": "NODINITE",
            "ProcessName": "Nodinite.Serilog.EventHubSink.Tests",
            "ProcessingMachineName": "NODINITE-DEV",
            "ProcessingModuleName": "DOTNETCORE.TESTS",
            "ProcessingModuleType": "DOTNETCORE.TESTPROJECT"
          }
        }
      }
    ]
  }
}
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

Logger log = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .CreateLogger();

Logging

Microsoft.Extensions.Primitives.StringValues id;
req.Headers.TryGetValue("x-ms-client-tracking-id", out id);

Serilog.ILogger log = new LoggerConfiguration()
     .WriteTo.NodiniteEventHubSink(connectionString, settings)
     .Enrich.WithProperty("Color", "Blue")
     .CreateLogger()
     .ForContext("ApplicationInterchangeId", id[0])
     .ForContext("body", "{\"Order\": {...");

log.Information("Order '12' has been imported.");

simple code example

Please review the Pickup Service for additional information.

Log Event

The Nodinite SeriLog sink produces a Nodinite JSON Log Event.

The JSON Log Event has three distinct parts:

  1. Event details (also named Additional field values within Nodinite)
  2. Payload (Body, 0 or more)
  3. Context properties (Key/Value)
graph TD subgraph "Log Event" subgraph "1. Details" roED[fal:fa-bolt Event Details
LogDateTime = 2018-05-03 13:37:00+02:00
EndPointName = https://api.nodinite.com/...
MessageType=Invoice
...] end subgraph "2. Payload" ro[fal:fa-envelope Message
Body=base64EncodedMessage] end subgraph "3. Context Properties" roKey[fal:fa-key Key Values
InvoiceNo = 123
CorrelationId=456
...] end end

1. Details

The following mandatory fields must be set on the Nodinite class either in configuration or with code:

Mandatory Data Type Field Value Comment
number LogAgentValueId 42 Who (Log Agents) sent the data
string EndPointName "Nodinite.Serilog.ApiSink.Tests" Name of Endpoint transport
string EndPointUri "https://sampleazurefunction1337.azurewebsites.net/api/HttpTrigger1" URI for Endpoint transport
number EndPointDirection 10 (Two-way receive) Direction for Endpoint transport
number EndPointTypeId 86 (HTTPS) Type of Endpoint transport
string OriginalMessageTypeName "https://ACME.com/Customers/1.0#Batch" Message Type Name

Note: The specified values are only examples

Review the JSON Log Event user guide for details about the optional fields.

2. Payload

If you want to log the payload (body), then please add it as plain text in the context property ("body").

3. Context Properties

The following optional coded context properties are mapped if present to the following Nodinite "additional field values":

Context Key Description
body Plain text The Nodinite SeriLog sink internally adds this to the optional Body field
OriginalMessageTypeName Name of MessageType, if not set, the default value is https://ACME.com/Customers/1.0#Batch
ApplicationInterchangeId Please re-use if possible a scenario wide id, for example the x-ms-client-tracking-id in Azure

Review the Context Options user guide for additional features overriding the default behaviour.

Automatically assigned

The following properties are automatically assigned by the Nodinite SeriLog sink:

Field Description Example
ServiceInstanceActivityId A guid unique for each log event ee2daba2-4807-4ad8-1337-620e2b9c0052
LocalInterchangeId A unique re-used guid set on all logged events within one class instance ff2daba2-4807-4ad8-1337-620e2b9c0053
LogDateTime UTC 2020-05-17T13:37:42.001+01:00
LogText The text to log Hello World
Log Status Code The user defined Log Status Code for the logged event Review the 'Log Status Code translation table' on this page.

Log Status Code translation table

The Nodinite Log Status Codes are translated as described in the table below from the following SeriLog Log Levels:

SeriLog Log Level Nodinite Log Status Code Nodinite Log Status Code example
Debug 0 None log.Debug
Info 0 None log.Information
Verbose 0 None log.Verbose
Error -1 Error log.Error
Fatal -2 Fatal log.Fatal
Warning 1 Warning log.Warning

.NET Exceptions

New 1.3.0

If you log an Exception, for example using the LogError( method, the Nodinite sink adds it to the Context. The name of the Context key is ExtendedProperties/1.0#Exception.

From Nodinite, you can then use the Formula plugin to create Search Fields and extract whatever piece of information you seek from the Exception.

Extract Message from Exception
Example extracting the Message part of an exception.


JsonPath('..Message', Context('ExtendedProperties/1.0#Exception'))

The Exception can host any number of inner exceptions.


Next Step

Log Events
JSON Log Event

Logging