Files
crewAI/docs/edge/ko/enterprise/guides/structured_logs.mdx
Lucas Gomide 2b4ae346da docs(enterprise): register structured logs + Datadog dashboard in pt-BR/ko/ar
Adds stub MDX pages with translated frontmatter and a "translation in
progress" note in each locale. Body content is English while waiting on
full translations, matching the discoverability of every other Enterprise
guide (registered across all four edge locales).

Also translates the Datadog OTLP /v1/logs touch-up and the new cross-links
in pt-BR/ko/ar versions of capture_telemetry_logs.mdx.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 17:43:39 -03:00

147 lines
7.7 KiB
Plaintext

---
title: "구조화된 JSON 로그"
description: "CrewAI AMP 배포에서 한 줄 JSON 로그 이벤트를 내보내 Datadog, Splunk, Loki 및 기타 로그 백엔드에서 더 저렴하고 구조화된 수집을 수행하세요."
icon: "brackets-curly"
mode: "wide"
---
<Note>
**번역 진행 중** — 콘텐츠가 영어로 표시됩니다.
</Note>
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](/ko/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="/ko/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="/ko/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>