---
title: "Datadog 适配器"
description: "通过原生的 HTTP 摄取 API 将广泛事件发送到 Datadog 日志。支持所有 Datadog 站点和 DD_* 环境变量。"
canonical_url: "https://evlog.zhcndoc.com/integrate/adapters/cloud/datadog"
---
# Datadog 适配器

> 通过原生的 HTTP 摄取 API 将广泛事件发送到 Datadog 日志。支持所有 Datadog 站点和 DD_* 环境变量。

[Datadog](https://www.datadoghq.com) 是一个监控和安全平台。evlog Datadog 适配器使用 **HTTP 日志摄入 API (v2)** 和 `DD-API-KEY` 标头，将你的广泛事件发送到 [Datadog 日志](https://docs.datadoghq.com/logs/)。

如果要改用基于 OpenTelemetry 的摄入方式，请参阅 [OTLP 适配器](https://evlog.zhcndoc.com/integrate/adapters/hybrid/otlp)。

<prompt :actions="["copy","cursor","claude"]" description="添加 Datadog 排水适配器" icon="i-simple-icons-datadog">

添加 Datadog 排水适配器，将 evlog 广泛事件发送到 Datadog 日志。

1. 确认我使用的框架并遵循其 evlog 集成模式
2. 如果尚未安装 evlog，请安装它
3. 从 'evlog/datadog' 导入 createDatadogDrain
4. 将 createDatadogDrain() 集成到框架的排水配置中
5. 在 .env 中设置 DD_API_KEY（或 DATADOG_API_KEY），并可选设置 DD_SITE
6. 通过触发请求进行测试，并在 Datadog 的日志资源管理器中查看结果

适配器文档：[https://www.evlog.dev/integrate/adapters/cloud/datadog](https://www.evlog.dev/integrate/adapters/cloud/datadog)
框架设置：[https://www.evlog.dev/integrate/frameworks/overview](https://www.evlog.dev/integrate/frameworks/overview)

</prompt>

## 安装

Datadog 适配器与 evlog 捆绑提供：

```typescript [src/index.ts]
import { createDatadogDrain } from 'evlog/datadog'
```

## 快速开始

### 1. 获取 API 密钥

1. 打开 [Datadog 组织设置 → API 密钥](https://app.datadoghq.com/organization-settings/api-keys)
2. 创建或复制一个具有提交日志权限的 API 密钥

### 2. 设置环境变量

```bash [.env]
DD_API_KEY=your-api-key
# 可选 — 默认为 datadoghq.com (US1)
DD_SITE=datadoghq.eu
```

### 3. 将排水集成到框架中

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog-drain.ts
import { createDatadogDrain } from 'evlog/datadog'

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

```typescript [Nitro]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createDatadogDrain } from 'evlog/datadog'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createDatadogDrain())
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'
import { createDatadogDrain } from 'evlog/datadog'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  drain: createDatadogDrain(),
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'
import { createDatadogDrain } from 'evlog/datadog'

export const { handle, handleError } = createEvlogHooks({ drain: createDatadogDrain() })
```

```typescript [TanStack Start]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createDatadogDrain } from 'evlog/datadog'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createDatadogDrain())
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'
import { createDatadogDrain } from 'evlog/datadog'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({ drain: createDatadogDrain() }),
]
```

```typescript [Hono]src/index.ts
import { createDatadogDrain } from 'evlog/datadog'

app.use(evlog({ drain: createDatadogDrain() }))
```

```typescript [Express]src/index.ts
import { createDatadogDrain } from 'evlog/datadog'

app.use(evlog({ drain: createDatadogDrain() }))
```

```typescript [Fastify]src/index.ts
import { createDatadogDrain } from 'evlog/datadog'

await app.register(evlog, { drain: createDatadogDrain() })
```

```typescript [Elysia]src/index.ts
import { createDatadogDrain } from 'evlog/datadog'

app.use(evlog({ drain: createDatadogDrain() }))
```

```typescript [NestJS]src/app.module.ts
import { createDatadogDrain } from 'evlog/datadog'

EvlogModule.forRoot({ drain: createDatadogDrain() })
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'
import { createDatadogDrain } from 'evlog/datadog'

const handler = withEvlog(new RPCHandler(router), { drain: createDatadogDrain() })
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'
import { createDatadogDrain } from 'evlog/datadog'

export default withEvlog(handler, { drain: createDatadogDrain() })
```

```typescript [Standalone]
import { createDatadogDrain } from 'evlog/datadog'

initLogger({ drain: createDatadogDrain() })
```

</framework-tabs>

广泛事件将显示在 **日志 → 资源管理器** 中。适配器将 `ddsource` 设置为 `evlog`，并将 `message` 设置为完整广泛事件的 JSON 字符串，以便在管道中轻松解析 JSON。

## 配置

适配器从多个来源读取配置（优先级从高到低）：

1. **传递给 createDatadogDrain() 的覆盖选项**
2. **runtimeConfig.datadog 或 runtimeConfig.evlog.datadog 中的运行时配置**（Nuxt/Nitro）
3. **环境变量**：请参阅下表

### 环境变量

<table>
<thead>
  <tr>
    <th>
      变量
    </th>
    
    <th>
      描述
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        DD_API_KEY
      </code>
    </td>
    
    <td>
      Datadog API 密钥（必填）。另见：<code>
        DATADOG_API_KEY
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DD_SITE
      </code>
    </td>
    
    <td>
      站点主机名（例如 <code>
        datadoghq.com
      </code>
      
      、<code>
        datadoghq.eu
      </code>
      
      、<code>
        us3.datadoghq.com
      </code>
      
      ）。另见：<code>
        DATADOG_SITE
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DATADOG_LOGS_URL
      </code>
    </td>
    
    <td>
      完整接入 URL — 会覆盖从 <code>
        site
      </code>
      
       派生的 URL
    </td>
  </tr>
</tbody>
</table>

### 运行时配置（仅限 Nuxt）

```typescript [nuxt.config.ts]
export default defineNuxtConfig({
  runtimeConfig: {
    datadog: {
      apiKey: '', // 通过 DD_API_KEY 或 DATADOG_API_KEY 设置
      site: 'datadoghq.eu',
    },
  },
})
```

### 覆盖选项

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog-drain.ts
import { createDatadogDrain } from 'evlog/datadog'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }))
})
```

```typescript [Nitro]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createDatadogDrain } from 'evlog/datadog'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }))
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'
import { createDatadogDrain } from 'evlog/datadog'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'
import { createDatadogDrain } from 'evlog/datadog'

export const { handle, handleError } = createEvlogHooks({
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
})
```

```typescript [TanStack Start]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createDatadogDrain } from 'evlog/datadog'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }))
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'
import { createDatadogDrain } from 'evlog/datadog'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({
      drain: createDatadogDrain({
        apiKey: '***',
        site: 'us5.datadoghq.com',
        timeout: 10000,
      }),
    }),
]
```

```typescript [Hono]src/index.ts
import { evlog } from 'evlog/hono'
import { createDatadogDrain } from 'evlog/datadog'

app.use(evlog({
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
}))
```

```typescript [Express]src/index.ts
import { evlog } from 'evlog/express'
import { createDatadogDrain } from 'evlog/datadog'

app.use(evlog({
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
}))
```

```typescript [Fastify]src/index.ts
import { evlog } from 'evlog/fastify'
import { createDatadogDrain } from 'evlog/datadog'

await app.register(evlog, {
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
})
```

```typescript [Elysia]src/index.ts
import { evlog } from 'evlog/elysia'
import { createDatadogDrain } from 'evlog/datadog'

app.use(evlog({
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
}))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'
import { createDatadogDrain } from 'evlog/datadog'

EvlogModule.forRoot({
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
})
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'
import { createDatadogDrain } from 'evlog/datadog'

const handler = withEvlog(new RPCHandler(router), {
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
})
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'
import { createDatadogDrain } from 'evlog/datadog'

export default withEvlog(handler, {
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
})
```

```typescript [Standalone]src/index.ts
import { initLogger } from 'evlog'
import { createDatadogDrain } from 'evlog/datadog'

initLogger({
  drain: createDatadogDrain({
    apiKey: '***',
    site: 'us5.datadoghq.com',
    timeout: 10000,
  }),
})
```

</framework-tabs>

### 完整配置参考

<table>
<thead>
  <tr>
    <th>
      选项
    </th>
    
    <th>
      类型
    </th>
    
    <th>
      默认值
    </th>
    
    <th>
      描述
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        apiKey
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Datadog API 密钥（必需）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        site
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        datadoghq.com
      </code>
    </td>
    
    <td>
      摄入主机使用的站点，格式为 <code>
        http-intake.logs.${site}
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intakeUrl
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      从 <code>
        site
      </code>
      
       推导
    </td>
    
    <td>
      <code>
        /api/v2/logs
      </code>
      
       的完整 POST URL
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        timeout
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5000
      </code>
    </td>
    
    <td>
      请求超时时间（毫秒）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        retries
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      在暂时性失败时的重试次数
    </td>
  </tr>
</tbody>
</table>

## 日志结构

每个广泛事件都会转换为一条 Datadog 日志，包含以下字段：

- **message**：列表视图中的简短单行摘要（例如 `ERROR GET /api/checkout (400)`），通过 `formatDatadogMessageLine` 构建。在 Live Tail 中比完整的 JSON 数据块更易于浏览
- **evlog**：完整的广泛事件，作为 **JSON 对象**（而非字符串）。树中任何位置的数字 HTTP **status** 字段都会重命名为 **httpStatusCode**，因此不会与 Datadog 保留的严重性 `status` 冲突
- **dd**：当事件携带链路上下文时为 `{ trace_id, span_id }`。请参阅[链路关联](#trace-correlation)
- **service**、**status**（Datadog 严重性，用于决定 Live Tail 颜色）、**ddsource**：`evlog`、**ddtags**：`env:…` 和可选的 `version:…`
- **timestamp**：来自 `WideEvent.timestamp` 的 Unix 毫秒时间戳

**摄入根级别的严重性（status）** 由适配器根据广泛事件的 **level** 和 HTTP **status** 计算得出（参见 `evlog/datadog` 中的 `resolveDatadogLogStatus`）。HTTP 200 对应的业务字段仍为 **info**，除非显式调用 **log.error()**。

如需高级用法，`sanitizeWideEventForDatadog(event)` 返回的即为要存储在 `evlog` 下的清洗后对象。

## 链路关联

Datadog 通过负载**根级别**的保留属性 **dd.trace_id** / **dd.span_id** 将日志关联到链路。嵌套副本（`@evlog.traceId`）可供搜索，但不会自行建立关联。这需要在 Datadog 日志管道中使用 [Trace Id Remapper](https://docs.datadoghq.com/logs/log_configuration/processors/trace_remapper/)，其配置位于代码库之外。适配器会将 `event.traceId` 和 `event.spanId` 提升到根级别的 `dd` 块中，因此无需设置管道即可实现关联：

```json
{
  "message": "ERROR GET /api/checkout (400)",
  "evlog": { "traceId": "4bf92f35…", "spanId": "00f067aa…", "…": "full wide event" },
  "dd": { "trace_id": "4bf92f35…", "span_id": "00f067aa…" },
  "service": "my-app",
  "status": "error",
  "ddsource": "evlog"
}
```

`evlog` 下的嵌套副本会保留，因此现有的 `@evlog.*` facets 和仪表板仍可继续使用。只有**非空字符串**才会被提升，因此空值或非字符串的 `traceId` / `spanId` 会被跳过，而不会作为 Datadog 无法解析的 id 发送；当两个 id 都未通过检查时，则不会存在 `dd` 键。

这些字段由 [`createDefaultEnrichers()`](https://evlog.zhcndoc.com/use-cases/enrichers) 中包含的 `createTraceContextEnricher` 填充，它会将传入的 W3C `traceparent` 标头解析为 `event.traceId`。该标头中的 span id 属于调用方，因此会写入 `event.parentSpanId`；只有其他来源提供了 `event.spanId` 时，才会设置 `dd.span_id`：

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog.ts
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()

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

```typescript [Nitro]server/plugins/evlog.ts
import { definePlugin } from 'nitro'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:enrich', enrich)
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  enrich: enrich,
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
export const { handle, handleError } = createEvlogHooks({ enrich: enrich })
```

```typescript [TanStack Start]server/plugins/evlog.ts
import { definePlugin } from 'nitro'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:enrich', enrich)
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
export const middleware: Route.MiddlewareFunction[] = [
  evlog({ enrich: enrich }),
]
```

```typescript [Hono]src/index.ts
import { evlog } from 'evlog/hono'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
app.use(evlog({ enrich: enrich }))
```

```typescript [Express]src/index.ts
import { evlog } from 'evlog/express'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
app.use(evlog({ enrich: enrich }))
```

```typescript [Fastify]src/index.ts
import { evlog } from 'evlog/fastify'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
await app.register(evlog, { enrich: enrich })
```

```typescript [Elysia]src/index.ts
import { evlog } from 'evlog/elysia'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
app.use(evlog({ enrich: enrich }))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
EvlogModule.forRoot({ enrich: enrich })
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
const handler = withEvlog(new RPCHandler(router), { enrich: enrich })
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
export default withEvlog(handler, { enrich: enrich })
```

```typescript [Standalone]src/index.ts
import { initLogger } from 'evlog'
import { createDefaultEnrichers } from 'evlog/enrichers'

const enrich = createDefaultEnrichers()
initLogger({ enrich: enrich })
```

</framework-tabs>

如果你的 id 来自其他地方（例如 tracer SDK 或供应商标头），可以自行设置 `event.traceId` / `event.spanId`，适配器会以相同方式获取它们：

```typescript
const span = tracer.scope().active()?.context()
log.set({ traceId: span?.toTraceId(), spanId: span?.toSpanId() })
```

如果你需要在自定义 drain 中使用相同的映射，可以从 `evlog/datadog` 导出 `resolveDatadogTraceContext(event)`。

## 在 Datadog 中查询

- **日志资源管理器**：`source:evlog`、`service:your-app`、`status:error`
- **Facets**：优先使用 `@evlog.path`、`@evlog.requestId`、`@evlog.level` 等。核心字段位于 **evlog** 下，而不是 `message` 中的 JSON 字符串
- **指标**：基于 `@evlog.*` 属性创建日志指标
- **管道**：如果你之前解析了 `message` 中的完整 JSON **字符串**，请将这些 facets 改为使用 `@evlog.*`。`message` 字段现在仅包含简短的摘要行

## 简单日志与广泛事件

实时追踪中的纯文本行（例如 “Form field is empty”）通常来自 **log.info('tag', 'msg')**，而非通过 **emit()** 发送的广泛事件。这些行输出到控制台（以及任何基于 Agent 的日志流），而 Datadog 排水器会为每个广泛事件发送一条结构化日志，来源为 **source:evlog**。

## 故障排除

### 缺少 API 密钥

```text [Console]
[evlog/datadog] Missing API key. Set DATADOG_API_KEY, DD_API_KEY...
```

设置 `DD_API_KEY`（或无前缀的 `DATADOG_API_KEY`）并重启进程。

### 403 禁止访问

API 密钥可能缺少日志写入权限或属于错误的组织。请在 Datadog 中验证密钥并尝试使用新密钥。

### 区域/站点错误

如果日志始终没有出现，请确认 `DD_SITE` 与你的 Datadog 账户匹配（例如，EU：`datadoghq.eu`）。对于自定义接收端 URL，请设置 `DATADOG_LOGS_URL`。

## 直接 API 使用

```typescript [server/utils/datadog.ts]
import { sendToDatadog, sendBatchToDatadog } from 'evlog/datadog'

await sendToDatadog(event, {
  apiKey: process.env.DD_API_KEY!,
  site: process.env.DD_SITE,
})

await sendBatchToDatadog(events, {
  apiKey: process.env.DD_API_KEY!,
})
```

## 下一步

- [OTLP 适配器](https://evlog.zhcndoc.com/integrate/adapters/hybrid/otlp)：通过 OpenTelemetry 发送日志（适用于 Datadog Agent / OTLP 端点）
- [自定义适配器](https://evlog.zhcndoc.com/extend/custom-drains)：构建你自己的目标目的地

---

- [Datadog 日志](https://docs.datadoghq.com/logs/)
- [OTLP 适配器](https://evlog.zhcndoc.com/integrate/adapters/hybrid/otlp)


## Sitemap

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