[Feature] OpenTelemetry GenAI exporter for Agent Traces (Phase 1: Java, spans-only)
- Dominant language
- Java
- Stars
- 452
- Forks
- 167
- Avg merge
- 5d 9h
- Merged PRs (30d)
- 49
Description
### Search before asking
* [x] I searched in the [[issues](https://github.com/apache/flink-agents/issues)](https://github.com/apache/flink-agents/issues) and found nothing similar.
### Description
Tracking issue for Phase 1 of the OTel exporter discussed in #929, as a follow-up to #900 and the Execution Event recording work in #924.
Scope (Phase 1):
* A **batch, offline converter** over already-collected Event Log files (`event-log.trace.enabled: true`). It maps Execution Events to OpenTelemetry GenAI spans: one `inputRunId` becomes one trace with a synthesized `invoke_agent` root, and the recorded execution hierarchy defines the rest of the span tree. Records are grouped by `inputRunId`, sorted by timestamp, and end-of-input marks completion.
* Span mapping follows #929: `llm` uses CLIENT; `tool`, `action`, `parser`, and the run root use INTERNAL. `parser` maps to `parse {name}` with `gen_ai.operation.name: "parse"`.
* Incomplete executions are still exported rather than dropped. A start without a terminal becomes a zero-duration span with status UNSET and `flink_agents.execution.incomplete = true`, plus an `INCOMPLETE_EXECUTION` diagnostic. The inverse case uses `MISSING_START`.
* Diagnostics follow the existing `trace_tree.py` shape (`code`, id, message, file location), including `MALFORMED_RECORD` for invalid input.
* The input contract is the serialized JSON Event Log record, not a particular file name. Explicit files are read as-is, unknown fields are ignored, and directory inputs may discover `events-*.log` files for convenience. For multi-subtask jobs, completeness of the supplied file set remains the caller's responsibility.
* Framework IDs are deterministically mapped to fixed-width OTel trace/span IDs so repeated exports produce the same IDs. Delivery remains at-least-once.
* Configuration is provided through converter arguments such as endpoint, protocol, and `service.name`. Content capture is excluded from Phase 1.
* Delivered as an **optional standalone module, not bundled into dist**, with OTel dependencies aligned through the `opentelemetry-bom`.
* Because the serialized Event Log format is shared across runtimes, the same converter works for both Java and Python agents.
Out of scope, tracked separately:
* A durable/replayable Event Log sink and a continuous OTel consumer with checkpoint/replay semantics.
* Content capture and richer usage metadata, pending the `entityMetadata` work discussed in #929 / #924.
* Optional OpenInference classification (`openinference.span.kind`) for backends such as Phoenix/Langfuse.
Rejected alternative (Flink's native `OpenTelemetryTraceReporterFactory`): see the note in #929.
Depends on #924 (recording contract).
### Are you willing to submit a PR?
* [x] I'm willing to submit a PR!
Contributor guide
Research direction
Start by reading the serialized JSON Event Log contract in #924 and the existing trace_tree.py diagnostic shape. Define the standalone Java converter around the issue's span, ID, grouping, and incomplete-execution rules, then verify that repeated exports are deterministic and malformed records produce the specified diagnostics.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- observability-sre
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100