Skip to content

feat(local): render OTel semantic attributes in transaction output#1015

Merged
BYK merged 13 commits into
mainfrom
feat/otel-semantic-local-formatter
May 23, 2026
Merged

feat(local): render OTel semantic attributes in transaction output#1015
BYK merged 13 commits into
mainfrom
feat/otel-semantic-local-formatter

Conversation

@BYK
Copy link
Copy Markdown
Member

@BYK BYK commented May 22, 2026

Summary

Enhance sentry local for the agent monitoring launch with OTel semantic attribute rendering, JSON output, SSE reconnection, and several quality improvements.

OTel Semantic Display

Port trace-semantic-display from sentry-mcp (PR #981) to enrich tail output with OTel semantic attribute rendering. Transactions with GenAI, MCP, HTTP, database, and other OTel attributes now show rich labels:

[gen_ai] chat anthropic/claude-4-sonnet [1200ms] [5 spans]
[mcp] tools/call search_files [320ms]
[db] SELECT users [postgresql] [12ms]

Covers 15 attribute families: GenAI, MCP, HTTP, database, GraphQL, RPC/cloud, messaging, FaaS, object stores, CloudEvents, CICD, feature flags, process, exception, and error types. Falls back to existing behavior when no semantic attributes are present.

--format json (NDJSON)

New --format json / -F json flag for machine-readable output. Each envelope item produces one JSON line with structured fields including semantic labels, stack frames, and source detection:

{"type":"transaction","op":"gen_ai","label":"chat anthropic/claude-4-sonnet","duration_ms":1200,"source":"server"}
{"type":"error","error_type":"TypeError","message":"x is not a function","filename":"app.ts","lineno":42,"source":"server"}

--filter ai

New filter value that matches transactions with GenAI or MCP OTel attributes, so you can focus on agent activity: sentry local -f ai

SSE Reconnection

The SSE consumer now reconnects automatically with exponential backoff on connection loss, using Last-Event-ID to resume from where the stream left off. Retry counter resets after successful reconnection. Transient HTTP errors after a previous successful session are retried rather than treated as fatal.

Quality Improvements

  • Signal handler leak fixed in run.ts — named handler refs, cleanup in finally, settled flag for close/error race
  • parsePort deduplicated — single implementation exported from server.ts
  • SENTRY_TRACES_SAMPLE_RATE preserved when already set (uses ?? fallback)
  • Startup banner with ingest URL, SSE endpoint, and connection hints
  • Agent monitoring docs with GenAI/MCP examples and JSON output section

Files Changed

File Change
src/lib/formatters/semantic-display.ts New — OTel semantic attribute rendering (15 families)
src/lib/formatters/local.ts Semantic display integration, JSON formatters, --filter ai, inferSourceName
src/commands/local/server.ts SSE reconnection, --format json, --filter ai, parsePort export, feedSSELine id tracking, startup banner
src/commands/local/run.ts Signal handler fix, parsePort import, env var preservation
docs/src/fragments/commands/local.md Agent monitoring section, JSON output section
test/lib/formatters/semantic-display.test.ts New — 70+ tests for all semantic formatters
test/lib/formatters/local.test.ts AI transaction tests, JSON format tests
test/commands/local/server.test.ts New — parsePort, feedSSELine, buildApp, CORS, isServerRunning
test/commands/local/run.test.ts Expanded — ENOENT, env preservation, separator handling

Port trace-semantic-display from sentry-mcp (PR #981) to enrich
sentry local tail output with OTel semantic attribute rendering.

Transactions with GenAI, MCP, HTTP, database, and other OTel attributes
now show rich labels instead of generic op + transaction name:

  [gen_ai] chat anthropic/claude-4-sonnet [1200ms] [5 spans]
  [mcp] tools/call search_files [320ms]
  [db] SELECT users [postgresql] [12ms]

The semantic display module covers 15 attribute families: GenAI, MCP,
HTTP, database, GraphQL, RPC/cloud, messaging, FaaS, object stores,
CloudEvents, CICD, feature flags, process, exception, and error types.

Falls back to existing behavior when no semantic attributes are present.
@BYK BYK requested a review from MathurAditya724 May 22, 2026 21:23
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-1015/

Built to branch gh-pages at 2026-05-23 12:39 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Codecov Results 📊

❌ Patch coverage is 75.30%. Project has 4281 uncovered lines.
✅ Project coverage is 81.97%. Comparing base (base) to head (head).

Files with missing lines (4)
File Patch % Lines
src/commands/local/server.ts 10.26% ⚠️ 70 Missing and 1 partials
src/lib/formatters/semantic-display.ts 94.09% ⚠️ 15 Missing and 13 partials
src/lib/formatters/local.ts 74.63% ⚠️ 17 Missing and 4 partials
src/commands/local/run.ts 100.00% ⚠️ 2 partials
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    81.87%    81.97%     +0.1%
==========================================
  Files          328       329        +1
  Lines        23359     23744      +385
  Branches     15114     15500      +386
==========================================
+ Hits         19123     19463      +340
- Misses        4236      4281       +45
- Partials      1621      1644       +23

Generated by Codecov Action

BYK added 2 commits May 22, 2026 21:29
- Hoist URL scheme regex to module-level constant
- Apply biome formatting fixes to test files
- Use template literal for string concatenation in test
- Add logger import and log.debug in formatHttpTarget catch block (#1)
- Document intentional inferSemanticOp domain omissions (#2)
- Handle single-element OTel array attributes in truncate (#4)
- Add numeric status code test (#5)
- Add tests for GraphQL, RPC, Messaging, ObjectStore, CloudEvents,
  CICD, and FeatureFlag formatters (#8)
- Simplify dedupeMetadata to skip redundant re-truncation (#10)
- Use consistent split pattern in formatResourceTarget (#13)
Comment thread src/lib/formatters/semantic-display.ts
S3 spans carry rpc.method/rpc.service attributes alongside aws.s3.*
attributes. Check for S3 before RPC so the op tag shows [s3] instead
of [rpc] for object store operations. Addresses Sentry Seer review.
Comment thread src/lib/formatters/semantic-display.ts Outdated
BYK and others added 2 commits May 23, 2026 11:20
- Deduplicate parsePort: export from server.ts, import in run.ts
- Fix signal handler leak in run.ts: store refs, remove in finally,
  add settled flag to prevent close/error race
- Add SSE reconnection with exponential backoff and Last-Event-ID
  resume support in the SSE consumer
- Add --format json flag for NDJSON output (machine-readable output
  for AI coding agents and automation tools)
- Preserve existing SENTRY_TRACES_SAMPLE_RATE (use ?? fallback)
- Add agent monitoring section to docs with gen_ai/mcp examples
- Add JSON output section to docs
- Add server.test.ts: parsePort, feedSSELine (with id tracking),
  buildApp (health, ingest, CORS, SSE, 413 rejection), isServerRunning
- Expand run.test.ts: -- separator stripping, ENOENT handling,
  SENTRY_TRACES_SAMPLE_RATE preservation
Comment thread src/lib/formatters/semantic-display.ts
Comment thread src/commands/local/server.ts
Comment thread src/commands/local/server.ts
…cate code

- Reset retries and backoff delay after a successful connection that
  later drops, so transient disconnects don't exhaust the retry budget
- Distinguish 'connected-then-lost' from 'error' in attemptSSEConnection
- Remove duplicate unreachable code blocks in inferSemanticOp
  (biome format artifact)
Comment thread src/commands/local/server.ts
Only treat 'no-connection' as fatal on the first attempt. If we've
previously connected successfully, the server may be restarting —
retry with backoff instead of giving up.
Comment thread src/lib/formatters/local.ts Outdated
BYK and others added 2 commits May 23, 2026 12:08
Review fixes:
- Add stack frame info (filename, lineno, colno, function) to JSON
  error output for AI agent consumption
- Add JSDoc to formatItemJson documenting sanitize design decision
- Fix docs: SENTRY_TRACES_SAMPLE_RATE is '1 (unless already set)'
- Fix ENOENT test to assert error message pattern, not just defined
- Add comprehensive JSON format tests (error, transaction, log, source)

Nice-to-haves:
- Add startup banner with ingest URL, SSE endpoint, and connection hints
- Add --filter ai to match transactions with GenAI/MCP OTel attributes
Comment thread src/commands/local/server.ts Outdated
Comment thread src/commands/local/server.ts Outdated
Security:
- Add stripBidi() for JSON output — JSON.stringify escapes C0/C1 but
  leaves Unicode BiDi override chars (U+202A-202E, U+2066-2069) intact,
  which can reorder terminal text. JSON formatters now call stripBidi()
  on all user-controlled string values.
- Extract BIDI_RE regex to module level, shared by sanitize() and stripBidi()

SSE reconnection:
- On first attempt, both 'no-connection' and 'error' are now fatal
  (server doesn't exist). Only retry after a previous successful
  connection (hasConnectedBefore).
Comment thread src/commands/local/server.ts
Comment thread src/lib/formatters/local.ts
Comment thread src/lib/formatters/local.ts
Mid-stream errors (network reset, proxy timeout) now correctly
report as 'connected-then-lost' instead of 'no-connection', enabling
the reconnection loop. The onConnected callback fires as soon as
the HTTP 200 response is received, before the stream read loop.
Comment thread src/commands/local/server.ts
Comment thread src/lib/formatters/local.ts
JSON.stringify only escapes C0 (U+0000-U+001F) per RFC 8259; C1
controls (U+0080-U+009F, e.g. CSI=U+009B) pass through unescaped.
stripBidi() now removes both C1 and BiDi chars, preventing terminal
injection when --format json output is displayed in a terminal.
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit adcc086. Configure here.

Comment thread src/commands/local/server.ts
Comment thread src/lib/formatters/local.ts
@BYK BYK merged commit aa92006 into main May 23, 2026
28 checks passed
@BYK BYK deleted the feat/otel-semantic-local-formatter branch May 23, 2026 12:50
attrs: AttributeSource,
fallbackLabel: string
): SemanticSpanDisplay | null {
const rpcSystem = getAttr(attrs, ["rpc.system.name"]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rpc.system.name is not a valid OTel attribute — should be rpc.system

The code reads rpc.system.name to identify the RPC system, but the OTel semantic conventions define this attribute as rpc.system (values: "grpc", "java_rmi", "dotnet_wcf", etc.). Standard OTel-instrumented RPC spans will never populate rpcSystem, silently dropping the system label from metadata and causing inferSemanticOp to miss spans that only carry rpc.system without rpc.service.

Evidence
  • getAttr(attrs, ["rpc.system.name"]) at line 317 will always return undefined for spans instrumented by standard OTel SDKs, which emit rpc.system.
  • db.system.name (used for databases at line 247) is the correct newer-semconv attribute, so the *.system.name pattern was incorrectly extended to RPC where no such rename occurred in the spec.
  • inferSemanticOp at line 146 also uses "rpc.system.name" as its primary key; a span with only {"rpc.system": "grpc"} would not be tagged as rpc.
  • Unit tests at test/lib/formatters/semantic-display.test.ts:285,504 also use rpc.system.name, confirming the wrong key propagated into test coverage rather than catching the mismatch.

Identified by Warden find-bugs · LHB-MGA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant