Datadog HTTP intake — NDJSON ingest from MaestroHub
Datadog's Logs → HTTP intake accepts line-delimited JSON directly. Our format=ndjson output ships every field Datadog needs to build correlations without a custom parser.
1. Create an OAuth2 client
Same as Splunk setup step 1.
2. Configure the collector
We recommend a small forwarder (Vector, Fluent Bit, or your own systemd timer) rather than the Datadog Agent's built-in HTTP source — the Agent's HTTP source doesn't support opaque cursors natively.
Vector
[sources.maestrohub_audit]
type = "http_client"
endpoint = "https://<your-maestro>/api/v1/audit/export"
method = "GET"
query = { format = "ndjson", from = "${MAESTRO_LAST_CURSOR_TS}", to = "now", cursor = "${MAESTRO_LAST_CURSOR}", limit = "1000" }
scrape_interval_secs = 60
auth = { strategy = "bearer", token = "${MAESTRO_ACCESS_TOKEN}" }
decoding = { codec = "json" }
framing = { method = "newline_delimited" }
[transforms.tag_source]
type = "remap"
inputs = ["maestrohub_audit"]
source = '''
.service = "maestrohub"
.@source = "maestrohub-audit"
'''
[sinks.datadog_logs]
type = "datadog_logs"
inputs = ["tag_source"]
default_api_key = "${DATADOG_API_KEY}"
site = "datadoghq.com"
Vector persists the cursor via its state_dir — set that to a durable path.
Refresh loop
Handle both token refresh and cursor persistence in a tiny wrapper script alongside Vector — Vector doesn't manage OAuth2 refresh natively.
3. Set up Datadog service correlation
In Datadog:
- Logs → Configuration → Pipelines → New Pipeline.
- Filter:
service:maestrohub. - Add a Grok Parser processor:
%%{data::json}
- Add a Standard Attribute Remapper:
correlation_id→dd.trace_id(so audit events correlate with APM traces)actor.id→usr.id(Datadog's user-facet field)org_id→dd.tags.tenant
4. Validate
Datadog log explorer:
service:maestrohub action:role.assigned
Every event carries:
action(e.g.role.assigned)actor_id,actor_emailsubjectresource_type,resource_idorg_id,correlation_idseverity(0–10),category
Correlation with APM
When your MaestroHub deployment ships APM traces to the same Datadog account, the correlation_id on every audit event equals the OpenTelemetry trace_id on the corresponding request. In Datadog:
- From an audit event, click through to the trace.
- From an APM trace, filter logs by
dd.trace_id:*to see every audit event the request produced.
Troubleshooting
Vector logs "unexpected response body".
The audit endpoint response is line-delimited JSON, not an array. Set framing.method = "newline_delimited" in Vector's decoding — a common misconfiguration.
Datadog shows records with no correlation_id. Records emitted before UC36 shipped carry empty correlation IDs. Records emitted after ship carry the trace id iff the request had an OpenTelemetry trace context (i.e. the caller's HTTP framework enabled the OTel middleware).