feat(node): cap attributes and events per span - #4586
Draft
turnipdabeets wants to merge 4 commits into
Draft
Conversation
Contributor
Contributor
|
Size Change: +4.21 kB (+0.02%) Total Size: 20.2 MB 📦 View Changed
ℹ️ View Unchanged
|
turnipdabeets
force-pushed
the
feat/traces-node-mvp
branch
from
August 21, 2026 12:45
a9ff420 to
5c7301c
Compare
turnipdabeets
force-pushed
the
feat/traces-span-limits
branch
from
August 21, 2026 12:45
fd9c549 to
896f06b
Compare
One runaway span could grow until the endpoint rejected it and the whole span was lost. Auto-context keys stay exempt so a capped span still links to its person and session.
A parsed __proto__ key swapped the attribute store's prototype, leaking every nested key past the cap uncounted, and reserved names like toString always read as already-present.
Storing a nullish value made every later write to that key free, so a span could exceed its cap uncounted. Nullish now removes the key, and the record hands out attributes with an ordinary prototype.
turnipdabeets
force-pushed
the
feat/traces-node-mvp
branch
from
August 24, 2026 11:25
e36495f to
9365a6c
Compare
turnipdabeets
force-pushed
the
feat/traces-span-limits
branch
from
August 24, 2026 11:25
896f06b to
a8a3011
Compare
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.
Problem
Stacked on #4579 — review that first.
Nothing bounded how much a single span could carry. A loop that calls
span.addEvent()per iteration, or an instrumentation layer that copies every request header onto a span, grows it without limit until the ingestion endpoint rejects the payload as too large — at which point the 413 path shrinks the batch to a single span, that span is still too big, and it is dropped entirely with a warning. You lose the whole span rather than the excess.Implements the
Span limitsrequirement of the traces capability spec.Changes
Caps user-supplied content per span, at OpenTelemetry's defaults.
droppedAttributesCount/droppedEventsCount, so a truncated span is visibly truncated rather than quietly wrong. Both are omitted when nothing was dropped.posthogDistinctId,sessionId,url.full,screen.nameandapp.statedo not count toward the cap, so a span at the limit still links back to its person and session — those are the join keys, and losing them to a noisy attribute loop would defeat the point of the span.setAttribute('plan', …)twice on a span at the cap updates rather than drops.startSpanare capped the same way as ones set later.Reviewer notes
beforeSpanSend, because a hook can add attributes.beforeSpanSendlives in feat(node): add a beforeSpanSend hook #4584, which is a sibling branch off the same base — neither PR can implement that half alone. Whichever merges second should wire it up; I have not tried to coordinate it inside either branch, since that would make each depend on the other.PostHogSpan, not at encode time, so an over-cap span never occupies memory in the first place — the point is to bound growth, not to trim on the way out.posthogDistinctIdthemselves gets the exempt treatment for that key. That seemed better than special-casing a hardcoded list.Verification
Six core tests covering both spec scenarios plus the boundary cases (overwrite-at-cap, start-time attributes, counters omitted when unused), and two node tests exercising the real client including the 128 default.
packages/core1003 pass,packages/node951 pass, typecheck clean, lint clean, public API references unchanged.Release info Sub-libraries affected
Libraries affected
@posthog/coreis also bumped (minor); it has no checkbox above.Checklist
Additive from a consumer's perspective: two new optional config keys and two optional wire fields.
SpanInitandResolvedTracesConfiggain required members, but both ship for the first time in #4579 and neither is constructible by a published consumer.Behaviour does change for a span that exceeds 128 user attributes or events — it is now truncated and marked rather than sent whole. Given the alternative today is the whole span being dropped by the endpoint, truncation is the more useful outcome, and the caps are configurable for anyone who disagrees.
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Built with Claude Code, directed by @turnipdabeets, against the
Span limitsrequirement in the traces capability spec.Two decisions worth a look: