Cloud or Self-Hosted
通过 OpenTelemetry 协议(OTLP)向 Grafana、Datadog、Honeycomb 以及任何兼容的后端发送日志。支持 gRPC 和 HTTP 传输。

OTLP(OpenTelemetry 协议)适配器以标准的 OpenTelemetry 格式发送日志。这适用于任何兼容 OTLP 的后端,包括:

  • Grafana Cloud (Loki)
  • Datadog
  • Honeycomb
  • Jaeger
  • Splunk
  • New Relic
  • 自建 OpenTelemetry 收集器
  • HyperDX

添加 OTLP 排水适配器

安装

OTLP 适配器与 evlog 一起打包提供:

src/index.ts
import { createOTLPDrain } from 'evlog/otlp'

快速开始

1. 设置您的 OTLP 端点

.env
OTLP_ENDPOINT=http://localhost:4318

2. 将排水连接到您的框架

// server/plugins/evlog-drain.ts
import { createOTLPDrain } from 'evlog/otlp'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createOTLPDrain())
})

配置

适配器按优先级从多个来源读取配置(优先级从高到低):

  1. 传递给 createOTLPDrain() 的覆盖项
  2. 运行时配置 runtimeConfig.otlp(仅限 Nuxt/Nitro)
  3. 环境变量

环境变量

变量说明
OTLP_ENDPOINTOTLP HTTP 端点(例如,http://localhost:4318)。标准的 OTEL_EXPORTER_OTLP_ENDPOINT 也可用。
OTLP_HEADERSkey=value 键值对形式提供的请求头,使用逗号分隔。标准的 OTEL_EXPORTER_OTLP_HEADERS 也可用。
OTEL_SERVICE_NAME服务名称覆盖项

运行时配置(仅限 Nuxt)

nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    otlp: {
      endpoint: '', // 通过 OTLP_ENDPOINT(或 OTEL_EXPORTER_OTLP_ENDPOINT)设置
    },
  },
})

覆盖选项

server/plugins/evlog-drain.ts
const drain = createOTLPDrain({
  endpoint: 'http://localhost:4318',
  serviceName: 'my-api',
  headers: {
    'Authorization': 'Bearer xxx',
  },
  resourceAttributes: {
    'deployment.environment': 'staging',
  },
})

完整配置参考

选项类型默认值描述
endpointstring-OTLP HTTP 端点(必填)
serviceNamestring来自事件覆盖 service.name 资源属性
headersobject-用于身份验证的自定义 HTTP 请求头
resourceAttributesobject-其他 OTLP 资源属性
recordShape'json' | 'compact''json'记录承载事件的方式(详情
timeoutnumber5000请求超时时间(毫秒)

部署

OTLP 是一种协议,而不是产品。相同的适配器既可以与您自行运行的收集器通信,也可以与托管网关通信。只有端点和请求头会发生变化。

自托管

运行一个 OpenTelemetry Collector,并让 evlog 指向它。无需其他配置:

otel-collector.yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    logs:
      receivers: [otlp]
      exporters: [debug]
终端
docker run --rm -p 4318:4318 \
  -v $(pwd)/otel-collector.yaml:/etc/otelcol/config.yaml \
  otel/opentelemetry-collector:latest
.env
OTLP_ENDPOINT=http://localhost:4318

从这里开始,收集器会将数据分发到您想要的任何位置:Loki、ClickHouse、Elasticsearch、托管后端,或同时分发到多个位置。这种间接层正是选择 OTLP 而不是直接适配器的原因。

托管网关

相同的适配器,加上需要凭据的端点:

OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic%20base64-encoded-credentials
Grafana Cloud 使用经过 URL 编码的请求头,因此 %20 表示空格。适配器会自动解码这种格式。

OTLP 日志格式

evlog 将广泛事件映射到 OTLP 日志格式:

evlog 字段OTLP 字段
levelseverityNumber / severityText
timestamptimeUnixNano
service资源属性 service.name
environment资源属性 deployment.environment
version资源属性 service.version
region资源属性 cloud.region
traceIdtraceId
spanIdspanId
所有其他字段日志属性

广泛事件中的每个字段都会作为 OTLP 记录字段、资源属性或日志属性发送。nullundefined 会被省略,而不会传输。json 仅在顶层删除它们,compact 则会在每一层删除它们,因此嵌套的 { user: { id: null } } 不会生成 user.id 属性。

记录形状

recordShape 控制记录承载事件的方式。默认值为 json

{
  "body": { "stringValue": "{\"timestamp\":\"\",\"method\":\"POST\",\"user\":{\"id\":\"usr_123\"}}" },
  "attributes": [
    { "key": "method", "value": { "stringValue": "POST" } },
    { "key": "user", "value": { "stringValue": "{\"id\":\"usr_123\",\"plan\":\"premium\"}" } }
  ]
}

当你的后端按摄取量收费或对属性建立分面时,值得切换到 compact

  • 正文是一行摘要POST /api/checkout (500),如果无法生成则回退到服务名称,而不是在属性旁边重复整个事件。将消息聚类成模板的后端只有在正文稳定时才能做到这一点。
  • 嵌套字段会变成点号分隔的属性,因此每个叶字段都是自己的分面:
    server/api/checkout.post.ts
    const drain = createOTLPDrain({ recordShape: 'compact' })
    
    log.set({ user: { id: 'usr_123', plan: 'premium' } })
    // → user.id, user.plan
    

只有普通对象会被遍历。数组会被序列化为单个 JSON 字符串。对数组建立索引,例如 ai.tools.0.name,会将列表变成一组无界的不同属性键,而大多数后端都会对此收费,并且没有任何后端可以将其制成图表。其他非普通对象也是如此,例如 Date。空对象会保留为单个 {} 属性,而不是消失。

下一个 major 版本中,compact 将成为默认值。如果你现在正在设置项目,请尽早切换。之后再迁移意味着需要重写基于 json 形状构建的查询。

严重性映射

evlog 级别OTLP 严重性编号OTLP 严重性文本
debug5DEBUG
info9INFO
warn13WARN
error17ERROR

故障排除

缺少端点错误

控制台
[evlog/otlp] 缺少端点。请设置 OTLP_ENDPOINT 或 OTEL_EXPORTER_OTLP_ENDPOINT

请确保设置了端点环境变量并且服务器已重新启动。

401 未授权

您的身份验证标头可能缺失或无效。请检查:

  1. OTEL_EXPORTER_OTLP_HEADERS 格式是否正确
  2. 凭据是否有效且未过期
  3. 端点 URL 是否正确

404 未找到

适配器发送到 /v1/logs。请确保您的端点:

  • 支持 OTLP HTTP(而非 gRPC)
  • 是基础 URL,不包含 /v1/logs 后缀

日志未显示

  1. 检查服务器控制台是否有 [evlog/otlp] 错误消息
  2. 首先使用本地收集器测试以验证格式
  3. 检查后端的摄取延迟(某些后端有 1-2 分钟的延迟)。

直接 API 用法

对于高级用例:

server/utils/otlp.ts
import { sendToOTLP, sendBatchToOTLP, toOTLPLogRecord } from 'evlog/otlp'

// 发送单个事件
await sendToOTLP(event, {
  endpoint: 'http://localhost:4318',
})

// 发送多个事件
await sendBatchToOTLP(events, {
  endpoint: 'http://localhost:4318',
})

// 将事件转换为 OTLP 格式(用于检查)
const otlpRecord = toOTLPLogRecord(event)

下一步