Search code examples
c#serilogserilog-aspnetcoreserilog-sinks-elasticsearch

Serilog ElasticSearch Sink custom properties


I'm trying to log to my Elasticsearch instance some data from .NET 6.0 Web Api with Serilog.AspNetCore and Serilog.Sinks.Elasticsearch installed, but I need to use custom properties for example:

  • bankName
  • totalValue
  • customer.name

and others similar to this, but I created custom Enricher:

    public class ApiEnricher : ILogEventEnricher
    {
        private const string bankName = "Total Bank";
        private const decimal totalValue = 21;
        private const string customerName = "Adam";

        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            // Add default values for required fields
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("bankName", bankName));
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("total.value", totalValue));
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("cusotmer.name", cusomerName));
        }
    }

I tried to add this to my appsettings.json:

    "Serilog": {
        "Using": [ "Serilog.Sinks.Elasticsearch", "WebApi.Common" ],
        "MinimumLevel": "Debug",
        "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "ApiEnricher" ],
        "WriteTo": [
            {
                "Name": "Console",
                "Args": {
                    "outputTemplate": "[{Timestamp:HH:mm:ss.fff} {Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
                }
            },
            {
                "Name": "Elasticsearch",
                "Args": {
                    "nodeUris": "http://host.docker.internal:9200",
                    "indexFormat": "logs-myApp-example-{0:yyyy.MM}",
                    "autoRegisterTemplate": true,
                    "inlineFields": true,
                    "numberOfReplicas": 2,
                    "numberOfShards": 2
                }
            }
        ]
    },

But with no luck, only example output I'm getting is:

{
  "@timestamp": "2023-11-27T14:02:24.2061221+00:00",
  "level": "Information",
  "messageTemplate": "Example {bankName} {totalValue} {customerName}",
  "message": "Example \"Forbank\" 21.0 \"Bar\"",
  "bankName": "Forbank",
  "totalValue": 21.0,
  "customerName": "Bar",
  "SourceContext": "WebApi.Controllers.SolutionController",
  "ActionId": "1a52a98f-ecc6-40f2-a632-dc045d53811b",
  "ActionName": "WebApi.Controllers.SolutionController.Init",
  "RequestId": "0HMVFA5SJN8GJ:00000004",
  "RequestPath": "/api/example/init",
  "ConnectionId": "0HMVFA5SJN8GJ"
}

Logging with: logger.LogInformation("Example {bankName} {totalValue} {customerName}", bankName, 21, "Bar");

works but I want solution that does not enforce rewriting each logs, also if possible I want it to be configurable from appsettings.json and I will prefer for those props to be only available on Elasticsearch sink.

EDIT:

Let me clarify where the question came from.

I received requirements from an external company regarding what the logs sent to Elastic should look like.

Among these requirements, I have a list of parameters that must appear in elastic, such as: user.name, user.phone, class, exception, etc.

And I was wondering if it could be done in some clever way without having to redo all the logs.


Solution

  • This could be done with just appsettings.json configuration, adding Properties section, see this example from official docs:

    {
      "Serilog": {
        "Using": [ "Serilog.Sinks.Elasticsearch" ],
        "MinimumLevel": "Warning",
        "WriteTo": [
          {
            "Name": "Elasticsearch",
            "Args": {
              "nodeUris": "http://localhost:9200"
            }
          }
        ],
        "Enrich": [ "FromLogContext", "WithMachineName" ],
        "Properties": {
          "Application": "My app"
        }
      }
    }