---
title: "evlog 与 OpenTelemetry：连接日志与追踪"
description: "通过 OTLP HTTP 将 evlog 事件发送到 OpenTelemetry Collector，并附加活动追踪上下文，而不替换现有追踪"
canonical_url: "https://evlog.zhcndoc.com/compare/opentelemetry"
---
# evlog 与 OpenTelemetry：连接日志与追踪

> 通过 OTLP HTTP 将 evlog 事件发送到 OpenTelemetry Collector，并附加活动追踪上下文，而不替换现有追踪

你已经将追踪导出到 OpenTelemetry Collector，现在希望在旁边添加应用程序日志。evlog 可以构建这些日志事件，并通过 OTLP 发送。保留现有的追踪插桩：evlog 不会创建 spans 或 metrics。

## 为每一层分配职责

```text [Data flow]
Application fields -> evlog event -> OTLP HTTP /v1/logs -> Collector -> backend
Active OTel span  -> traceId + spanId on the event
OTel SDK         -> spans and metrics                -> Collector -> backend
```

SDK 为操作添加插桩，Collector 接收并处理遥测数据，而 backend 提供存储、查询和告警。[OpenTelemetry 文档](https://opentelemetry.io/docs/) 介绍了这些职责。语义约定统一共享属性，而自定义应用属性仍然有效。

## 使用活动 span 上下文导出事件

此示例假设 Node.js 应用已经初始化了其 OpenTelemetry SDK 和上下文管理器。该函数必须在活动 span 内运行，才能附加追踪上下文。如果尚未配置追踪，请参阅 [JavaScript 插桩指南](https://opentelemetry.io/docs/languages/js/instrumentation/)。

在该应用中安装日志包和 OTel API：

```bash [Terminal]
npm install evlog @opentelemetry/api
```

该函数将已完成的结账事件写入本地 JSON，并通过 OTLP 导出。它会显式等待交付完成：导出失败会记录 drain 名称但不会抛出，因此不会中断请求路径。调用方需要观察失败时，请使用 `drain.raw({ event })`。不要同时在全局注册此 drain，否则事件会被导出两次。

```typescript [checkout-log.ts]
import { isSpanContextValid, trace } from '@opentelemetry/api'
import { createLogger, initLogger } from 'evlog'
import { createOTLPDrain } from 'evlog/otlp'

initLogger({ env: { service: 'checkout' }, pretty: false })
const drain = createOTLPDrain({ recordShape: 'compact' })

export async function recordCheckout(orderId: string, outcome: 'paid' | 'declined') {
  const log = createLogger({ orderId, outcome })
  const spanContext = trace.getActiveSpan()?.spanContext()
  if (spanContext && isSpanContextValid(spanContext)) {
    log.set({ traceId: spanContext.traceId, spanId: spanContext.spanId })
  }
  if (outcome === 'declined') log.error(new Error('Payment declined'))
  const event = log.emit()
  if (event) await drain({ event })
}
```

在应用程序的环境中配置 OTLP HTTP 基础端点。如果未设置，drain 会记录缺少端点的警告并跳过交付：

```bash [.env]
OTLP_ENDPOINT=http://localhost:4318
```

通过应用程序或 Node 的 `--env-file` 选项加载该文件。仅有一个名为 `.env` 的文件并不会配置任意 Node.js 进程。

对于本地 Collector，启用 logs pipeline。以下最小配置会通过 debug exporter 打印接收到的记录：

```yaml [otel-collector.yaml]
receivers:
  otlp:
    protocols:
      http:
        endpoint: 127.0.0.1:4318
exporters:
  debug:
    verbosity: detailed
service:
  pipelines:
    logs:
      receivers: [otlp]
      exporters: [debug]
```

使用此配置运行 Collector，并在已插桩的操作中调用 `recordCheckout('order-123', 'paid')`。位于独立容器中的 Collector 需要可访问的接收器地址和端口映射。对于同一主机上的进程，请使用上面的环回配置。

## 在发布前验证关联

通过 `orderId` 查找记录。其 resource 应包含 `service.name: checkout`，并且其 `traceId` 和 `spanId` 应与同一操作中的活动 span 匹配。如果没有有效的活动 span，事件仍然会导出，但这些标识符不会存在。

该 drain 会将级别和时间戳映射到 OTLP 严重性和时间字段。使用 `recordShape: 'compact'` 时，嵌套的应用字段会变成点号分隔的属性。默认 JSON 形状使用序列化的事件 body，并将嵌套属性值作为 OTLP 键值列表发送。设置 `semanticConventions: true` 可在 evlog 字段旁添加 `http.request.method` 和 `gen_ai.usage.input_tokens` 等 OpenTelemetry 属性名称。[OTLP 适配器参考](https://evlog.zhcndoc.com/integrate/adapters/hybrid/otlp)介绍了映射和交付选项。

TraceContext 丰富化器会读取传入的 `traceparent`，并将其 span 标识符记录为 `parentSpanId`，因为该 span 属于调用方。它不会设置 `spanId`。需要将事件关联到服务器 span 时，请像上面一样使用活动的 SDK 上下文。

## 决定采样内容和交付方式

evlog 中的事件采样和 [OpenTelemetry 中的追踪采样](https://opentelemetry.io/docs/concepts/sampling/) 作用于不同的信号。保留的日志可能引用一个被追踪策略丢弃的 trace。在依赖从每条错误日志导航到已存储 trace 之前，请协调好这些策略。

此示例会等待网络请求完成。对于生产环境的 handler 集成，请有意配置 drain pipeline 和框架的后台交付生命周期。该适配器支持 OTLP HTTP logs，不支持仅限 gRPC 的端点。序列化、批处理、重试和导出都会增加成本，而事件构建微基准不会测量这些成本。有关这些边界，请参阅 [性能](https://evlog.zhcndoc.com/reference/performance)。

如果你使用 Nitro，集成可以在启用 evlog 后，通过 `evlog:drain` 调用 `createOTLPDrain()`。无论采用哪种设置，都请保留现有的 OTel 插桩。有关库到库的选择，请继续阅读 [evlog 与其他日志记录器的对比](https://evlog.zhcndoc.com/reference/vs-other-loggers)。

---

- [OTLP 适配器](https://evlog.zhcndoc.com/integrate/adapters/hybrid/otlp)
- [性能基准](https://evlog.zhcndoc.com/reference/performance)


## Sitemap

See the full [sitemap](https://evlog.zhcndoc.com/sitemap.md) for all pages.
