Skip to content

feat: add OpenTelemetry error reporting - #13

Open
matheus1lva wants to merge 6 commits into
mainfrom
feat/add-sentry
Open

feat: add OpenTelemetry error reporting#13
matheus1lva wants to merge 6 commits into
mainfrom
feat/add-sentry

Conversation

@matheus1lva

@matheus1lva matheus1lva commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Report Worker errors as OpenTelemetry log records over OTLP/HTTP. Cloudflare Workers can't run the OpenTelemetry Node SDK (its OTLP exporter is Node-coupled), so the OTLP/HTTP log payload is built and sent directly via fetch — still standard OTLP. Vendor-neutral: point OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT (and the matching OTEL_EXPORTER_OTLP_*_HEADERS for auth) at any OTLP backend. No-op until configured.

How to review

  • src/observability.ts: captureError(ctx, env, error) POSTs an OTLP LogsData payload (ERROR severity, exception.* attributes). Signal-specific logs endpoints are used as-is; the generic endpoint gets /v1/logs appended. Headers are percent-decoded per the OTLP env-var spec. timeUnixNano is BigInt(Date.now()) * 1_000_000n. Uses ctx.waitUntil so the export completes after the response is returned.
  • src/index.ts: unexpected-error and ApiError with status ≥ 500 call captureError. OTEL_* fields are on the Env type.
  • src/edge-cache.ts: background caches.default.put failures are routed through captureError.

Test plan

  • Manual: set OTEL_EXPORTER_OTLP_ENDPOINT (+ headers), trigger the 500 branch, confirm the log record reaches the backend.
  • Manual: leave the endpoint unset, confirm nothing is exported and responses are unchanged.
  • Automated: bun run test and bun run typecheck.

Risk / impact

Low. With the OTLP endpoint unset (the default), reporting is a no-op and behavior is unchanged. No funds, auth, or migration paths touched.

@matheus1lva matheus1lva changed the title feat: add Sentry error monitoring feat: add OpenTelemetry error reporting Jun 18, 2026

@murderteeth murderteeth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

Adds vendor-neutral OpenTelemetry error reporting to the Cloudflare Worker. Since the OTel Node SDK can't run on Workers, src/observability.ts hand-builds an OTLP/HTTP JSON log record and POSTs it via ctx.waitUntil (off the latency path). Fires on unexpected errors and on ApiError with status ≥ 500; no-op until OTEL_EXPORTER_OTLP_ENDPOINT (or _LOGS_ENDPOINT) is set. Verified locally: typecheck passes, 43/43 tests pass, wrangler deploy --dry-run bundles cleanly. Payload carries only error.name/message/stack — no request data or secrets.

Dependencies

  • package.json unchanged — but bun.lock gained an orphan @opentelemetry/api@1.9.1 entry (see Issues). The package itself is benign (official OTel org, no install scripts), but nothing imports it.

Issues

  • bun.lock:171 — Orphan @opentelemetry/api@1.9.1 lockfile entry with no package.json counterpart; residue from the abandoned SDK approach, and it does get installed (resolves vitest's optional peer).
    • Change: revert the bun.lock hunk so the lockfile matches the manifest.
    • Done when: bun install --frozen-lockfile succeeds and @opentelemetry/api no longer appears in bun.lock or node_modules.

Suggestions

  • src/index.ts:92 / src/edge-cache.ts:79ctx.waitUntil(edgeCache().put(...)) has no .catch, so background cache-write failures become unhandled rejections that never reach the OTLP backend. This PR is the natural place to route them through captureError.
  • test/observability.test.ts:59 — the "reports rejected exports" test stubs a resolved 401; the actual fetch-rejection .catch branch (src/observability.ts:71) is untested, and the documented no-op path (endpoint unset → zero fetches) has no test either.
  • src/observability.ts:39String(Date.now() * 1_000_000) exceeds MAX_SAFE_INTEGER; String(BigInt(Date.now()) * 1_000_000n) is exact.
  • src/observability.ts:16parseHeaders splits on , without percent-decoding, diverging from the OTLP env-var spec; a header value containing a comma would be split into a bogus second header.

Verdict

APPROVE


How This Was Reviewed

This review was conducted using the review-pr skill.

@matheus1lva

Copy link
Copy Markdown
Collaborator Author

8668b12:

  • reverted the orphan @opentelemetry/api lockfile entry (bun install --frozen-lockfile is clean; package not in node_modules)
  • cache waitUntil failures go through captureError
  • timeUnixNano is BigInt(Date.now()) * 1_000_000n
  • parseHeaders percent-decodes values
  • tests cover fetch rejection, unset-endpoint no-op, 401 export, and the header/timestamp cases

@murderteeth murderteeth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

Re-review of 8668b12 (delta: one commit since ee2411a). Prior review items verified: orphan @opentelemetry/api lockfile entry removed (frozen install clean), edge-cache waitUntil failures now routed through captureError, real fetch-rejection and no-op tests added without weakening the old ones, timeUnixNano now exact via BigInt. Typecheck, tests (46/46), and wrangler deploy --dry-run all pass. One new blocking defect introduced by the parseHeaders rewrite.

Issues

  • src/observability.ts:22 — The new decodeURIComponent calls in parseHeaders are unguarded, so a malformed OTEL_EXPORTER_OTLP_HEADERS value containing a raw un-encoded % (e.g. authorization=Api-Key 50%off) throws URIError synchronously inside captureError. Verified by execution. At src/index.ts:111/123 the throw escapes the catch block, turning every 5xx into a Cloudflare 1101 worker exception instead of the JSON error response; at src/edge-cache.ts:88 it rejects the waitUntil promise, re-introducing the uncaught background rejection this revision just fixed.
    • Change: wrap the two decodes in a try/catch inside parseHeaders only — fall back to the raw (undecoded) key/value or skip the malformed pair.
    • Keep: the comma-split + percent-decode behavior for well-formed values must remain (a %2C in a value still decodes to ,); the existing observability tests must still pass.
    • Done when: a test sets OTEL_EXPORTER_OTLP_HEADERS to a value with a raw % and asserts captureError does not throw, alongside the existing decode tests passing.

(This is a new regression introduced when applying previous review feedback. Lets prioritize a spec for automated pr reviews and cite this as a reference case.)

Verdict

REQUEST_CHANGES


How This Was Reviewed

This review was conducted using the review-pr skill.

@murderteeth murderteeth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Previous round's item is resolved — decodeHeaderPart guards the decode, %2C still decodes, raw-% test added. Branch on its own is green (typecheck clean, 47/47).

Issues

  • Resolve conflicts with main

Verdict

REQUEST_CHANGES


How This Was Reviewed

This review was conducted using the review-pr skill.

@murderteeth murderteeth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

PR #13 adds vendor-neutral OTLP/HTTP error reporting for Worker failures. The latest revision resolves the previous malformed-header issue and cleanly incorporates current main. No blocking issues found.

Issues

None.

Suggestions

None.

Verdict

APPROVE


How This Was Reviewed

  • Reviewed the complete diff against current main
  • Verified prior review findings and conflict resolution
  • Confirmed no new dependencies
  • bun run typecheck passed
  • bun run test passed: 101/101 tests
  • wrangler deploy --dry-run passed
  • GitHub CI checks are green
  • Visual verification was not applicable because there are no UI changes

This review was conducted using the review-pr skill.

@matheus1lva

Copy link
Copy Markdown
Collaborator Author

on hold until doppler - so we can get better deployments

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.

2 participants