ClickHouseInc

Serilog + ClickHouse:.NET 日志新范式,告别慢查询与高成本!

图片

本文字数:14407;估计阅读时间:37 分钟

作者:Alex Soffronow Pagonidis

Image

如果您目前在 .NET 应用程序中使用 Serilog (Serilog),您的日志很可能被发送到 Seq、ELK 或某个云日志平台。然而,扩展日志基础设施面临诸多挑战:查询速度变慢、成本不断攀升,并且管理日志处理管道也变得愈发困难。

ClickHouse (ClickHouse) 提供了一种截然不同的方法:它不再将日志视为需要发送到专门系统的数据,而是让您将其存储在一个高性能的分析型数据库 (analytical database) 中,并使用 SQL (SQL) 进行查询。ClickHouse 旨在摄取海量数据并对其执行快速查询,这使其天然适合处理结构化日志,能够高效地过滤、聚合和探索数据。

在本文中,我们将构建一个 ASP.NET (ASP.NET) 服务,它使用 Serilog 直接将结构化日志写入 ClickHouse。如果您已经在使用 Serilog,那么集成 ClickHouse 将非常简单:只需一个 WriteTo.ClickHouse() 调用,即可将您的日志直接发送到 ClickHouse,并全面控制数据模式 (schema)、索引 (indexing) 和存储 (storage)。

本文将涵盖:

• 使用 ClickHouse sink 配置 Serilog

• 在 C# 中使用流式构建器 (fluent builder) 设计数据模式 (schema)

• 使用请求级上下文(例如,关联 ID (correlation IDs)、用户数据等)丰富日志

• 使用 SQL 查询日志并构建实用的诊断功能

本演示的完整代码,以及便捷的 Docker (Docker) 配置,可在 GitHub 上找到(https://github.com/ClickHouse/clickhouse-serilog-demo/tree/main)。

Image
ClickHouse 为何适用于日志?

ClickHouse 是一种列式分析数据库 (column-oriented analytical database),尤其擅长处理高频追加写入 (append-heavy writes) 和分析型读取 (analytical reads),后者在扫描海量数据的同时仅需访问少数列。换句话说,它非常适合用于存储日志。

• 列式存储 = 更少的数据读取。 例如, SELECT timestamp, level, message WHERE level = 'Error' 这样的查询只会扫描这三列,而不会扫描每一行的所有字段。

• 10-20 倍的压缩率,日志场景典型。 将相似值(如时间戳、日志级别)存储在一起,能够实现极高的压缩比,从而显著降低存储成本。

• 结构化数据保持结构化。 原生 JSON (JSON) 支持意味着您可以将日志属性存储为类型化字段 (typed fields),并直接对其进行查询:例如 WHERE properties.UserId = 'user-42' 。

• 内置全文搜索。 倒排索引 (inverted indexes) 使得在大型数据集上进行快速文本搜索成为可能,无需单独的搜索引擎。

• 使用 SQL,而非定制查询语言。 GROUP BY 、 JOIN 、窗口函数 (window functions)、物化视图 (materialized views) 等,您熟悉的一切功能都可直接使用。

除此之外,ClickStack 还提供了一个用户界面 (UI),用于探索日志数据、构建仪表盘、使用自然语言搜索等功能。它还支持日志聚类等高级功能,通过对相似日志模式进行分组来加速根本原因分析。

想了解 ClickHouse 在可观测性领域的一些实际部署案例,请查阅我们之前的文章:

• Trip.com 如何从 Elasticsearch 迁移到 ClickHouse 并构建 50PB 日志平台:在相同硬件条件下,实现了 4 倍的数据容量,性能比 Elasticsearch 快 4 到 30 倍。(https://clickhouse.com/blog/how-trip.com-migrated-from-elasticsearch-and-built-a-50pb-logging-solution-with-clickhouse)

• Netflix 如何使用 ClickHouse 优化其 PB 级日志系统 ,实现每秒处理超过 1000 万条事件。(https://clickhouse.com/blog/netflix-petabyte-scale-logging)

Image
设置基础设施

这是我们将要搭建的系统的高层概览:

Image

前提条件

• 带有 Docker Compose 的 Docker

Docker Compose

我们的基础设施由两个容器组成:ClickStack 和我们的演示 API。有关完整的 Docker 设置,请参阅 docker-compose.yml。

ClickStack 将 ClickHouse、一个可观测性 UI 和一个 OTel collector 打包成一个单一容器。为了方便,我们在此处使用 ClickStack,但 Serilog sink 通过其 HTTP 接口直接写入 ClickHouse。这种直接方法使我们能够完全控制表结构:包括自定义列、Typed JSON、分区和压缩编解码器。

DEFAULT_CONNECTIONS 和 DEFAULT_SOURCES 环境变量预配置了一个自定义数据源,从而使 ClickStack UI 能够查询我们的日志表。

想先看看最终结果吗?

git clone https://github.com/ClickHouse/clickhouse-serilog-demo.git

cd clickhouse-serilog-demo

docker compose up

……并打开 http://localhost:8080 开始探索日志。

Image
构建演示 API

服务

我们的演示是一个简单的产品目录和下单 API,没有独立的数据库。它在所有日志级别上生成各种日志事件:

Endpoint
Log behavior
GET /products
Info:目录已列出
GET /products/{id}
Info:已找到 / Warning:未找到
POST /products
Info:产品已创建
POST /orders
Info:已下单 / Warning:库存不足 / Error:缺货或支付失败
GET /orders/{id}
Warning:未找到

它还包含一些用于健康检查和流量生成的实用端点。

Serilog + ClickHouse sink 配置

首先,安装所需的包:

dotnet add package Serilog.AspNetCore

dotnet add package Serilog.Enrichers.Environment

dotnet add package Serilog.Sinks.ClickHouse

Serilog.AspNetCore 提供了核心的 Serilog 集成和 UseSerilogRequestLogging()。Serilog.Enrichers.Environment 添加了 WithMachineName()。而 Serilog.Sinks.ClickHouse 则是其 sink 本身。

由于 Serilog sink 直接写入 ClickHouse,你可以完全控制表模式,无需管理任何中间管道。我们在启动时配置 sink 和表模式。你无需完全理解所有这些细节即可开始——默认配置即可正常工作,但在有需要时,你可以完全控制其模式。

builder.Host.UseSerilog((context, services, loggerConfiguration) =>

{

    loggerConfiguration

        .MinimumLevel.Debug()

        .ReadFrom.Configuration(context.Configuration)

        .Enrich.FromLogContext()

        .Enrich.WithProperty("ServiceName", "DemoApi")

        .Enrich.WithMachineName()

        .WriteTo.Console()

        .WriteTo.ClickHouse(

            connectionString: context.Configuration["ClickHouse:ConnectionString"]!,

            configureSchema: schema => schema

                .WithDatabase("logs")

                .WithTableName("app_logs")

                .AddTimestampColumn()

                .AddLevelColumn()

                .AddMessageColumn()

                .AddMessageTemplateColumn()

                .AddExceptionColumn()

                .AddPropertiesColumn("properties", "JSON(ServiceName String, RequestPath String, Elapsed Float64, UserId String, OrderId String, OrderStatus String)")

                .AddPropertyColumn("CorrelationId", "Nullable(String)")

                .AddPropertyColumn("RequestPath", "Nullable(String)")

                .AddPropertyColumn("StatusCode", "Nullable(Int32)", writeMethod: PropertyWriteMethod.Raw)

                .AddPropertyColumn("ServiceName", "LowCardinality(String)")

                .AddIndex("INDEX idx_message message TYPE text(tokenizer = splitByNonAlpha, preprocessor = lowerUTF8(message)) GRANULARITY 8")

                .WithEngine(@"ENGINE = MergeTree

                              ORDER BY (level, timestamp)

                              TTL timestamp + toIntervalDay(30)"),

            flushInterval: TimeSpan.FromSeconds(2));

});

下面我们来详细解析这个 schema builder:

• AddTimestampColumn() — DateTime64(6) 类型,精度达到微秒级,采用 UTC 时间。

• AddLevelColumn() — 日志级别,字符串类型(例如 Information 、 Warning 、 Error 等)。

• AddMessageColumn() — 渲染后的消息,其中已替换了属性值。

• AddMessageTemplateColumn() — 原始模板(例如 "Order {OrderId} placed by {UserId}" ),这对于对相似日志条目进行分组非常有用。

• AddExceptionColumn() — 完整的异常文本,包含堆栈跟踪;如果没有异常则为空。

• AddPropertiesColumn("properties", "JSON(...)") — 所有增强属性都将作为 ClickHouse 的 JSON 列 存储。第二个参数用于声明频繁查询的子列的类型提示——例如, ServiceName 为 String 类型, Elapsed 为 Float64 类型, UserId 为 String 类型等。ClickHouse 仍然可以动态接受任何其他属性;这些类型提示只是为了在这些特定路径上实现更好的压缩和更快的查询。如果没有类型提示, AddPropertiesColumn() 会创建一个普通的 JSON 列,在插入时推断其类型。

• AddPropertyColumn("CorrelationId", ...) — 将 CorrelationId 提升为独立的顶级列。由于它在几乎每个请求跟踪查询中都会被使用,将其作为专用列会更快速、更便捷。

• AddPropertyColumn("StatusCode", ..., PropertyWriteMethod.Raw) — Raw 参数指示写入器提取 CLR 原始值(一个 int 类型),而非其字符串表示。

• AddIndex("INDEX idx_message ...") — 在 message 列上添加一个 全文索引 。 text 索引类型通过 splitByNonAlpha 分词和 lowerUTF8 预处理构建倒排索引。

• WithEngine() — 允许您提供一个自定义的表引擎创建字符串。在本例中,我们按级别和时间戳对日志进行排序,并设置 30 天的生命周期 (time-to-live)。

请注意 .ReadFrom.Configuration(context.Configuration) 调用,它会从 appsettings.json 中获取日志级别覆盖配置。正如 Serilog.AspNetCore 文档所推荐 的,我们禁用了嘈杂的内置 ASP.NET Core 日志源,以便 UseSerilogRequestLogging() 成为 HTTP 请求事件的单一事实来源:

{

  "Serilog": {

    "MinimumLevel": {

      "Override": {

        "Microsoft.AspNetCore.Hosting": "Warning",

        "Microsoft.AspNetCore.Mvc": "Warning",

        "Microsoft.AspNetCore.Routing": "Warning"

      }

    }

  }

}

如果没有这些覆盖配置,您会从 ASP.NET 托管和路由管道中看到每个请求产生多个冗余事件。

该 Sink 在首次批量写入时会自动创建 logs 数据库和 app_logs 表。在生产环境中,建议在外部管理 schema,而不是让库自动创建表:这样能提供更可预测的控制,并允许你独立处理数据迁移。

生产环境下的 schema 模式

该演示 schema 有意设计得较为简单。在生产环境中,你通常会利用 ClickHouse 提供的丰富配置选项来优化性能。例如,CODEC(Delta(8), ZSTD(1)) 等编码器声明允许你为时间序列型列调整压缩参数。

如果你正在 Kubernetes 中运行,你可能希望将 Namespace、PodName 和 ContainerName 等列包含在 ORDER BY 键中,以便快速过滤查询。欲深入了解数据层建模,请查阅我们之前的博文 Building an Observability Solution with ClickHouse。生产环境下的列列表可能更像这样:

\`Timestamp\` DateTime64(9) CODEC(Delta(8), ZSTD(1)),

\`SeverityText\` LowCardinality(String) CODEC(ZSTD(1)),

\`ServiceName\` LowCardinality(String) CODEC(ZSTD(1)),

\`Body\` String CODEC(ZSTD(1)),

\`Namespace\` LowCardinality(String),

\`PodName\` LowCardinality(String),

\`ContainerName\` LowCardinality(String),

\`Properties\` JSON

关联 ID 中间件

我们还将设置一个中间件层来处理关联 ID,这使我们能够跨不同服务追踪相关请求。每个请求都会获取一个关联 ID,该 ID 可以来自 X-Correlation-ID 请求头,或者在请求头缺失时生成一个新的 GUID。此 ID 会被推送到 Serilog 的 LogContext 中,从而使该请求内的每个日志条目自动携带此关联 ID:

public sealed class CorrelationIdMiddleware(RequestDelegate next)

{

    public async Task InvokeAsync(HttpContext context)

    {

        var correlationId = context.Request.Headers["X-Correlation-ID"].FirstOrDefault()

                            ?? Guid.NewGuid().ToString();

        context.Response.Headers["X-Correlation-ID"] = correlationId;

        using (LogContext.PushProperty("CorrelationId", correlationId))

        {

            await next(context);

        }

    }

}

请在中间件管道中,UseSerilogRequestLogging() 之前注册此功能,以便关联 ID 也能出现在 Serilog 的请求摘要事件中。

每个请求一个事件

UseSerilogRequestLogging() 用每个请求的单个摘要事件取代了 ASP.NET 中冗长且针对每个中间件的日志记录方式,该摘要事件包含了 HTTP 方法、路径、状态码和耗时。这种方式更简洁高效。

你可以通过添加额外的属性来丰富此完成事件:全局范围通过 EnrichDiagnosticContext,针对每个端点则通过 IDiagnosticContext。我们的演示实现了这两种方式。在中间件配置中,你可以从 HttpContext 添加变量,例如用户代理字符串:

app.UseSerilogRequestLogging(options =>

{

    options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>

    {

        diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value ?? "unknown");

        diagnosticContext.Set("UserAgent", httpContext.Request.Headers.UserAgent.ToString());

    };

});

接着,在各个独立的端点中,你可以附加该端点特有的属性,这些属性将一同出现在该完成事件中:

app.MapPost("/orders", (OrderRequest request, OrderService orders,

    IDiagnosticContext diagnosticContext) =>

{

    diagnosticContext.Set("UserId", request.UserId);

    diagnosticContext.Set("ProductId", request.ProductId);

    // ...

});

健康检查

/health 接口利用 ASP.NET Core 内置的健康检查框架来验证 ClickHouse 的连接状态。我们使用一个简单的 IHealthCheck 实现,它会打开一个连接并执行 SELECT 1 命令(详见 ClickHouseHealthCheck.cs)。

平滑关机

由于此接收器 (sink) 会在内存中批量处理事件,因此在进程退出前,您必须调用 Log.CloseAndFlush()(或其异步版本),否则最后一个未完成的批次将丢失。

我们的演示代码使用 try/finally 块来包裹 app.Run():

try

{

    app.Run();

}

finally

{

    await Log.CloseAndFlushAsync();

}

这确保了在正常关机时,内存中最后一个批次的数据能够被成功刷新。

Image
运行演示

启动整个技术栈:

docker compose up -d

大约 15 秒后,您可以通过 http://localhost:5000 访问演示 API,ClickHouse Play UI 位于 http://localhost:8123/play,而仪表盘 UI 则在 http://localhost:8080。

Image

此 API 内置了一个流量生成器,可以对每个接口进行调用:

curl -X POST http://localhost:5000/generate-traffic

您将在控制台中看到结构化日志输出:

[14:23:01 INF] Product catalog listed — 5 products

[14:23:01 WRN] Product 999 not found

[14:23:01 INF] Product 6 (Standing Desk) created in category Furniture at $599.99

[14:23:01 INF] Order a1b2c3d4 placed by user-42: 2x Mechanical Keyboard for $299.98

[14:23:01 WRN] Low stock alert: product 3 (USB-C Hub) has only 3 units remaining

[14:23:01 ERR] Order failed — product 4 (Monitor Stand) is out of stock

[14:23:02 ERR] Chaos endpoint triggered — TimeoutException

结构化日志实战

结构化日志与传统日志的关键区别在于,结构化日志将数据保留为带有类型和名称的字段,而不是将所有内容扁平化为单一字符串。数字依然是数字,日期依然是日期,并且每个属性都可以单独查询。这使得日志成为一个可查询的数据集,并允许 ClickHouse 这样的列式数据库高效地存储和压缩它们。

以下是订单服务如何记录成功订单的日志:

_logger.LogInformation(

    "Order {OrderId} placed by {UserId}: {Quantity}x {ProductName} for {TotalPrice:C}",

    orderId, request.UserId, request.Quantity, product.Name, product.Price * request.Quantity);

这会生成一个带有命名和类型属性的日志事件,而不仅仅是一个扁平的字符串。在 ClickHouse 中,您可以随后进行查询:

SELECT * FROM logs.app_logs

WHERE properties.UserId = 'user-42'

ORDER BY timestamp DESC;

Image
探索您的日志

由于您的日志是标准的 ClickHouse 表,您可以使用任何 SQL 客户端进行查询。最快的入门方式是使用 ClickHouse 内置的 Play UI(位于 http://localhost:8123/play)或 ClickStack 界面(位于 http://localhost:8080)。

在 ClickStack UI 中,您可以看到已渲染的日志消息以及底层的结构化属性。

Image
Image

我们的 ClickStack 实例预配置了一个数据源,指向 logs.app_logs 表,并包含一个预构建的仪表板。一旦演示生成流量,该仪表板就会即时显示数据。

Image

该仪表板包含五个可视化组件,展示了在 ClickHouse 中使用结构化日志数据所能获得的开箱即用功能:

• 错误率 — 堆叠条形图,显示每个时间段内 countIf(level='Error') / count() 的结果,助您一目了然地洞察错误峰值。

• 按级别划分的日志量 — 按严重性级别分组的日志总数。有助于识别日志模式的变化(例如,警告的突然激增)。

• 响应时间 (p50 / p95 / p99) — 基于 properties.Elapsed (由 Serilog 的请求日志中间件捕获的毫秒级持续时间)的分位数折线图。之所以能实现此功能,是因为数据 sink 将 Elapsed 作为类型化列写入,而不是将其隐藏在 JSON blob 中。

• 按端点划分的请求 — 按 RequestPath 分组的日志数量,让您了解哪些端点承载了最多流量。

• 警告和错误 — 一个可搜索的日志表,筛选出 level:"Warning" OR level:"Error" 级别的记录,并显示时间戳、级别和消息。

SQL 查询

一旦生成了流量,您可以尝试以下查询:

每分钟错误率:

SELECT

    toStartOfMinute(timestamp) AS minute,

    countIf(level = 'Error') AS errors,

    count() AS total,

    round(errors / total * 100, 2) AS error_rate_pct

FROM logs.app_logs

WHERE timestamp > now() - INTERVAL 1 HOUR

GROUP BY minute

ORDER BY minute;

慢请求检测:

SELECT timestamp, message, StatusCode, properties.Elapsed

FROM logs.app_logs

WHERE properties.Elapsed > 1000

ORDER BY properties.Elapsed DESC

LIMIT 10;

由于数据本质上就是 SQL 表,您可以将日志数据与其他 ClickHouse 表进行 JOIN 操作,构建用于实时聚合的物化视图 (materialized views),或者将结果导出到任何支持 SQL 的工具中。

Image
生产环境考量

批处理

该 Sink 会在数据写入 ClickHouse 之前对日志事件进行批处理。在 ClickHouse 中,每次插入都会创建一个新的 表部分 (table part)(一个 ClickHouse 稍后在后台进行合并的不可变数据段)。因此,建议一次性插入大批量数据,而非进行小而频繁的插入操作。如果确实需要进行小批量插入,则应启用 异步插入 (async inserts)。

关键参数如下:

• batchSizeLimit — 每个批次的最大事件数(默认:10,000)。对于高吞吐量服务,建议增加此值。

• flushInterval — 刷新间隔时间(默认:5 秒)。如需接近实时的可见性,可适当降低此值。

• queueLimit — 内存中缓冲的最大事件数(默认:100,000)。当 ClickHouse 处理速度缓慢时,此参数用于提供反压机制。

表引擎、分区、索引

ClickHouse 在数据存储和查询方面提供了极大的控制力。对于大多数日志工作负载,默认采用基于时间的 MergeTree 引擎是一个稳健的起点:

ENGINE = MergeTree

PARTITION BY toMonday(timestamp)

ORDER BY (timestamp);

这种方式使数据按时间组织,与典型的日志查询和数据保留策略高度契合。

用户可以通过在 level 或 CorrelationId 等常用过滤列上添加二级索引来进一步优化性能,从而使 ClickHouse 在查询时能够跳过大量数据。此外,ClickHouse 还提供了按列配置压缩的能力,用户可以灵活设置增量编码 (delta encoding) 和压缩算法。

对于文本搜索,ClickHouse 同时支持轻量级 bloom filter 索引和全文本 inverted 索引。在实际基准测试中,全文本索引的查询速度比 bloom filter 索引快约 10 倍,能够将耗时数秒的全扫描查询缩短至亚秒级查找。其代价是磁盘上索引文件会显著增大。但对于大多数部署而言,这是一项值得的权衡:正如 text index GA blog 中所述,在 ClickHouse Cloud 中,存储成本通常仅占整体基础设施开销的一小部分。

如果您希望深入探究存储布局、分区策略和索引类型的调优,请查阅 ClickStack Performance Tuning guide(https://clickhouse.com/docs/use-cases/observability/clickstack/performance_tuning)。

基于 TTL 的日志保留

ClickHouse 支持数据自动过期:

ALTER TABLE logs.app_logs

MODIFY TTL timestamp + INTERVAL 30 DAY

SETTINGS ttl_only_drop_parts = 1;

30 天后,旧分区会被自动删除。ttl_only_drop_parts 设置确保 ClickHouse 直接删除整个数据分片,而非通过重写数据来移除过期行,这对于日志工作负载而言效率要高得多,因为日志工作负载通常同一分区内的所有行会同时过期。无需 cron 任务,亦无手动清理负担。

故障处理

在服务器端,ClickHouse Cloud 承担了大部分繁重工作,它能自动处理数据复制、故障转移、弹性扩展和数据备份。您的数据汇聚器 (sink) 只需指向一个连接字符串,用户无需管理底层基础设施。

在客户端,即便 ClickHouse 集群运行状况良好,网络仍然可能出现瞬时抖动:例如 DNS 解析瞬时异常,或者服务与数据库之间的短暂连接中断。数据汇聚器 (sink) 通过在内存中缓冲日志事件,并在下一个刷新周期进行重试来应对此类情况。最大队列大小、刷新间隔和批处理大小均可配置。对于大多数服务,默认配置已能提供一个合理的容错窗口。

请注意,这是一个内存缓冲区 (in-memory buffer),而非持久化队列 (durable queue)。如果服务在缓冲区满时重启,这些事件将会丢失。若需确保消息投递,则需添加回退机制。

为监测这些情况,请将 sink 的回调函数接入您的告警系统 (alerting system)。其中 onBatchFailed 尤为关键:如果日志写入失败,您会需要及时获知。

.WriteTo.ClickHouse(

    connectionString: "...",

    tableName: "app_logs",

    onBatchWritten: (count, elapsed) =>

        Console.WriteLine($"Wrote {count} events in {elapsed.TotalMilliseconds}ms"),

    onBatchFailed: (exception, count) =>

        Console.Error.WriteLine($"Failed to write {count} events: {exception.Message}"));

基于具化视图 (materialized views) 的实时告警

ClickHouse 具化视图 (materialized views) 会在数据插入时进行转换:您无需任何外部数据管道,即可实时聚合错误率。例如,若要跟踪每分钟的错误计数:

CREATE MATERIALIZED VIEW logs.error_counts_per_minute

ENGINE = SummingMergeTree

ORDER BY minute

AS

SELECT

    toStartOfMinute(timestamp) AS minute,

    level,

    count() AS count

FROM logs.app_logs

WHERE level IN ('Error', 'Fatal')

GROUP BY minute, level;

每当一批日志被插入时,ClickHouse 都会增量更新聚合数据。随后,您可以查询 error_counts_per_minute 以构建实时错误仪表盘 (real-time error dashboard),或在计数激增时触发告警。这些视图也可在 ClickStack 中使用,ClickStack 内置了告警支持。

Image
资源

• GitHub 上的演示源码(https://github.com/ClickHouse/clickhouse-serilog-demo/tree/main)

• Serilog.Sinks.ClickHouse GitHub 仓库(https://github.com/ClickHouse/Serilog.Sinks.ClickHouse)

• ClickHouse Cloud — 提供免费套餐的托管 ClickHouse(https://clickhouse.com/cloud)

• Serilog 文档(https://serilog.net/)

• ClickStack 性能调优 — 用于生产可观测性 (observability) 工作负载的优化(https://clickhouse.com/docs/use-cases/observability/clickstack/performance_tuning)

• ClickHouse 中的全文搜索 — 现已全面可用 (GA) — 亚秒级日志搜索的倒排索引(https://clickhouse.com/blog/full-text-search-ga-release)

• Trip.com 如何使用 ClickHouse 构建 50PB 日志解决方案 — 从 Elasticsearch 迁移,在相同硬件上实现 4 倍数据容量提升(https://clickhouse.com/blog/how-trip.com-migrated-from-elasticsearch-and-built-a-50pb-logging-solution-with-clickhouse)

• 使用 ClickHouse 构建日志平台并相比 Datadog 节省数百万美元 — 相比 SaaS 方案成本降低 200 倍,压缩比达 17 倍(https://clickhouse.com/blog/building-a-logging-platform-with-clickhouse-and-saving-millions-over-datadog)

Image
总结

我们构建了一个 .NET 服务,它通过 Serilog 生成结构化日志事件 (structured log events),直接写入 ClickHouse,并支持我们通过 SQL 查询日志。

关键要点 (key takeaways):

• Serilog 用户即插即用 :如果你的 .NET 应用程序已经在使用 Serilog,集成 ClickHouse sink 只需调用一次 WriteTo.ClickHouse() 方法即可。

• 成本与性能 :ClickHouse 处理日志工作负载的成本远低于传统日志平台,实现了 10-20 倍的数据压缩,并能对数十亿行数据进行亚秒级分析查询。

• 完整的模式控制 :你可以决定日志的存储方式。通过 C# 中流畅的模式构建器,可以定义类型化列、二级索引、分区以及压缩编解码器等。

• 全文搜索 :ClickHouse 正式发布的倒排索引(inverted indexes)功能,让你的现有表格也能实现亚秒级的日志搜索——无需额外的搜索引擎。

• 无前端锁定 :你的日志数据以标准 ClickHouse 表的形式存储。你可以通过 Play UI 使用 SQL 进行查询,或连接 Grafana (通过 ClickHouse 数据源插件)、 Metabase 、 ClickStack 以及任何其他支持 SQL 的工具。

整个演示只需执行一个 docker compose up 命令即可运行。克隆仓库,生成一些流量,你就能在 ClickHouse 中看到结构化日志(structured logs)的实际效果。如果你准备尝试托管部署,ClickHouse Cloud 将是你的理想选择。

/END/

试用阿里云 ClickHouse企业版

轻松节省30%云资源成本?阿里云数据库ClickHouse 云原生架构全新升级,首次购买ClickHouse企业版计算和存储资源组合,首月消费不超过99.58元(包含最大16CCU+450G OSS用量)了解详情:https://t.aliyun.com/Kz5Z0q9G

图片
图片

征稿启示

面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:[email protected]

图片图片