Serilog.Sinks.ClickHouse 2.2.0

dotnet add package Serilog.Sinks.ClickHouse --version 2.2.0
                    
NuGet\Install-Package Serilog.Sinks.ClickHouse -Version 2.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Serilog.Sinks.ClickHouse" Version="2.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Serilog.Sinks.ClickHouse" Version="2.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Serilog.Sinks.ClickHouse" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Serilog.Sinks.ClickHouse --version 2.2.0
                    
#r "nuget: Serilog.Sinks.ClickHouse, 2.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Serilog.Sinks.ClickHouse@2.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Serilog.Sinks.ClickHouse&version=2.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Serilog.Sinks.ClickHouse&version=2.2.0
                    
Install as a Cake Tool

About

A Serilog sink that writes structured log events to ClickHouse. Events are batched for efficient bulk inserts. Optionally, the library handles table creation automatically.

Requirements
  • ClickHouse 24.1+ (for native JSON column support)
  • .NET 6.0 or later

Quick Start

Install the package:

dotnet add package Serilog.Sinks.ClickHouse

Basic configuration:

Log.Logger = new LoggerConfiguration()
    .WriteTo.ClickHouse(
        connectionString: "Host=localhost;Port=8123;Database=logs",
        tableName: "app_logs")
    .CreateLogger();

Log.Information("User {UserId} logged in from {IpAddress}", 123, "10.0.0.1");

This creates a table app_logs with the following columns:

Column Type Description
timestamp DateTime64(6) Event timestamp (UTC)
level LowCardinality(String) Log level (Information, Warning, etc.)
message String Rendered message with property values substituted
message_template String Raw Serilog message template
exception Nullable(String) Exception.ToString() or null
properties JSON All log event properties as native JSON

All enriched properties are captured in the properties column and can be queried with ClickHouse's JSON dot notation:

SELECT * FROM app_logs WHERE properties.UserId = 123

Preset Schemas

// Default — the 6-column schema shown above
var schema = DefaultSchema.Create("app_logs").Build();

// Minimal — just timestamp, level (numeric), and message
var schema = DefaultSchema.CreateMinimal("app_logs").Build();

// Comprehensive — default + full log event JSON
var schema = DefaultSchema.CreateComprehensive("app_logs").Build();

// Start from a preset and add custom columns
// Each preset returns a SchemaBuilder, which can be further customized before calling .Build()
var schema = DefaultSchema.Create("app_logs")
    .AddPropertyColumn("UserId", "Nullable(Int64)")
    .AddPropertyColumn("RequestPath", "Nullable(String)")
    .Build();

Custom Schema

Use the schema builder to control which columns are created, their names, types, and the table engine:

.WriteTo.ClickHouse(
    connectionString: "Host=localhost;Port=8123;Database=logs",
    configureSchema: schema => schema
        .WithTableName("custom_logs")
        .AddTimestampColumn("event_time", precision: 6)
        .AddLevelColumn("severity", asString: true)
        .AddMessageColumn()
        .AddExceptionColumn()
        .AddPropertiesColumn()
        .AddPropertyColumn("UserId", "Nullable(Int64)", writeMethod: PropertyWriteMethod.Raw)
        .AddPropertyColumn("RequestPath", "Nullable(String)")
        .WithEngine("ENGINE = MergeTree() ORDER BY (event_time) PARTITION BY toYYYYMM(event_time)"))

Schema Builder Methods

Method Description
AddTimestampColumn(name, precision, useUtc) Event timestamp. Default: DateTime64(6), UTC.
AddLevelColumn(name, asString) Log level as LowCardinality(String) or UInt8.
AddMessageColumn(name) Rendered message (properties substituted).
AddMessageTemplateColumn(name) Raw message template.
AddExceptionColumn(name) Exception string or null.
AddPropertiesColumn(name) All properties as JSON.
AddPropertiesColumn(name, columnType) Properties with a custom type string.
AddPropertyColumn(property, type, ...) Single named property extracted into its own column.
AddLogEventColumn(name) Entire log event serialized as JSON.
AddColumn(columnWriter) Any custom ColumnWriterBase implementation.
OnCluster(clusterName) Adds ON CLUSTER clause for distributed DDL.

All column methods also accept optional codec, defaultExpression, ttl, and comment parameters for column-level DDL.

Choosing ORDER BY

ClickHouse query performance depends heavily on ORDER BY. For typical log tables:

// Good for time-range queries
.WithEngine("ENGINE = MergeTree() ORDER BY (timestamp)")

// Better if you often filter by level
.WithEngine("ENGINE = MergeTree() ORDER BY (level, timestamp)")

## Extracting Individual Properties

Use `SinglePropertyColumnWriter` to pull specific enriched properties into dedicated typed columns. This is useful when you want to query or index a known property efficiently instead of parsing JSON.

```csharp
.AddPropertyColumn("UserId", "Nullable(Int64)", writeMethod: PropertyWriteMethod.Raw)
.AddPropertyColumn("RequestPath", "Nullable(String)")
.AddPropertyColumn("CorrelationId", "Nullable(UUID)")

Write methods:

Method Behavior
PropertyWriteMethod.Raw Extracts the raw CLR value from ScalarValue (default)
PropertyWriteMethod.ToString Calls .ToString() on the property value
PropertyWriteMethod.Json Serializes the property as JSON

If a log event doesn't contain the named property, the sink sends a ClickHouse column default (the field is skipped and ClickHouse applies the type's default value, e.g. 0 for Int64, '' for String).

Table Creation Modes

.WriteTo.ClickHouse(
    connectionString: "...",
    tableName: "app_logs",
    tableCreation: TableCreationMode.CreateIfNotExists)  // default
Mode Behavior
CreateIfNotExists Runs CREATE TABLE IF NOT EXISTS on first batch (default, safe, idempotent)
None Assumes the table already exists. Validates existence on startup by default.
DropAndRecreate Drops and recreates the table. Destroys data — dev/testing only.

Batching

The sink uses Serilog's BatchingOptions to control buffer size and flush events. You can tune the defaults:

.WriteTo.ClickHouse(
    connectionString: "Host=localhost;Port=8123;Database=logs",
    tableName: "app_logs",
    batchSizeLimit: 10_000,           // Max events per batch (default: 10,000)
    flushInterval: TimeSpan.FromSeconds(10),  // Time between flushes (default: 5s)
    queueLimit: 100_000)           // Max events in queue (default: 100,000)

Important:* Call Log.CloseAndFlush() on application shutdown to ensure buffered events are written. In ASP.NET Core, UseSerilog() handles this automatically.

Callbacks

Hook into batch lifecycle for metrics or alerting:

.WriteTo.ClickHouse(
    connectionString: "...",
    tableName: "app_logs",
    onBatchWritten: (count, duration) =>
        Console.WriteLine($"Wrote {count} events in {duration.TotalMilliseconds}ms"),
    onBatchFailed: (ex, count) =>
        Console.WriteLine($"Failed to write {count} events: {ex.Message}"))

Note that retries are handled by Serilog and controlled via RetryTimeLimit.

Full Options

For complete control, pass a ClickHouseSinkOptions directly:

var options = new ClickHouseSinkOptions
{
    ConnectionString = "Host=localhost;Port=8123;Database=logs",
    Schema = DefaultSchema.Create("app_logs").Build(),
    TableCreation = new TableCreationOptions
    {
        Mode = TableCreationMode.CreateIfNotExists,
        ValidateOnStartup = true
    },
    MinimumLevel = LogEventLevel.Information,
    FormatProvider = CultureInfo.InvariantCulture,
    OnBatchWritten = (count, duration) => { /* metrics */ },
    OnBatchFailed = (ex, count) => { /* alerting */ }
};

Log.Logger = new LoggerConfiguration()
    .WriteTo.ClickHouse(options)
    .CreateLogger();

Dependency Injection / ASP.NET

If you already have a ClickHouseClient or ClickHouseDataSource registered in your DI container, pass it directly instead of a connection string:

Using ClickHouseDataSource (.NET 7+)

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton(_ =>
    new ClickHouseDataSource("Host=localhost;Port=8123;Database=logs"));

builder.Host.UseSerilog((context, services, loggerConfiguration) =>
{
    var dataSource = services.GetRequiredService<ClickHouseDataSource>();
    loggerConfiguration.WriteTo.ClickHouse(dataSource, tableName: "app_logs");
});

Using ClickHouseClient

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IClickHouseClient>(_ =>
    new ClickHouseClient("Host=localhost;Port=8443;Protocol=https;Database=logs"));

builder.Host.UseSerilog((context, services, loggerConfiguration) =>
{
    var client = services.GetRequiredService<IClickHouseClient>();
    loggerConfiguration.WriteTo.ClickHouse(client, tableName: "app_logs");
});

All DI overloads accept the same optional parameters (database, batchSizeLimit, flushInterval, etc.) as the connection-string overload. For full control, pass a ClickHouseSinkOptions alongside the client or data source:

loggerConfiguration.WriteTo.ClickHouse(options, client);
loggerConfiguration.WriteTo.ClickHouse(options, dataSource); // .NET 7+

Connection String

The sink uses ClickHouse.Driver for the connection. Connection string format:

Host=localhost;Port=8123;Database=logs;User=default;Password=

Troubleshooting

See the serilog docs for debugging and diagnostics tips.

Supported Frameworks

net6.0, net8.0, net9.0, net10.0

License

Apache-2.0

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.2.0 102 4/17/2026
2.1.0 629 3/19/2026
2.0.0 121 2/18/2026
1.0.1 638 8/25/2025
1.0.0 10,921 12/30/2020

## v2.2.0

- Column-level DDL options: `codec`, `defaultExpression`, `ttl`, and `comment` on all `Add*Column` builder methods, maps to ClickHouse `CODEC(...)`, `DEFAULT expr`, `TTL expr`, and `COMMENT 'text'` clauses
- `SchemaBuilder.OnCluster()`: adds `ON CLUSTER cluster_name` to `CREATE TABLE` and `DROP TABLE` DDL for distributed ClickHouse deployments

## v2.1.0

- `SchemaBuilder.AddIndex()` — add ClickHouse data-skipping indexes to auto-created tables (e.g. `set`, `minmax`, `tokenbf_v1`)

## v2.0.0

Complete rewrite of the ClickHouse Serilog sink, built on top of the official [ClickHouse.Driver](https://github.com/ClickHouse/clickhouse-cs).

- New `IBatchedLogEventSink` implementation — batching is fully delegated to Serilog
- Fluent schema builder API for custom column layouts, table engines, and TTL
- Built-in column writers for timestamp, level, message, exception, properties, and full log event JSON
- `SinglePropertyColumnWriter` for extracting individual enriched properties into dedicated typed columns
- Properties stored as native ClickHouse `JSON` with dot-notation query support
- Automatic table creation with `CreateIfNotExists`, `DropAndRecreate`, and `None` modes
- Per-column error isolation — failed columns get default values instead of dropping the entire event
- DI-friendly overloads for `ClickHouseClient`, `ClickHouseDataSource` (.NET 7+), and `ClickHouseClientSettings`
- Observability callbacks (`OnBatchWritten`, `OnBatchFailed`) for metrics and alerting
- Targets net6.0, net8.0, net9.0, net10.0

See the [README](https://github.com/ClickHouse/Serilog.Sinks.ClickHouse) for usage and configuration.