Datadog Adapter
Datadog is a monitoring and security platform. The evlog Datadog adapter sends your wide events to Datadog Logs using the HTTP Logs intake API (v2) with the DD-API-KEY header.
For OpenTelemetry-based ingestion instead, see the OTLP adapter.
Add the Datadog drain adapter to send evlog wide events to Datadog Logs.
1. Identify which framework I'm using and follow its evlog integration pattern
2. Install evlog if not already installed
3. Import createDatadogDrain from 'evlog/datadog'
4. Wire createDatadogDrain() into my framework's drain configuration
5. Set DD_API_KEY (or DATADOG_API_KEY) and optionally DD_SITE in .env
6. Test by triggering a request and checking Log Explorer in Datadog
Adapter docs: https://www.evlog.dev/adapters/datadog
Framework setup: https://www.evlog.dev/frameworks
Installation
The Datadog adapter comes bundled with evlog:
import { createDatadogDrain } from 'evlog/datadog'
Quick Start
1. Get your API key
- Open Datadog Organization Settings → API Keys
- Create or copy an API key with permission to submit logs
2. Set environment variables
DD_API_KEY=your-api-key
# Optional — defaults to datadoghq.com (US1)
DD_SITE=datadoghq.eu
3. Wire the drain to your framework
// server/plugins/evlog-drain.ts
import { createDatadogDrain } from 'evlog/datadog'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('evlog:drain', createDatadogDrain())
})
import { createDatadogDrain } from 'evlog/datadog'
app.use(evlog({ drain: createDatadogDrain() }))
import { createDatadogDrain } from 'evlog/datadog'
app.use(evlog({ drain: createDatadogDrain() }))
import { createDatadogDrain } from 'evlog/datadog'
await app.register(evlog, { drain: createDatadogDrain() })
import { createDatadogDrain } from 'evlog/datadog'
app.use(evlog({ drain: createDatadogDrain() }))
import { createDatadogDrain } from 'evlog/datadog'
EvlogModule.forRoot({ drain: createDatadogDrain() })
import { createDatadogDrain } from 'evlog/datadog'
initLogger({ drain: createDatadogDrain() })
Wide events appear in Logs → Explorer. The adapter sets ddsource to evlog and message to a JSON string of the full wide event for easy JSON parsing in pipelines.
Configuration
The adapter reads configuration from multiple sources (highest priority first):
- Overrides passed to
createDatadogDrain() - Runtime config at
runtimeConfig.datadogorruntimeConfig.evlog.datadog(Nuxt/Nitro) - Environment variables — see table below
Environment Variables
| Variable | Nuxt alias | Description |
|---|---|---|
DD_API_KEY | NUXT_DATADOG_API_KEY | Datadog API key (required). Also: DATADOG_API_KEY |
DD_SITE | NUXT_DATADOG_SITE | Site hostname (e.g. datadoghq.com, datadoghq.eu, us3.datadoghq.com). Also: DATADOG_SITE |
DATADOG_LOGS_URL | NUXT_DATADOG_LOGS_URL | Full intake URL — overrides URL derived from site |
Runtime Config (Nuxt only)
export default defineNuxtConfig({
runtimeConfig: {
datadog: {
apiKey: '', // Set via NUXT_DATADOG_API_KEY or DD_API_KEY
site: 'datadoghq.eu',
},
},
})
Override Options
const drain = createDatadogDrain({
apiKey: '***',
site: 'us5.datadoghq.com',
timeout: 10000,
})
Full Configuration Reference
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Datadog API key (required) |
site | string | datadoghq.com | Site for intake host http-intake.logs.${site} |
intakeUrl | string | from site | Full POST URL for /api/v2/logs |
timeout | number | 5000 | Request timeout (ms) |
retries | number | 2 | Retries on transient failures |
Log shape
Each wide event becomes one Datadog log with:
message— short one-line summary for the list view (e.g.ERROR GET /api/checkout (400)), built withformatDatadogMessageLine. Easier to scan than a full JSON blob in Live Tail.evlog— full wide event as a JSON object (not a string). Numeric HTTPstatusfields anywhere in the tree are renamed tohttpStatusCodeso they never clash with Datadog’s reserved severitystatus.service,status(Datadog severity — drives Live Tail color),ddsource:evlog,ddtags:env:…and optionalversion:…timestamp: Unix milliseconds fromWideEvent.timestamp
Severity (status) at intake root is computed by the adapter from the wide event’s level and HTTP status (resolveDatadogLogStatus in evlog/datadog). Business-only fields on HTTP 200 stay info unless you call log.error().
For advanced use, sanitizeWideEventForDatadog(event) returns only the sanitized object you would store under evlog.
Querying in Datadog
- Log Explorer:
source:evlog,service:your-app,status:error - Facets: prefer
@evlog.path,@evlog.requestId,@evlog.level, etc. — core fields are underevlog, not a JSON string inmessage - Metrics: log-based metrics on
@evlog.*attributes - Pipelines: if you previously parsed a full JSON string inside
message, move those facets to@evlog.*. Themessagefield is now a short summary line only.
Simple logs vs wide events
Plain-text lines in Live Tail (e.g. “Form field is empty”) usually come from log.info('tag', 'msg') or similar, not from the wide event sent on emit(). Those lines go to the console (and any Agent-based log stream), while the Datadog drain sends one structured log per wide event under source:evlog.
Troubleshooting
Missing API key
[evlog/datadog] Missing API key. Set NUXT_DATADOG_API_KEY, DATADOG_API_KEY, or DD_API_KEY...
Set DD_API_KEY (or unprefixed DATADOG_API_KEY) and restart the process.
403 Forbidden
The API key may lack log ingestion permission or belong to the wrong organization. Verify the key in Datadog and try a new key.
Wrong region / site
If logs never appear, confirm DD_SITE matches your Datadog account (e.g. EU: datadoghq.eu). For a custom intake URL, set DATADOG_LOGS_URL / NUXT_DATADOG_LOGS_URL.
Direct API usage
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!,
})
Next Steps
- OTLP Adapter — Send logs via OpenTelemetry (works with Datadog Agent / OTLP endpoint)
- Custom Adapters — Build your own destination