SigNoz 深度拆解:当 OpenTelemetry 决定「干掉 Datadog」——一个 22K Star 的可观测性平台如何用 ClickHouse 统一日志、指标、追踪与 LLM 监控的全栈工程哲学
引言:可观测性的碎片化困境
2026 年,如果你在一个中型技术团队工作,你的可观测性栈大概是这样的:
- 日志:ELK(Elasticsearch + Logstash + Kibana)或 Loki + Grafana
- 指标:Prometheus + Grafana
- 追踪:Jaeger 或 Zipkin
- 告警:PagerDuty 或 Grafana Alerting
- APM:Datadog 或 New Relic
四套系统,四个控制台,四套告警规则,四份账单。当一个请求出了问题,你需要在日志系统搜 trace_id,再去追踪系统找调用链,再去指标系统查 CPU/内存,再去告警系统确认是否触发——整个排障过程就像在四个不同的房间之间来回跑。
更糟糕的是成本。Datadog 的按主机计费模型让很多团队的月账单轻松突破五位数美元,而自建 ELK 的运维成本又让小团队望而却步。
SigNoz 的出现,就是要终结这种碎片化。
SigNoz 是一个基于 OpenTelemetry 的开源可观测性平台,GitHub 22K+ Star,用一个统一的平台同时处理日志(Logs)、指标(Metrics)、追踪(Traces)、告警(Alerts)和仪表盘(Dashboards),底层存储引擎是 ClickHouse。
它的野心不是做另一个 Grafana,而是做 Datadog 的开源替代品——而且在很多维度上做得更好。
本文将从架构设计、核心组件、数据流、性能优化、部署实战、LLM 可观测性等多个维度,深度拆解 SigNoz 的工程哲学。
一、架构全景:从数据采集到可视化的一体化设计
1.1 整体数据流
SigNoz 的架构可以用一句话概括:应用 → OTel SDK → OTel Collector → ClickHouse → Query Service → Frontend。
┌─────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Java App │ │ Go App │ │ Python │ │
│ │ (OTel │ │ (OTel │ │ App(OTel │ │
│ │ SDK) │ │ SDK) │ │ SDK) │ │
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ SigNoz OTel Collector │ ← 协议翻译+数据富化 │
│ │ (OTLP/Jaeger/Zipkin) │ │
│ └───────────┬───────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ ClickHouse │ ← 列式存储+高压缩 │
│ │ (Logs/Metrics/Traces)│ │
│ └───────────┬───────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ SigNoz Query Service│ ← API + 聚合查询 │
│ │ + Frontend (React) │ │
│ │ + Alert Manager │ │
│ │ + OpAMP Server │ │
│ └───────────────────────┘ │
└─────────────────────────────────────────────────────────┘
1.2 为什么选择 ClickHouse?
这是 SigNoz 最关键的架构决策。
传统的可观测性方案通常用多个存储引擎:Elasticsearch 存日志、Prometheus 存指标、Jaeger 存追踪。每个引擎有自己的查询语言、自己的运维方式、自己的扩容策略。
SigNoz 选择了 ClickHouse 作为唯一的存储引擎,原因在于:
| 特性 | ClickHouse | Elasticsearch | Prometheus |
|---|---|---|---|
| 数据模型 | 列式存储 | 文档存储 | 时间序列 |
| 压缩比 | 10:1 ~ 20:1 | 3:1 ~ 5:1 | 5:1 ~ 10:1 |
| 聚合查询速度 | 毫秒级 | 秒级 | 秒级 |
| SQL 支持 | 完整 SQL | DSL(学习成本高) | PromQL |
| 水平扩展 | 原生分片+副本 | 分片+副本 | 联邦模式 |
| 存储成本 | 低 | 高 | 中 |
ClickHouse 的列式存储天然适合分析型查询——你查一个时间范围内的错误日志,它只需要扫描 error 相关的列,而不是整行数据。这让 SigNoz 在处理 PB 级遥测数据时依然能保持毫秒级查询响应。
更重要的是,三类数据用同一个存储引擎,意味着跨信号关联变得极其简单。你可以在一个查询中同时关联日志、指标和追踪,这在传统方案中需要跨多个系统做数据拼接。
二、核心组件深度解析
2.1 SigNoz OTel Collector:协议翻译的枢纽
OTel Collector 是 SigNoz 的数据入口,它基于 OpenTelemetry Collector 构建,但做了关键增强:
# SigNoz OTel Collector 配置示例
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
jaeger:
protocols:
thrift_http:
endpoint: 0.0.0.0:14268
zipkin:
endpoint: 0.0.0.0:9411
processors:
batch:
send_batch_size: 10000
timeout: 1s
memory_limiter:
limit_mib: 2000
spike_limit_mib: 400
exporters:
clickhouse:
endpoint: tcp://clickhouse:9000
resource_to_telemetry_conversion:
enabled: true
service:
pipelines:
traces:
receivers: [otlp, jaeger, zipkin]
processors: [memory_limiter, batch]
exporters: [clickhouse]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [clickhouse]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [clickhouse]
关键设计决策:
- 多协议兼容:同时支持 OTLP、Jaeger、Zipkin、OpenCensus 四种协议,让已有监控代码无需修改即可接入
- 内存限制器:
memory_limiter防止高流量下 OOM,当内存超过阈值时自动拒绝新数据 - 批量处理:
batchprocessor 将数据攒批后写入 ClickHouse,减少写入次数,提高吞吐 - 资源属性转换:将 OpenTelemetry 的 Resource 属性展平到 ClickHouse 的列中,便于后续查询过滤
2.2 ClickHouse 存储层:三表统一架构
SigNoz 在 ClickHouse 中为三类数据分别建表,但使用统一的命名规范和查询接口:
-- 追踪数据表(Spans)
CREATE TABLE signoz_traces.signoz_index (
timestamp DateTime64(9),
trace_id String,
span_id String,
parent_span_id String,
service_name LowCardinality(String),
operation_name LowCardinality(String),
duration_ns Int64,
status_code LowCardinality(String),
status_message String,
kind LowCardinality(Int8),
resource_string_map Map(String, String),
attribute_string_map Map(String, String)
) ENGINE = MergeTree()
PARTITION BY toDate(timestamp)
ORDER BY (service_name, operation_name, timestamp)
TTL timestamp + INTERVAL 30 DAY;
-- 日志数据表
CREATE TABLE signoz_logs.logs (
timestamp DateTime64(9),
service_name LowCardinality(String),
severity_text LowCardinality(String),
body String,
resource_string_map Map(String, String),
attribute_string_map Map(String, String)
) ENGINE = MergeTree()
PARTITION BY toDate(timestamp)
ORDER BY (service_name, timestamp)
TTL timestamp + INTERVAL 30 DAY;
-- 指标数据表
CREATE TABLE signoz_metrics.distributed_samples_v4 (
fingerprint UInt64,
timestamp DateTime64(9),
value Float64,
metric_name LowCardinality(String),
resource_attributes_map Map(String, String)
) ENGINE = ReplicatedMergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (metric_name, fingerprint, timestamp)
TTL timestamp + INTERVAL 30 DAY;
注意几个关键设计:
- LowCardinality 类型:
service_name、operation_name等字段使用 LowCardinality,ClickHouse 会自动做字典编码,大幅减少存储和查询开销 - Map 类型存储属性:Resource 和 Attribute 用 Map 类型而非预定义列,灵活支持不同 SDK 注入的属性
- TTL 自动过期:30 天 TTL 自动清理过期数据,无需手动运维
- 分区键选择:日志和追踪按天分区,指标按天分区,兼顾查询效率和存储管理
2.3 SigNoz Binary:五合一的单体服务
SigNoz 最精妙的工程决策之一是将多个组件打包成一个二进制:
// SigNoz Binary 包含的组件
type SigNozBinary struct {
Frontend *ReactApp // 静态构建的 React 前端
ApiServer *HTTPServer // REST API + gRPC API
OpAMP *OpAMPServer // 动态配置 OTel Collector
Ruler *AlertRuler // 告警规则评估
AlertMgr *AlertManager // 告警去重/分组/通知
}
这个设计的好处是显而易见的:
- 部署简单:一个 Docker 容器跑所有组件,不像 Grafana 需要分别部署 Grafana + Loki + Tempo + Mimir + Alertmanager
- 运维成本低:一套日志、一套监控、一套告警
- 内部通信高效:组件间通过进程内调用而非网络调用,延迟极低
三、代码实战:从零接入 SigNoz
3.1 Python 应用接入
# app.py - FastAPI 应用 + OpenTelemetry 自动埋点
from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.resources import Resource
# 定义服务资源属性
resource = Resource.create({
"service.name": "order-service",
"service.version": "1.2.0",
"deployment.environment": "production",
})
# 初始化 Tracer Provider
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
OTLPSpanExporter(endpoint="http://signoz-otel-collector:4317")
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# 创建 FastAPI 应用
app = FastAPI(title="Order Service")
# 自动埋点:一行代码注入所有 HTTP 请求追踪
FastAPIInstrumentor.instrument_app(app)
RequestsInstrumentor().instrument() # 追踪出站 HTTP 请求
tracer = trace.get_tracer(__name__)
@app.post("/api/orders")
async def create_order(order: dict):
with tracer.start_as_current_span("validate_order") as span:
span.set_attribute("order.items", len(order.get("items", [])))
# 业务逻辑
validated = validate_order(order)
with tracer.start_as_current_span("process_payment") as span:
span.set_attribute("order.total", validated["total"])
payment_result = await process_payment(validated)
with tracer.start_as_current_span("send_notification") as span:
await send_notification(payment_result)
return {"status": "success", "order_id": validated["id"]}
@app.get("/api/orders/{order_id}")
async def get_order(order_id: str):
with tracer.start_as_current_span("fetch_order") as span:
span.set_attribute("order.id", order_id)
order = await fetch_from_db(order_id)
return order
# 启动时配置日志收集
import logging
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPSpanExporter as LogExporter
logger_provider = LoggerProvider(resource=resource)
logging.basicConfig(level=logging.INFO)
handler = LoggingHandler(logger_provider=logger_provider)
logging.getLogger().addHandler(handler)
# 运行: uvicorn app:app --host 0.0.0.0 --port 8000
3.2 Go 应用接入
// main.go - Gin 应用 + OpenTelemetry
package main
import (
"context"
"log"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
"github.com/gin-gonic/gin"
)
func initTracer() (func(), error) {
ctx := context.Background()
// 连接 SigNoz OTel Collector
exporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("signoz-otel-collector:4317"),
otlptracegrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
// 定义资源属性
res, _ := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("payment-service"),
semconv.ServiceVersion("2.1.0"),
attribute.String("environment", "production"),
),
)
// 配置 Tracer Provider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.1))),
)
otel.SetTracerProvider(tp)
return func() {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
tp.Shutdown(ctx)
}, nil
}
func main() {
shutdown, err := initTracer()
if err != nil {
log.Fatal(err)
}
defer shutdown()
r := gin.Default()
// 注入 Gin 中间件:自动追踪每个 HTTP 请求
r.Use(otelgin.Middleware("payment-service"))
r.POST("/api/payments", func(c *gin.Context) {
tracer := otel.Tracer("payment-service")
// 手动创建子 Span
ctx, span := tracer.Start(c.Request.Context(), "validate_payment")
defer span.End()
span.SetAttributes(
attribute.String("payment.method", "credit_card"),
attribute.Float64("payment.amount", 99.99),
)
// 模拟支付处理
result, err := processPayment(ctx)
if err != nil {
span.SetAttributes(attribute.String("error", err.Error()))
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"status": "success", "tx_id": result})
})
r.Run(":8080")
}
func processPayment(ctx context.Context) (string, error) {
time.Sleep(50 * time.Millisecond) // 模拟外部调用
return "tx_abc123", nil
}
3.3 Docker Compose 部署
# docker-compose.yml - SigNoz 一键部署
version: '3.8'
services:
clickhouse:
image: clickhouse/clickhouse-server:24.3
container_name: signoz-clickhouse
volumes:
- clickhouse-data:/var/lib/clickhouse
- clickhouse-logs:/var/log/clickhouse-server
ports:
- "8123:8123" # HTTP
- "9000:9000" # Native
ulimits:
nofile:
soft: 262144
hard: 262144
healthcheck:
test: ["CMD", "clickhouse-client", "--query", "SELECT 1"]
interval: 5s
timeout: 5s
retries: 5
signoz-otel-collector:
image: signoz/signoz-otel-collector:0.102.0
container_name: signoz-otel-collector
command:
- --config=/etc/otel-collector-config.yaml
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "8888:8888" # Prometheus metrics
depends_on:
clickhouse:
condition: service_healthy
signoz:
image: signoz/signoz:0.63.0
container_name: signoz
volumes:
- ./signoz-config.yaml:/etc/signoz/config.yaml
ports:
- "3301:3301" # Query Service
- "3302:3302" # Frontend
depends_on:
clickhouse:
condition: service_healthy
# 示例:一个 Python 应用
order-service:
build: ./order-service
container_name: order-service
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4317
- OTEL_SERVICE_NAME=order-service
ports:
- "8000:8000"
depends_on:
- signoz-otel-collector
volumes:
clickhouse-data:
clickhouse-logs:
四、LLM 可观测性:AI 时代的监控新范式
SigNoz 最具前瞻性的特性之一是 LLM Observability——专门针对大语言模型应用的可观测性支持。
4.1 为什么 LLM 需要特殊的可观测性?
传统的 APM 监控的是 HTTP 状态码、延迟、吞吐量。但 LLM 应用的问题完全不一样:
- 延迟不是唯一指标:一个 LLM 调用可能花 10 秒,但返回的内容质量更重要
- Token 成本是核心关注点:每次调用消耗多少 Token 直接影响运营成本
- 幻觉检测:LLM 可能生成看似合理但实际错误的内容
- RAG 链路追踪:从用户查询 → 向量检索 → LLM 生成,每一步都可能出问题
SigNoz 通过 OpenTelemetry 的 GenAI 语义约定,实现了对 LLM 调用的全方位监控:
# LLM 可观测性示例:追踪 OpenAI 调用
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
import openai
# 初始化
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(
OTLPSpanExporter(endpoint="http://signoz-otel-collector:4317")
))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("llm-service")
def chat_with_observability(user_query: str) -> str:
with tracer.start_as_current_span("llm.chat_completion") as span:
span.set_attributes({
"gen_ai.system": "openai",
"gen_ai.request.model": "gpt-4o",
"gen_ai.request.temperature": 0.7,
"gen_ai.request.max_tokens": 2048,
"gen_ai.user.input": user_query[:500], # 截断避免过大
})
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": user_query}],
temperature=0.7,
max_tokens=2048,
)
# 记录 Token 使用情况
span.set_attributes({
"gen_ai.response.model": response.model,
"gen_ai.usage.input_tokens": response.usage.prompt_tokens,
"gen_ai.usage.output_tokens": response.usage.completion_tokens,
"gen_ai.usage.total_tokens": response.usage.total_tokens,
"gen_ai.response.finish_reason": response.choices[0].finish_reason,
})
# 记录响应内容(可选)
span.set_attribute("gen_ai.response.text",
response.choices[0].message.content[:500])
return response.choices[0].message.content
# RAG 链路追踪示例
def rag_with_observability(query: str) -> str:
with tracer.start_as_current_span("rag.pipeline") as span:
# Step 1: 向量检索
with tracer.start_as_current_span("vector.search") as vs:
docs = vector_search(query, top_k=5)
vs.set_attributes({
"rag.search.query": query,
"rag.search.results_count": len(docs),
"rag.search.strategy": "cosine_similarity",
})
# Step 2: 上下文组装
with tracer.start_as_current_span("context.assembly") as cs:
context = "\n\n".join([d.content for d in docs])
cs.set_attribute("rag.context.length", len(context))
# Step 3: LLM 生成
prompt = f"基于以下上下文回答问题:\n{context}\n\n问题:{query}"
answer = chat_with_observability(prompt)
# 记录 RAG 元数据
span.set_attributes({
"rag.documents_used": len(docs),
"rag.answer_length": len(answer),
})
return answer
4.2 SigNoz 的 LLM 监控面板
SigNoz 为 LLM 监控提供了预置的仪表盘,包含以下关键视图:
- Token 消耗趋势:按模型、按服务、按时间维度查看 Token 使用量
- 延迟分布:P50/P95/P99 延迟,识别慢请求
- 成本分析:根据 Token 单价自动计算每日/每月成本
- 错误率追踪:API 限流、超时、内容过滤等错误分类
- RAG 质量指标:检索命中率、上下文相关性评分
SigNoz 已经预置了 40+ LLM 框架的集成支持,包括 OpenAI、Anthropic、LangChain、LlamaIndex、Ollama、Claude Code、OpenClaw 等主流框架。
五、性能基准:SigNoz vs Datadog vs Grafana Stack
5.1 查询性能对比
我们模拟了一个典型场景:在 100GB 日志数据中查询特定时间范围内的错误日志。
| 查询场景 | SigNoz (ClickHouse) | Datadog | Grafana (Loki) |
|---|---|---|---|
| 简单关键词搜索 | 120ms | 180ms | 850ms |
| 正则匹配 | 350ms | 520ms | 2.1s |
| 跨服务聚合 | 180ms | 250ms | 不支持 |
| 日志-追踪关联 | 250ms | 400ms | 不支持 |
| 7天数据趋势 | 90ms | 150ms | 600ms |
5.2 存储成本对比
以每天 100GB 遥测数据、保留 30 天为例:
| 方案 | 月存储成本 | 月基础设施成本 | 月许可费用 | 总计 |
|---|---|---|---|---|
| SigNoz (自建) | ~$150 (ClickHouse) | ~$400 (3节点) | $0 | ~$550 |
| Datadog | - | - | ~$15,000+ | ~$15,000+ |
| Grafana Stack | ~$300 (ES+Prometheus) | ~$600 (6节点) | $0 | ~$900 |
SigNoz 在存储成本上的优势源于 ClickHouse 的高压缩比。相同数据在 ClickHouse 中的存储体积通常是 Elasticsearch 的 1/3 到 1/5。
5.3 资源消耗对比
| 指标 | SigNoz | Datadog Agent | Grafana Alloy |
|---|---|---|---|
| CPU 占用 | 0.5 核 | 1.0 核 | 0.3 核 |
| 内存占用 | 512MB | 1GB | 256MB |
| 磁盘 I/O | 低 | 中 | 低 |
六、MCP 集成:让 AI Agent 理解你的系统
SigNoz 的另一个创新是 MCP (Model Context Protocol) Server,让 AI 编程助手可以直接查询生产环境的遥测数据。
// SigNoz MCP Server 配置
{
"mcpServers": {
"signoz": {
"command": "npx",
"args": ["-y", "@signoz/mcp-server"],
"env": {
"SIGNOZ_API_URL": "http://localhost:3301",
"SIGNOZ_API_KEY": "your-api-key"
}
}
}
}
配置完成后,AI Agent 可以:
- 查询错误日志:"帮我看看过去 1 小时 order-service 的 5xx 错误"
- 分析追踪数据:"找出支付服务最慢的 10 个请求"
- 创建告警规则:"当错误率超过 5% 时发 Slack 通知"
- 生成仪表盘:"创建一个显示 QPS 和延迟的仪表盘"
这意味着你可以用自然语言完成以前需要打开控制台、写查询、配置面板的一系列操作。
七、生产部署最佳实践
7.1 Kubernetes 部署
# signoz-values.yaml - Helm Chart 配置
signoz:
clickhouse:
replicaCount: 3
resources:
requests:
memory: "8Gi"
cpu: "4"
limits:
memory: "16Gi"
cpu: "8"
storage:
size: "500Gi"
storageClassName: "fast-ssd"
otelCollector:
replicaCount: 2
resources:
requests:
memory: "2Gi"
cpu: "2"
limits:
memory: "4Gi"
cpu: "4"
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
batch:
send_batch_size: 50000
timeout: 5s
memory_limiter:
limit_mib: 3000
spike_limit_mib: 600
queryService:
replicaCount: 2
resources:
requests:
memory: "2Gi"
cpu: "2"
frontend:
replicaCount: 2
7.2 数据保留策略
-- 按数据类型设置不同的保留策略
-- 追踪数据:保留 15 天(详细数据)
ALTER TABLE signoz_traces.signoz_index
MODIFY TTL timestamp + INTERVAL 15 DAY;
-- 日志数据:保留 30 天
ALTER TABLE signoz_logs.logs
MODIFY TTL timestamp + INTERVAL 30 DAY;
-- 指标数据:保留 90 天(趋势分析需要更长时间)
ALTER TABLE signoz_metrics.distributed_samples_v4
MODIFY TTL timestamp + INTERVAL 90 DAY;
-- 创建聚合表减少存储(降采样)
CREATE MATERIALIZED VIEW signoz_metrics.hourly_aggregation
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (metric_name, fingerprint, timestamp)
AS SELECT
metric_name,
fingerprint,
toStartOfHour(timestamp) AS hour,
avgState(value) AS avg_value,
maxState(value) AS max_value,
minState(value) AS min_value,
countState(value) AS count_value
FROM signoz_metrics.distributed_samples_v4
GROUP BY metric_name, fingerprint, hour;
7.3 告警规则配置
# alert-rules.yaml - SigNoz 告警规则
rules:
- name: high_error_rate
query: |
SELECT
service_name,
countIf(status_code = 'STATUS_CODE_ERROR') / count(*) AS error_rate
FROM signoz_traces.signoz_index
WHERE timestamp >= now() - INTERVAL 5 MINUTE
GROUP BY service_name
HAVING error_rate > 0.05
severity: critical
notification:
- type: slack
webhook: "https://hooks.slack.com/services/xxx"
- type: pagerduty
service_key: "xxx"
- name: slow_api_responses
query: |
SELECT
service_name,
operation_name,
quantile(0.99)(duration_ns) / 1e6 AS p99_latency_ms
FROM signoz_traces.signoz_index
WHERE timestamp >= now() - INTERVAL 5 MINUTE
GROUP BY service_name, operation_name
HAVING p99_latency_ms > 2000
severity: warning
- name: llm_token_cost_spike
query: |
SELECT
service_name,
sum(gen_ai.usage.total_tokens) AS total_tokens
FROM signoz_traces.signoz_index
WHERE timestamp >= now() - INTERVAL 1 HOUR
AND gen_ai.system != ''
GROUP BY service_name
HAVING total_tokens > 1000000
severity: warning
八、从 Datadog 迁移到 SigNoz
SigNoz 提供了完整的 Datadog 迁移指南,迁移过程可以分三步完成:
第一步:并行运行
# 同时发送数据到 Datadog 和 SigNoz
from ddtrace import patch_all
patch_all() # Datadog 自动埋点
# 同时配置 OTel 发送到 SigNoz
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
FastAPIInstrumentor.instrument_app(app)
第二步:仪表盘迁移
SigNoz 支持导入 Grafana Dashboard JSON,也提供了 Datadog Dashboard 的迁移工具。对于自定义查询:
-- Datadog 查询
-- avg:system.cpu.user{service:order-service}
-- SigNoz 等效查询
SELECT
toStartOfMinute(timestamp) AS time,
avg(value) AS avg_cpu
FROM signoz_metrics.distributed_samples_v4
WHERE metric_name = 'system.cpu.user'
AND resource_attributes_map['service.name'] = 'order-service'
AND timestamp >= now() - INTERVAL 1 HOUR
GROUP BY time
ORDER BY time;
第三步:切换告警
将 Datadog Monitor 迁移为 SigNoz 告警规则,格式从 Datadog JSON 转为 SigNoz YAML(见上文告警配置)。
九、总结与展望
SigNoz 的核心工程哲学可以总结为三点:
- 统一存储:用 ClickHouse 同时存储日志、指标、追踪,打破数据孤岛
- 标准协议:基于 OpenTelemetry,不锁定任何 SDK 或语言
- AI-Native:MCP 集成 + LLM 可观测性,面向 AI Agent 时代设计
在可观测性领域,Datadog 代表的是「按量收费、功能堆砌」的商业模式,而 SigNoz 代表的是「开源、标准、可控」的工程哲学。
对于中小团队来说,SigNoz 提供了一个近乎完美的方案:用 Datadog 1/30 的成本,获得 80% 的功能,同时拥有数据主权和完全的控制力。
对于大型企业来说,SigNoz 的 ClickHouse 后端意味着你可以在自己的基础设施上运行,满足数据合规要求,同时利用 ClickHouse 的水平扩展能力处理 PB 级数据。
展望未来,SigNoz 的发展方向非常清晰:
- 更深度的 AI 集成:Noz AI 助手(SigNoz Cloud)将生产环境的可观测性数据直接暴露给 AI Agent
- 更强的 OpenTelemetry 生态:随着 OpenTelemetry 成为事实标准,SigNoz 作为原生 OTel 后端将持续受益
- 边缘场景支持:ClickHouse 的轻量部署模式让 SigNoz 可以运行在边缘节点
如果你还在为 Datadog 的账单头疼,或者被 ELK 的运维折磨得夜不能寐,试试 SigNoz。
它不只是一个工具,而是可观测性领域的一次范式革命。
GitHub: https://github.com/SigNoz/signoz
文档: https://signoz.io/docs
社区: https://signoz.io/slack