-
-
Notifications
You must be signed in to change notification settings - Fork 791
fix: external traces now respect parent sampling, and prevent broken traces when there is no external trace context #2395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🦋 Changeset detectedLatest commit: e4d0edb The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughAdds propagation of traceFlags throughout tracing code: parseTraceparent/serializeTraceparent now handle traceFlags; external trace context types and TraceContextManager include and propagate traceFlags; tracing SDK uses external traceFlags to determine sampling, gate span/log export, rebuild parent span contexts, and filter internal-only spans (using SEMATTRS_HTTP_URL). The webapp service now serializes traceFlags when propagating external trace context. A changeset entry records a patch release for trigger.dev about preserving parent sampling and preventing broken traces. Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
…traces when there is no external trace context
3e16462
to
747721c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🔭 Outside diff range comments (4)
packages/core/src/v3/isomorphic/traceContext.ts (1)
8-25
: Harden traceparent parsing and update manager to reuse itShort: I verified parseTraceparent only enforced 4 parts and found a direct split in the trace context manager that also needs the same validation. Apply the hardened parser and have the manager use it.
Files to change:
- packages/core/src/v3/isomorphic/traceContext.ts — harden parser (accept 3/4 parts, validate hex lengths, non-zero IDs, normalize lowercase, drop invalid flags).
- packages/core/src/v3/traceContext/manager.ts — stop manual split; call the hardened parseTraceparent so external tracecontext is validated consistently (split is at ~line 68).
Proposed diffs:
packages/core/src/v3/isomorphic/traceContext.ts
export function parseTraceparent( traceparent?: string ): { traceId: string; spanId: string; traceFlags?: string } | undefined { if (!traceparent) { return undefined; } - - const parts = traceparent.split("-"); - - if (parts.length !== 4) { - return undefined; - } - - const [version, traceId, spanId, traceFlags] = parts; - - if (version !== "00") { - return undefined; - } - - if (!traceId || !spanId) { - return undefined; - } - - return { traceId, spanId, traceFlags }; + const parts = traceparent.split("-"); + + // Accept either 3- or 4-part headers (defensive), otherwise reject + if (parts.length !== 3 && parts.length !== 4) { + return undefined; + } + + const [version, traceIdRaw, spanIdRaw, traceFlagsRaw] = parts; + + if (version !== "00") { + return undefined; + } + + if (!traceIdRaw || !spanIdRaw) { + return undefined; + } + + const isHex = /^[0-9a-f]+$/i; + const traceId = traceIdRaw.toLowerCase(); + const spanId = spanIdRaw.toLowerCase(); + const traceFlagsCandidate = + typeof traceFlagsRaw === "string" ? traceFlagsRaw.toLowerCase() : undefined; + + // Validate lengths, hex, and not-all-zero constraints + if ( + traceId.length !== 32 || + !isHex.test(traceId) || + traceId === "00000000000000000000000000000000" + ) { + return undefined; + } + if (spanId.length !== 16 || !isHex.test(spanId) || spanId === "0000000000000000") { + return undefined; + } + + // Validate flags if provided; drop invalid flags + const traceFlags = + traceFlagsCandidate && /^[0-9a-f]{2}$/.test(traceFlagsCandidate) + ? traceFlagsCandidate + : undefined; + + return { traceId, spanId, traceFlags }; }packages/core/src/v3/traceContext/manager.ts
import { Context, context, propagation, trace, TraceFlags } from "@opentelemetry/api"; import { TraceContextManager } from "./types.js"; +import { parseTraceparent } from "../isomorphic/traceContext.js"; export class StandardTraceContextManager implements TraceContextManager { public traceContext: Record<string, unknown> = {}; @@ if ("traceparent" in traceContext && typeof traceContext.traceparent === "string") { - const [version, traceId, spanId, traceFlags] = traceContext.traceparent.split("-"); - - if (!traceId || !spanId) { - return undefined; - } - - return { - traceId, - spanId, - traceFlags, - tracestate: tracestate, - }; + const parsed = parseTraceparent(traceContext.traceparent); + if (!parsed) { + return undefined; + } + + return { + traceId: parsed.traceId, + spanId: parsed.spanId, + traceFlags: parsed.traceFlags, + tracestate: tracestate, + }; }I ran a repo search — the only direct traceparent.split occurrences are:
- packages/core/src/v3/isomorphic/traceContext.ts
- packages/core/src/v3/traceContext/manager.ts (split at ~line 68)
Please apply both changes so all external traceparent parsing is consistent and validated. If you want strict W3C-only behavior instead, keep the 4-part check and reject 3-part headers.
packages/core/src/v3/traceContext/manager.ts (2)
1-3
: Import and reuse shared parser to avoid duplication and inconsistencies
extractExternalTraceContext
reimplements traceparent parsing. Prefer reusing the sharedparseTraceparent
to keep validation and normalization centralized.Apply this diff to import the parser:
-import { Context, context, propagation, trace, TraceFlags } from "@opentelemetry/api"; +import { Context, context, propagation, trace, TraceFlags } from "@opentelemetry/api"; +import { parseTraceparent } from "../isomorphic/traceContext.js";
67-79
: De-duplicate parsing and enforce validation via parseTraceparent()Current code splits the string and doesn’t validate version/length/hex. Reuse
parseTraceparent
for correctness and consistency with isomorphic utilities.Apply this diff:
- if ("traceparent" in traceContext && typeof traceContext.traceparent === "string") { - const [version, traceId, spanId, traceFlags] = traceContext.traceparent.split("-"); - - if (!traceId || !spanId) { - return undefined; - } - - return { - traceId, - spanId, - traceFlags, - tracestate: tracestate, - }; - } + if ("traceparent" in traceContext && typeof traceContext.traceparent === "string") { + const parsed = parseTraceparent(traceContext.traceparent); + if (!parsed) { + return undefined; + } + return { + traceId: parsed.traceId, + spanId: parsed.spanId, + traceFlags: parsed.traceFlags, + tracestate, + }; + }packages/core/src/v3/otel/tracingSDK.ts (1)
413-415
: Fix logs/spans correlation when there is no external trace contextCurrently, logs skip traceId override if externalTraceContext is absent, but spans still use the generated fallback externalTraceId. This breaks correlation between spans and logs. Allow logs to use the fallback externalTraceId as well.
- if (!logRecord.spanContext || !this.externalTraceId || !this.externalTraceContext) { + if (!logRecord.spanContext || !this.externalTraceId) { return logRecord; }
🧹 Nitpick comments (6)
packages/core/src/v3/traceContext/types.ts (1)
11-13
: Document expected format of traceFlags (2-char hex, lowercase) and consider validation at boundariesAdding
traceFlags?: string
is correct. To avoid propagation of malformed flags, please document the expected format and, where feasible, normalize/validate at parse/serialize boundaries (see comments in isomorphic/traceContext.ts).Would you like me to add a lightweight validation note in the JSDoc here and ensure callers (parse/serialize) normalize to two lowercase hex chars?
apps/webapp/app/runEngine/services/triggerTask.server.ts (1)
380-385
: Optional: tolerate older/lenient traceparent formats to avoid skipping propagation
parseTraceparent
currently returns undefined for non-4-part headers. If you expect any upstream sources to send 3-part headers, consider makingparseTraceparent
tolerant (see suggested refactor in isomorphic/traceContext.ts). Otherwise, the code rightly falls back to the originaltraceContext
unchanged.Do we have any external integrations known to send non-compliant traceparent values? If yes, I can adjust parsing to keep propagation working while still validating IDs.
packages/core/src/v3/otel/tracingSDK.ts (4)
306-313
: Dropping internal-only spans can create dangling parents; consider reparenting childrenFiltering internal-only spans is correct, but children will still point to a parentSpanId that was removed, producing broken chains in external backends.
Optional refactor: in export(), precompute the set of spans you will drop (internal-only/unsampled), build a map spanId -> parentSpanContext, and reparent exported spans to the nearest exported ancestor (or undefined if none). This preserves a consistent tree after filtering.
356-356
: Tighten types for exporter APIMatch the SpanExporter signature to improve type safety.
- export(spans: any[], resultCallback: (result: any) => void): void { + export(spans: ReadableSpan[], resultCallback: (result: any) => void): void {
364-364
: Use OTEL diag logger for exporter errorsPrefer diag.error for consistency with OTEL diagnostics and central log control.
- console.error(e); + diag.error("ExternalSpanExporterWrapper.export error", e as Error);
395-395
: Tighten types for log exporter APIAlign with LogRecordExporter for stronger type safety.
- export(logs: any[], resultCallback: (result: any) => void): void { + export(logs: ReadableLogRecord[], resultCallback: (result: any) => void): void {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
⛔ Files ignored due to path filters (4)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
references/d3-chat/package.json
is excluded by!references/**
references/d3-chat/src/instrumentation.ts
is excluded by!references/**
references/d3-chat/trigger.config.ts
is excluded by!references/**
📒 Files selected for processing (6)
.changeset/violet-llamas-roll.md
(1 hunks)apps/webapp/app/runEngine/services/triggerTask.server.ts
(1 hunks)packages/core/src/v3/isomorphic/traceContext.ts
(3 hunks)packages/core/src/v3/otel/tracingSDK.ts
(5 hunks)packages/core/src/v3/traceContext/manager.ts
(3 hunks)packages/core/src/v3/traceContext/types.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
**/*.{ts,tsx}
: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations
Files:
packages/core/src/v3/traceContext/types.ts
packages/core/src/v3/traceContext/manager.ts
packages/core/src/v3/isomorphic/traceContext.ts
apps/webapp/app/runEngine/services/triggerTask.server.ts
packages/core/src/v3/otel/tracingSDK.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
We use zod a lot in packages/core and in the webapp
Files:
packages/core/src/v3/traceContext/types.ts
packages/core/src/v3/traceContext/manager.ts
packages/core/src/v3/isomorphic/traceContext.ts
apps/webapp/app/runEngine/services/triggerTask.server.ts
packages/core/src/v3/otel/tracingSDK.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}
: In the webapp, all environment variables must be accessed through theenv
export ofenv.server.ts
, instead of directly accessingprocess.env
.
When importing from@trigger.dev/core
in the webapp, never import from the root@trigger.dev/core
path; always use one of the subpath exports as defined in the package's package.json.
Files:
apps/webapp/app/runEngine/services/triggerTask.server.ts
🧠 Learnings (1)
📚 Learning: 2025-07-18T17:50:25.014Z
Learnt from: CR
PR: triggerdotdev/trigger.dev#0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-07-18T17:50:25.014Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : ALWAYS generate Trigger.dev tasks using the `task` function from `trigger.dev/sdk/v3` and export them as shown in the correct pattern.
Applied to files:
apps/webapp/app/runEngine/services/triggerTask.server.ts
🧬 Code Graph Analysis (2)
packages/core/src/v3/isomorphic/traceContext.ts (1)
apps/webapp/app/v3/eventRepository.server.ts (1)
traceId
(1324-1336)
packages/core/src/v3/otel/tracingSDK.ts (2)
packages/core/src/v3/index.ts (1)
SemanticInternalAttributes
(22-22)packages/core/src/v3/semanticInternalAttributes.ts (1)
SemanticInternalAttributes
(1-64)
🪛 GitHub Check: CodeQL
packages/core/src/v3/otel/tracingSDK.ts
[failure] 458-458: Incomplete URL substring sanitization
'https://api.trigger.dev' can be anywhere in the URL, and arbitrary hosts may come before or after it.
[failure] 462-462: Incomplete URL substring sanitization
'https://billing.trigger.dev' can be anywhere in the URL, and arbitrary hosts may come before or after it.
[failure] 466-466: Incomplete URL substring sanitization
'https://cloud.trigger.dev' can be anywhere in the URL, and arbitrary hosts may come before or after it.
[failure] 470-470: Incomplete URL substring sanitization
'https://engine.trigger.dev' can be anywhere in the URL, and arbitrary hosts may come before or after it.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: typecheck / typecheck
🔇 Additional comments (5)
.changeset/violet-llamas-roll.md (1)
5-5
: Changelog entry is clear and scoped correctlyPatch-level note succinctly captures the behavioral fix for external traces and sampling propagation.
apps/webapp/app/runEngine/services/triggerTask.server.ts (1)
386-391
: Good: propagate parent spanId and traceFlags to preserve sampling decisionPassing
parsedTraceparent.traceFlags
through toserializeTraceparent(...)
ensures the sampling bit is preserved alongside the updated parent spanId. This aligns with the PR objective.packages/core/src/v3/otel/tracingSDK.ts (3)
24-24
: Good addition: use SEMATTRS_HTTP_URL for detectionImporting SEMATTRS_HTTP_URL aligns the check with OTEL semantic conventions and avoids magic strings.
346-347
: Good guard to avoid broken traces when no external parent is availableSetting parentSpanContext to undefined for attempt spans without external context prevents constructing an invalid parent reference.
444-454
: Internal-only span filter looks goodSkipping partial spans and the explicit url.path check for the ingest endpoint are appropriate safeguards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
packages/core/src/v3/otel/tracingSDK.ts (2)
341-347
: Bug: mapping external traceFlags using string equality drops sampling and ignores numeric flags
externalTraceContext.traceFlags
is typed as a number, but the code checkstypeof === "string"
and compares to"01"
. This will always fall back toparentSpanContext?.traceFlags
, losing the upstream sampling decision. Respect the numeric bitmask instead.Apply this diff:
- traceFlags: - typeof this.externalTraceContext.traceFlags === "string" - ? this.externalTraceContext.traceFlags === "01" - ? 1 - : 0 - : parentSpanContext?.traceFlags ?? 0, + traceFlags: + typeof this.externalTraceContext.traceFlags === "number" + ? (this.externalTraceContext.traceFlags & TraceFlags.SAMPLED) + : parentSpanContext?.traceFlags ?? TraceFlags.NONE,
444-476
: Harden internal URL detection: avoid substring host checks; use strict hostname matchUsing
includes()
onurl.hostname
can misclassify hosts (e.g., attacker.example.com/api.trigger.dev). Compare exact hostnames via a Set and check path equality for the ingest endpoint.Apply this diff:
- const internalHosts = [ - "api.trigger.dev", - "billing.trigger.dev", - "cloud.trigger.dev", - "engine.trigger.dev", - "platform.trigger.dev", - ]; - - return ( - internalHosts.some((host) => url.hostname.includes(host)) || - url.pathname.includes("/api/v1/usage/ingest") - ); + return INTERNAL_HOSTNAMES.has(url.hostname) || url.pathname === "/api/v1/usage/ingest";Add this near the top of the file (outside the function) to replace the removed inline array:
const INTERNAL_HOSTNAMES = new Set([ "api.trigger.dev", "billing.trigger.dev", "cloud.trigger.dev", "engine.trigger.dev", "platform.trigger.dev", ]);
🧹 Nitpick comments (2)
packages/core/src/v3/traceContext/manager.ts (2)
35-37
: Nit: error message references the wrong method nameThe message says “withExternalSpan” but the method is
withExternalTrace
.Apply this diff:
- "No active span found. withExternalSpan must be called within an active span context." + "No active span found. withExternalTrace must be called within an active span context."
63-75
: Robust extraction with graceful failure on invalid traceparentParsing
traceparent
and returningundefined
when invalid prevents broken traces from malformed inputs. The returned numerictraceFlags
is ideal for downstream bitmask checks.If you want to tighten types here, consider narrowing
traceContext
toRecord<string, unknown>
before property access to appease TS withoutany
:-function extractExternalTraceContext(traceContext: unknown) { +function extractExternalTraceContext(traceContext: unknown) { if (typeof traceContext !== "object" || traceContext === null) { return undefined; } - const tracestate = - "tracestate" in traceContext && typeof traceContext.tracestate === "string" + const ctx = traceContext as Record<string, unknown>; + const tracestate = + typeof ctx.tracestate === "string" ? traceContext.tracestate : undefined; - if ("traceparent" in traceContext && typeof traceContext.traceparent === "string") { - const externalSpanContext = parseTraceParent(traceContext.traceparent); + if (typeof ctx.traceparent === "string") { + const externalSpanContext = parseTraceParent(ctx.traceparent);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/core/src/v3/otel/tracingSDK.ts
(6 hunks)packages/core/src/v3/traceContext/manager.ts
(3 hunks)packages/core/src/v3/traceContext/types.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/core/src/v3/traceContext/types.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
**/*.{ts,tsx}
: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations
Files:
packages/core/src/v3/traceContext/manager.ts
packages/core/src/v3/otel/tracingSDK.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
We use zod a lot in packages/core and in the webapp
Files:
packages/core/src/v3/traceContext/manager.ts
packages/core/src/v3/otel/tracingSDK.ts
🧬 Code Graph Analysis (2)
packages/core/src/v3/traceContext/manager.ts (3)
apps/webapp/app/runEngine/services/triggerTask.server.ts (1)
traceContext
(355-399)apps/webapp/app/presenters/v3/SpanPresenter.server.ts (1)
traceContext
(600-620)packages/core/src/v3/trace-context-api.ts (1)
traceContext
(5-5)
packages/core/src/v3/otel/tracingSDK.ts (2)
packages/core/src/v3/index.ts (1)
SemanticInternalAttributes
(22-22)packages/core/src/v3/semanticInternalAttributes.ts (1)
SemanticInternalAttributes
(1-64)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: typecheck / typecheck
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
packages/core/src/v3/traceContext/manager.ts (2)
3-3
: Good addition: rely on OpenTelemetry's parser for correctnessImporting and using
parseTraceParent
centralizes W3C parsing and avoids homegrown parsing bugs.
40-45
: Propagating external traceFlags into span context is correctUsing the external
traceFlags
(numeric) preserves upstream sampling decisions. This aligns with the PR goal of respecting parent sampling.packages/core/src/v3/otel/tracingSDK.ts (4)
296-306
: Correct: centralize sampling decision via bitmask helperInitializing
_isExternallySampled
usingisTraceFlagSampled(externalTraceContext?.traceFlags)
keeps behavior consistent across spans and logs and respects the LSB sampling bit.
309-314
: Early-return gating is soundShort-circuiting span export when not externally sampled avoids emitting spans/logs for unsampled parent traces.
383-393
: Consistent log gating with sampling bitmaskMirrors span behavior; if parent isn’t sampled, emit nothing. This prevents “stray” logs for unsampled traces.
490-496
: LGTM: sampling check uses numeric bitmask
(traceFlags & TraceFlags.SAMPLED) === TraceFlags.SAMPLED
is the correct interpretation per W3C. Defaulting to sampled when flags are absent matches the current default behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/core/src/v3/otel/tracingSDK.ts (1)
439-471
: Fix URL host/path checks: use exact hostname match and strict path comparison (CodeQL: incomplete URL substring sanitization)Using includes() on url.hostname and url.pathname is vulnerable to false positives (e.g., evilapi.trigger.dev matches "api.trigger.dev"; "/api/v1/usage/ingestX" matches the ingest path). Parse and compare strictly.
Apply this diff within this function:
@@ - const internalHosts = [ - "api.trigger.dev", - "billing.trigger.dev", - "cloud.trigger.dev", - "engine.trigger.dev", - "platform.trigger.dev", - ]; - - return ( - internalHosts.some((host) => url.hostname.includes(host)) || - url.pathname.includes("/api/v1/usage/ingest") - ); + return ( + INTERNAL_HOSTNAMES.has(url.hostname) || + url.pathname === "/api/v1/usage/ingest" + );Add this module-level constant (outside the function) to avoid per-call allocations:
const INTERNAL_HOSTNAMES = new Set<string>([ "api.trigger.dev", "billing.trigger.dev", "cloud.trigger.dev", "engine.trigger.dev", "platform.trigger.dev", ]);This resolves the CodeQL “Incomplete URL substring sanitization” findings and prevents accidental matches.
🧹 Nitpick comments (2)
packages/core/src/v3/otel/tracingSDK.ts (2)
333-345
: Propagate external traceFlags to parent and spanContext for consistencyParent sampling is respected for gating, but exported spans will still carry their pre-existing traceFlags unless overridden. For consistency with the parent sampling decision and downstream processors, propagate the external traceFlags to both parentSpanContext (when rebuilding) and the span’s spanContext.
Apply this diff:
@@ if (parentSpanContext) { parentSpanContext = { ...parentSpanContext, - traceId: externalTraceId, + traceId: externalTraceId, + traceFlags: + typeof this.externalTraceContext?.traceFlags === "number" + ? this.externalTraceContext.traceFlags + : parentSpanContext.traceFlags, }; } @@ return { ...span, - spanContext: () => ({ ...spanContext, traceId: externalTraceId }), + spanContext: () => ({ + ...spanContext, + traceId: externalTraceId, + traceFlags: + typeof this.externalTraceContext?.traceFlags === "number" + ? this.externalTraceContext.traceFlags + : spanContext.traceFlags, + }), parentSpanContext, };Also applies to: 347-351
378-388
: Type the exporter wrapper methods to OTEL types instead of anyThe wrapper’s export signatures use any[], which loses type-safety and IDE help. Prefer ReadableLogRecord[]/ReadableSpan[] and the proper callback result type.
Apply these diffs:
@@ -class ExternalLogRecordExporterWrapper { +class ExternalLogRecordExporterWrapper { @@ - export(logs: any[], resultCallback: (result: any) => void): void { + export(logs: ReadableLogRecord[], resultCallback: (result: unknown) => void): void { @@ - const modifiedLogs = logs.map(this.transformLogRecord.bind(this)); + const modifiedLogs = logs.map(this.transformLogRecord.bind(this)); this.underlyingExporter.export(modifiedLogs, resultCallback); }And above, for the span wrapper:
@@ - export(spans: any[], resultCallback: (result: any) => void): void { + export(spans: ReadableSpan[], resultCallback: (result: unknown) => void): void {Note: If you want to be exact, you can import and use the concrete ExportResult type from OTEL instead of unknown, but the key improvement is avoiding any here.
Also applies to: 390-396
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/core/src/v3/otel/tracingSDK.ts
(6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
**/*.{ts,tsx}
: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations
Files:
packages/core/src/v3/otel/tracingSDK.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
We use zod a lot in packages/core and in the webapp
Files:
packages/core/src/v3/otel/tracingSDK.ts
🧬 Code Graph Analysis (1)
packages/core/src/v3/otel/tracingSDK.ts (1)
packages/core/src/v3/semanticInternalAttributes.ts (1)
SemanticInternalAttributes
(1-64)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: typecheck / typecheck
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
packages/core/src/v3/otel/tracingSDK.ts (6)
1-7
: Importing TraceFlags to honor sampling bits — good changePulling in TraceFlags enables correct bitmask checks for sampling. This aligns with the PR objective to respect parent sampling.
30-30
: Confirm SEMATTRS_HTTP_URL availability in the current semconv versionSome @opentelemetry/semantic-conventions versions expose different constant names. Ensure SEMATTRS_HTTP_URL resolves to "http.url" for the installed version to avoid undefined attribute lookups and false negatives in isSpanInternalOnly().
Would you like me to scan the repo for the installed semconv version and verify the constant’s presence via a quick script?
296-307
: External sampling preserved via bitmask helper — LGTMUsing isTraceFlagSampled(externalTraceContext?.traceFlags) centralizes and correctly interprets the sampling bit. Defaulting to sampled when flags are absent addresses “prevent broken traces when there is no external trace context.”
309-316
: Gate export on external sampling and filter internal-only spans — solidEarly-return to drop unsampled traces and internal-only spans reduces exporter load and matches the PR’s intent.
473-483
: safeParseUrl is robust and side-effect free — good utilityGracefully handles non-strings and parse errors; perfect for defensive URL parsing.
485-491
: Sampling bitmask helper (isTraceFlagSampled) correctly uses TraceFlags.SAMPLEDThis correctly interprets the sampling bit from numeric traceFlags and defaults to sampled when absent.
No description provided.