mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-25 08:45:09 +00:00
Documents the structured-logs work shipped in crewAI-enterprise PR #1195 and ships the customer-facing Datadog dashboard the CON-249 self-hosted observability ask called out for. - docs/edge/en/enterprise/guides/structured_logs.mdx: schema v1 reference, opt-in env var (CREWAI_LOG_FORMAT=json), before/after JSON example, compatibility contract. Backend-agnostic — usable for Splunk, Loki, ELK, CloudWatch as well. - docs/edge/en/enterprise/guides/datadog_dashboard.mdx: two ingestion paths (Datadog Agent stdout vs Datadog OTLP intake) for self-hosted customers to pick from, facet-promotion prerequisites, 3-step dashboard import, dashboard tour, customization tips, troubleshooting. - docs/edge/en/enterprise/guides/datadog_dashboard.json: the importable dashboard artifact itself — 4 sections (Header / Throughput / Errors / Cost) with template variables wired to @automation_name, @crewai_version, and service. - docs/edge/en/enterprise/guides/capture_telemetry_logs.mdx: clarify that the default Datadog OTel template ships traces only and link to the new log-export options (Structured Logs + Datadog Dashboard). - docs/docs.json: register both new pages in the edge/en sidebar alongside capture_telemetry_logs. Version snapshots (v1.x.x) and non-English locales deliberately untouched — new content lives only on the edge channel; translation stubs land in a follow-up PR. Co-authored-by: Cursor <cursoragent@cursor.com>
143 lines
7.6 KiB
Plaintext
143 lines
7.6 KiB
Plaintext
---
|
|
title: "Structured JSON Logs"
|
|
description: "Emit single-line JSON log events from CrewAI AMP deployments for cheaper, structured ingestion in Datadog, Splunk, Loki, and other log backends."
|
|
icon: "brackets-curly"
|
|
mode: "wide"
|
|
---
|
|
|
|
CrewAI AMP can emit one JSON object per log event on stdout instead of the default multi-line text format. Each event ships with typed context fields (automation, kickoff, execution, trace IDs, exception details), making logs cheaper to index, easier to search, and trivially correlatable with traces.
|
|
|
|
This page describes the JSON schema, how to enable it, and how to verify it's working. For a ready-made Datadog dashboard built on top of these fields, see [Datadog Dashboard for crewAI](/en/enterprise/guides/datadog_dashboard).
|
|
|
|
## Why use JSON output
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Lower ingestion cost" icon="dollar-sign">
|
|
Most managed log backends bill per event. A Python traceback in text format is counted as one event per line — 30+ events for a single error. JSON output collapses each traceback into a single event with the stack trace as an escaped string field.
|
|
</Card>
|
|
<Card title="Structured search" icon="magnifying-glass">
|
|
Search by `@automation_id`, `@exception.type`, `@kickoff_id` instead of grepping free-text. Build dashboards on typed facets without parser configuration.
|
|
</Card>
|
|
<Card title="APM ↔ logs correlation" icon="link">
|
|
Every event carries `trace_id` and `span_id` when fired inside a recording span, so backends auto-link logs to traces.
|
|
</Card>
|
|
<Card title="Backend agnostic" icon="server">
|
|
The format is plain JSON — Datadog, Splunk, Loki, Elasticsearch, and CloudWatch all parse it natively without custom log pipelines.
|
|
</Card>
|
|
</CardGroup>
|
|
|
|
## Enabling JSON output
|
|
|
|
Set the `CREWAI_LOG_FORMAT` environment variable to `json` on every container that runs your deployment (API + workers).
|
|
|
|
```shell
|
|
CREWAI_LOG_FORMAT=json
|
|
```
|
|
|
|
Restart the deployment to pick up the change. Every log line on stdout from that point on is a single JSON object.
|
|
|
|
<Note>
|
|
The default value is `text`, which preserves the legacy human-readable line format byte-for-byte. Setting any value other than `json` falls back to text mode. There is no migration step — the variable is read at process start and the format switches immediately.
|
|
</Note>
|
|
|
|
## What a log event looks like
|
|
|
|
A single info-level log inside an active automation kickoff:
|
|
|
|
```json
|
|
{
|
|
"schema": "v1",
|
|
"ts": "2026-06-17T16:14:23.482914Z",
|
|
"level": "INFO",
|
|
"logger": "crewai_enterprise.utilities.pii_redaction",
|
|
"crewai_version": "1.14.7",
|
|
"msg": "PII tracking state reset (engines preserved)",
|
|
"automation_id": "12",
|
|
"task_id": "0843a930-b306-464b-89c8-bfafa78cc711",
|
|
"kickoff_id": "0843a930-b306-464b-89c8-bfafa78cc711",
|
|
"execution_id": "0843a930-b306-464b-89c8-bfafa78cc711",
|
|
"automation_name": "research_flow"
|
|
}
|
|
```
|
|
|
|
An error with a Python exception is collapsed into a single event with the traceback as a string:
|
|
|
|
```json
|
|
{
|
|
"schema": "v1",
|
|
"ts": "2026-06-17T16:14:31.218450Z",
|
|
"level": "ERROR",
|
|
"logger": "api.tasks.flow_run_task",
|
|
"crewai_version": "1.14.7",
|
|
"msg": "Flow execution failed",
|
|
"automation_id": "12",
|
|
"kickoff_id": "0843a930-b306-464b-89c8-bfafa78cc711",
|
|
"execution_id": "0843a930-b306-464b-89c8-bfafa78cc711",
|
|
"automation_name": "research_flow",
|
|
"exception": {
|
|
"type": "ValueError",
|
|
"message": "Topic cannot be empty",
|
|
"stacktrace": "Traceback (most recent call last):\n File \"/app/flow.py\", line 42, in summarize\n ...\nValueError: Topic cannot be empty\n"
|
|
}
|
|
}
|
|
```
|
|
|
|
The same error in legacy text mode would have produced ~25 separate log events (one per traceback line) — all of which the backend would bill and index individually.
|
|
|
|
## Schema v1 field reference
|
|
|
|
Within the `v1` schema, fields are only added, never renamed or removed. New fields will appear as soon as a deployment is upgraded.
|
|
|
|
| Field | Type | Always present | Source |
|
|
|-------|------|----------------|--------|
|
|
| `schema` | string | Yes | Constant `"v1"`. Increment indicates a breaking schema change. |
|
|
| `ts` | string (ISO-8601 UTC, microseconds) | Yes | Record creation time, e.g. `2026-06-17T16:14:23.482914Z`. |
|
|
| `level` | string | Yes | Python log level name: `DEBUG` / `INFO` / `WARNING` / `ERROR` / `CRITICAL`. |
|
|
| `logger` | string | Yes | Dotted logger name, e.g. `api.tasks.flow_run_task`. |
|
|
| `crewai_version` | string | Yes (when `crewai` package metadata is resolvable) | Installed `crewai` package version, e.g. `"1.14.7"`. |
|
|
| `msg` | string | Yes | Rendered log message (after `%`-formatting / `{}`-formatting). |
|
|
| `automation_id` | string | When `CREWAI_PLUS_ID` env var is set | Numeric deployment ID (AMP provisions this on every container). |
|
|
| `task_id` | string | On Celery worker logs | Celery task UUID, or `"no-task"` for non-task contexts. |
|
|
| `kickoff_id` | string | Inside an automation kickoff | UUID of the current kickoff. |
|
|
| `execution_id` | string | Inside an automation kickoff | UUID of the current sub-execution. Equal to `kickoff_id` at the top level; differs for nested flow methods that spawn sub-executions. |
|
|
| `automation_name` | string | Inside an automation kickoff | Human-readable automation/flow name, e.g. `"research_flow"`. |
|
|
| `trace_id` | string (32-hex) | Inside a recording OpenTelemetry span | Hex trace ID. Omitted when no span is active. |
|
|
| `span_id` | string (16-hex) | Inside a recording OpenTelemetry span | Hex span ID. Omitted when no span is active. |
|
|
| `exception` | object | When the log record has `exc_info` | `{type, message, stacktrace}` — full traceback as a single escaped string. |
|
|
|
|
<Tip>
|
|
Any additional `extra={...}` kwargs passed to a logger call appear as top-level JSON fields verbatim. Reserved field names above always win to keep the schema stable.
|
|
</Tip>
|
|
|
|
## Verifying it's working
|
|
|
|
After enabling the env var and restarting, fetch the latest container logs and confirm each line is a single JSON object:
|
|
|
|
```shell
|
|
# Example: docker logs <api-container> --tail 10
|
|
docker logs $(docker ps -qf name=crewai-api) --tail 10 | jq -r '.msg'
|
|
```
|
|
|
|
If the output is JSON, each line will parse successfully and `jq` will print only the `msg` field. If you see "parse error", the env var didn't take effect — confirm it's set in the running container and that the deployment was restarted after the change.
|
|
|
|
## Compatibility and versioning
|
|
|
|
The `schema` field declares the contract. Within `v1`, CrewAI commits to:
|
|
|
|
- **Never removing a field** that customers may have built queries or dashboards against.
|
|
- **Never renaming a field** in place — renames happen via a schema bump (e.g. `v2`), with the old name kept as a deprecated alias for at least one release cycle.
|
|
- **Adding new fields** at any time. Consumers should ignore unknown top-level keys.
|
|
|
|
When a `v2` is introduced, both the `schema` field and the migration guide will be published in advance, and `v1` will continue to be emitted for one release cycle so dashboards and queries have time to migrate.
|
|
|
|
## What's next
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Datadog Dashboard for crewAI" icon="dog" href="/en/enterprise/guides/datadog_dashboard">
|
|
Import a ready-made operations dashboard built on these facets — executions, errors, token cost, version distribution. Works with both the Datadog Agent and Datadog's OTLP intake.
|
|
</Card>
|
|
<Card title="OpenTelemetry Export" icon="magnifying-glass-chart" href="/en/enterprise/guides/capture_telemetry_logs">
|
|
Ship logs and traces to your own OTel collector or directly to a backend's OTLP intake. The same context fields land as OTLP attributes, so the dashboard works regardless of which path you use.
|
|
</Card>
|
|
</CardGroup>
|