Skip to content

A high-performance Serilog sink that writes log events to MongoDB

License

Notifications You must be signed in to change notification settings

loresoft/serilog-sinks-mongo

Repository files navigation

Serilog.Sinks.Mongo

NuGet License

A high-performance Serilog sink that writes log events to MongoDB. This sink provides efficient batching, flexible configuration, and support for MongoDB-specific features like TTL indexes and capped collections.

Features

  • Batched Writes - Efficient batch processing of log events
  • Automatic Expiration - TTL index support for automatic log rotation
  • Capped Collections - Size and document count limited collections
  • Flexible Configuration - Code-based and configuration file support
  • Customizable Document Format - Extensible document factory pattern
  • High Performance - Asynchronous writes with configurable buffering

Installation

Install via NuGet:

dotnet add package Serilog.Sinks.Mongo

Or using Package Manager Console:

Install-Package Serilog.Sinks.Mongo

Quick Start

Basic Usage

using Serilog;

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs"
    )
    .CreateLogger();

Log.Information("Hello, MongoDB!");
Log.CloseAndFlush();

With TTL Index (Automatic Expiration)

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs",
        expireAfter: TimeSpan.FromDays(30) // Logs expire after 30 days
    )
    .CreateLogger();

With Capped Collection

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs",
        maxDocuments: 10000, // Maximum 10,000 documents
        maxSize: 10485760    // Maximum 10 MB
    )
    .CreateLogger();

Configuration

Code-Based Configuration

Using Connection String

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs",
        minimumLevel: LogEventLevel.Information,
        expireAfter: TimeSpan.FromDays(7),
        batchSizeLimit: 100,
        bufferingTimeLimit: TimeSpan.FromSeconds(2)
    )
    .CreateLogger();

Using MongoUrl

var mongoUrl = new MongoUrl("mongodb://localhost:27017");

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        mongoUrl: mongoUrl,
        databaseName: "serilog",
        collectionName: "logs"
    )
    .CreateLogger();

Using Options Configuration

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(options =>
    {
        options.ConnectionString = "mongodb://localhost:27017";
        options.DatabaseName = "serilog";
        options.CollectionName = "logs";
        options.MinimumLevel = LogEventLevel.Debug;
        options.ExpireAfter = TimeSpan.FromDays(30);
        options.BatchSizeLimit = 100;
        options.BufferingTimeLimit = TimeSpan.FromSeconds(5);
        
        // Capped collection options
        options.CollectionOptions = new CreateCollectionOptions
        {
            Capped = true,
            MaxSize = 5242880,    // 5 MB
            MaxDocuments = 1000
        };
        
        // Custom properties to promote to top-level
        options.Properties = new HashSet<string> 
        { 
            "SourceContext", 
            "RequestId",
            "UserId"
        };
    })
    .CreateLogger();

JSON Configuration (appsettings.json)

{
  "Serilog": {
    "Using": ["Serilog.Sinks.Mongo", "Serilog.Sinks.Console"],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "MongoDB",
        "Args": {
          "connectionString": "mongodb://localhost:27017",
          "databaseName": "serilog",
          "collectionName": "logs",
          "expireAfter": "30.00:00:00"
        }
      }
    ],
    "Enrich": ["FromLogContext"]
  }
}

Then in your code:

using Serilog;
using Microsoft.Extensions.Configuration;

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

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

Configuration Options

MongoSinkOptions

Property Default Description
ConnectionString - MongoDB connection string
MongoUrl - Alternative to ConnectionString
DatabaseName "serilog" Database name
CollectionName "logs" Collection name
MinimumLevel Verbose Minimum log level to write
ExpireAfter - TTL for automatic document expiration
BatchSizeLimit 100 Maximum batch size
BufferingTimeLimit 00:00:02 Maximum time to wait before writing a batch
CollectionOptions - MongoDB collection creation options
Properties {"SourceContext"} Properties to promote to top-level
DocumentFactory - Custom document factory
MongoFactory - Custom MongoDB factory

Document Structure

By default, log events are stored with the following structure:

{
  "_id": ObjectId("..."),
  "Timestamp": ISODate("2025-11-27T10:30:00.000Z"),
  "Level": "Information",
  "Message": "User logged in successfully",
  "TraceId": "00-abc123...",
  "SpanId": "def456...",
  "SourceContext": "MyApp.Controllers.AuthController",
  "Properties": {
    "UserId": "12345",
    "Username": "john.doe",
    "IPAddress": "192.168.1.1"
  },
  "Exception": {
    "Message": "...",
    "Type": "System.Exception",
    "Text": "...",
    "HResult": -2146233088
  }
}

Property Promotion

Properties can be promoted from the Properties object to the top level of the document:

options.Properties = new HashSet<string> 
{ 
    "SourceContext",
    "RequestId",
    "UserId",
    "MachineName"
};

This results in:

{
  "_id": ObjectId("..."),
  "Timestamp": ISODate("2025-11-27T10:30:00.000Z"),
  "Level": "Information",
  "Message": "Processing request",
  "SourceContext": "MyApp.Services.ProcessingService",
  "RequestId": "req-789",
  "UserId": "12345",
  "MachineName": "WEB-SERVER-01",
  "Properties": {
    // Other properties...
  }
}

Custom Document Factory

Implement IDocumentFactory to customize the document structure:

public class CustomDocumentFactory : DocumentFactory
{
    public override BsonDocument? CreateDocument(LogEvent logEvent, MongoSinkOptions options)
    {
        var document = base.CreateDocument(logEvent, options);
        
        if (document != null)
        {
            // Add custom fields
            document["Application"] = "MyApp";
            document["Environment"] = "Production";
            
            // Custom transformations
            if (logEvent.Properties.TryGetValue("RequestPath", out var path))
            {
                document["Path"] = path.ToString();
            }
        }
        
        return document;
    }
}

// Use the custom factory
Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(options =>
    {
        options.ConnectionString = "mongodb://localhost:27017";
        options.DatabaseName = "serilog";
        options.CollectionName = "logs";
        options.DocumentFactory = new CustomDocumentFactory();
    })
    .CreateLogger();

Advanced Scenarios

Multiple Sinks with Different Configurations

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "errors",
        minimumLevel: LogEventLevel.Error
    )
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "all-logs",
        minimumLevel: LogEventLevel.Information,
        expireAfter: TimeSpan.FromDays(7)
    )
    .CreateLogger();

With Microsoft.Extensions.Hosting

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSerilog(loggerConfiguration =>
{
    loggerConfiguration
        .MinimumLevel.Information()
        .Enrich.FromLogContext()
        .WriteTo.Console()
        .WriteTo.MongoDB(
            connectionString: "mongodb://localhost:27017",
            databaseName: "serilog",
            collectionName: "logs",
            expireAfter: TimeSpan.FromDays(30)
        );
});

var host = builder.Build();
await host.RunAsync();

Performance Tuning

Batch Configuration

Adjust batching settings based on your throughput requirements:

options.BatchSizeLimit = 500;           // Larger batches for high throughput
options.BufferingTimeLimit = TimeSpan.FromSeconds(10); // Longer wait for batch fill

MongoDB Collection Strategies

TTL Index (Time-Based Expiration)

Best for applications that need automatic log cleanup:

.WriteTo.MongoDB(
    connectionString: "mongodb://localhost:27017",
    databaseName: "serilog",
    collectionName: "logs",
    expireAfter: TimeSpan.FromDays(30)
)

A TTL index is automatically created on the Timestamp field to removes expired documents.

Capped Collection (Size/Count Limited)

Best for fixed-size log storage:

.WriteTo.MongoDB(
    connectionString: "mongodb://localhost:27017",
    databaseName: "serilog",
    collectionName: "logs",
    maxDocuments: 100000,  // Keep latest 100k documents
    maxSize: 104857600     // Or 100 MB, whichever is hit first
)

Oldest documents are automatically removed when limits are reached.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A high-performance Serilog sink that writes log events to MongoDB

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors 3

  •  
  •  
  •  

Languages