feat(dev): collect local OTEL traces in project dev - #1980
Merged
Conversation
tejaskash
had a problem deploying
to
e2e-testing
August 12, 2026 14:44 — with
GitHub Actions
Failure
tejaskash
had a problem deploying
to
e2e-testing
August 12, 2026 14:47 — with
GitHub Actions
Failure
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 14, 2026 20:38
b4d0349 to
e2e696c
Compare
tejaskash
had a problem deploying
to
e2e-testing
August 14, 2026 20:39 — with
GitHub Actions
Failure
tejaskash
had a problem deploying
to
e2e-testing
August 14, 2026 20:59 — with
GitHub Actions
Failure
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 17, 2026 17:30
56c20f7 to
a59ebf1
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## refactor #1980 +/- ##
============================================
+ Coverage 97.15% 97.17% +0.02%
============================================
Files 387 389 +2
Lines 23124 23386 +262
============================================
+ Hits 22465 22726 +261
- Misses 659 660 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
2 times, most recently
from
August 17, 2026 17:53
cad8772 to
7e315b9
Compare
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 20, 2026 16:09
84429af to
d070faf
Compare
tejaskash
added a commit
that referenced
this pull request
Aug 20, 2026
Pure OTLP wire handling (per-trace batch partitioning, id normalization, frontend shaping) and append-only per-trace JSONL storage. A batch routinely carries spans from several traces, so persistence partitions by trace id — writing whole batches under the first id corrupts trace identity. Consumed by the OTLP collector in #1980, which stacks on this.
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 20, 2026 16:18
d070faf to
dd15e9e
Compare
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 21, 2026 15:23
dd15e9e to
0c0682e
Compare
tejaskash
added a commit
that referenced
this pull request
Aug 21, 2026
Pure OTLP wire handling (per-trace batch partitioning, id normalization, frontend shaping) and append-only per-trace JSONL storage. A batch routinely carries spans from several traces, so persistence partitions by trace id — writing whole batches under the first id corrupts trace identity. Consumed by the OTLP collector in #1980, which stacks on this.
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 21, 2026 15:44
be5c3d5 to
4b80241
Compare
tejaskash
added a commit
that referenced
this pull request
Aug 21, 2026
* feat(dev): OTLP trace storage for local dev Pure OTLP wire handling (per-trace batch partitioning, id normalization, frontend shaping) and append-only per-trace JSONL storage. A batch routinely carries spans from several traces, so persistence partitions by trace id — writing whole batches under the first id corrupts trace identity. Consumed by the OTLP collector in #1980, which stacks on this. * fix(dev): match trace filters against every participating service A distributed trace spans several local agents whose exports append to the same trace file; filtering by any participant must find it, not only the first service seen. * fix(dev): address review on OTLP trace storage - narrow OtlpAttributes to the key/value wire form (the flat-record variant had no producer); drop the dead passthrough branches - flatten array attributes through extractAnyValue so ints stay numeric and nested kvlists survive (was stringifying and dropping them) - count rendered (post-filter) spans for the list summary instead of raw records, so the count matches the waterfall the inspector shows - surface non-ENOENT fs errors from reads instead of masking them as empty - add newest-N limit to list() for the inspector's per-invocation poll * fix(dev): flatten kvlist-valued OTLP attributes instead of dropping them flattenAttributes hand-rolled a value branch per AnyValue kind and had no kvlistValue case, so an attribute whose value is a kvlist (or anything the chain didn't enumerate) silently vanished. Route every attribute value through extractAnyValue, which already unwraps all variants including kvlist — smaller and complete. Addresses Gitika's review on transforms.ts (reuse extractAnyValue; kvlist must not disappear).
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 21, 2026 17:51
4b80241 to
733c274
Compare
Contributor
Author
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
2 times, most recently
from
August 21, 2026 17:59
fce12fd to
27fbae3
Compare
Hweinstock
reviewed
Aug 21, 2026
tejaskash
added a commit
that referenced
this pull request
Aug 21, 2026
Harrison review on #1980: - httpServer: generalize the answer-before-close comment (drop OTLP specificity) — the module is a shared io primitive (the Inspector server reuses it). - flags: explain why a default-true boolean is exposed as --no-<name>. - collector: reword the onError comment to state the collector's guarantee (ack + hand to onError) rather than the caller's report-once behavior; drop the volatile "matches the reference CLI" aside.
notgitika
reviewed
Aug 21, 2026
aidandaly24
reviewed
Aug 21, 2026
An in-process OTLP/HTTP receiver (protobuf via the pinned otlp-transformer decoders, or JSON) persists agent traces through the storage layer. project dev starts it unless --no-traces or the runtime disables instrumentation, points every spawned agent at it (signal-specific OTEL env), rewrites the endpoint to host.docker.internal for containers — with an explicit host-gateway mapping so Linux Docker Engine resolves it — and keeps uvicorn --reload workers instrumented via sitecustomize on PYTHONPATH. Oversized collector requests get a 413 before the connection closes so exporters do not retry them as transient failures. Rebuilt from explicit paths: the previous tree-snapshot commit accidentally reverted unrelated merged work (cdk target guard, config-bundle TUI, error classification).
A batch that can't be persisted (disk full, permissions) was being turned into a 500, which the OTEL SDK exporter retries forever while the user sees nothing. Ack the export (200) so retries stop, and surface the failure once via an onError sink threaded from the collector to the dev handler, which owns the IO to warn the user. Addresses Gitika's review on store.ts:45 (catch in one place; don't let persistence faults read as a silent, retried loss).
Follow-ups from #2043 review (Gitika, Harrison): - Add a TraceStore.list spanCount test with transport-noise spans (1 agent + 4 http-send -> "1"), guarding the post-filter count. - Rename hexFromB64OrString -> hexFromBase64OrHex; both reviewers misread B64.
Harrison review on #1980: - httpServer: generalize the answer-before-close comment (drop OTLP specificity) — the module is a shared io primitive (the Inspector server reuses it). - flags: explain why a default-true boolean is exposed as --no-<name>. - collector: reword the onError comment to state the collector's guarantee (ack + hand to onError) rather than the caller's report-once behavior; drop the volatile "matches the reference CLI" aside.
…cycle Address reviewer findings on the collector and dev wiring: - Validate top-level OTLP shape and return 400 instead of mislabeling a bad payload as a persistence error. - Guard the shared HTTP server against a client that disconnects mid-response so it can no longer crash project dev; add an optional bind host. - Bind the collector to 0.0.0.0 for container runtimes so a container can reach it over the host bridge on Linux. - Run the container template under opentelemetry-instrument so it emits traces. - Keep the collector alive through the child's shutdown grace so final spans are not lost. - Force the OTEL settings that would otherwise let shell or .env.local values disable or break local collection. - Make the uv sitecustomize discovery abortable and read its path from a marker rather than the last merged output line.
tejaskash
force-pushed
the
feat/dev-otel-collector
branch
from
August 21, 2026 18:55
adf371b to
10be0a6
Compare
notgitika
approved these changes
Aug 21, 2026
aidandaly24
approved these changes
Aug 21, 2026
aidandaly24
left a comment
Contributor
There was a problem hiding this comment.
Thanks for making the fixes. This looks good to me!
tejaskash
added a commit
that referenced
this pull request
Aug 21, 2026
The #1980 rebase carried a single-runtime host check (runtime.build) into the multi-agent dev handler, where the variable is the runtimes array. Bind all interfaces when any selected runtime runs in a container.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Review after #2043
Until #2043 merges, the diff shows both PRs' commits (GitHub's stack tooling wouldn't retarget the base); the work to review here is the single collector commit on top. It collapses to just that once #2043 merges.
What this does
agentcore project devgives a local agent tracing with no extra config: an in-process OTLP/HTTP receiver starts on a loopback port, the agent's OpenTelemetry SDK is pointed at it via env vars, and traces persist through #2043's TraceStore toagentcore/.cli/traces/. Nothing leaves the machine;--no-tracesorinstrumentation.enableOtel: falseopts out.io/httpServer.ts— loopback-onlynode:httpprimitive (body caps, abort-signal close).otel/collector.ts—POST /v1/traces+/v1/logs, protobuf or JSON. Pinned@opentelemetry/otlp-transformer@0.213.0, the last version shipping request decoders — do not bump.host.docker.internalrewrite for containers; sitecustomize-on-PYTHONPATH souvicorn --reloadworkers stay instrumented (a wrapper would only instrument the reloader parent).Verification
Per-layer tests (receiver over real ephemeral-port HTTP with protobuf + JSON fixtures; handler with fake runners/collector) plus end to end: built bundle → real
project create→project dev→ real Bedrock invocation → traces on disk. Protobuf decode verified under bun, the Node bundle, and a compiled binary.