-
-
Couldn't load subscription status.
- Fork 1.3k
Setup otel/tracing example #5552
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
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThis PR adds a complete new example project ( Changes
Sequence DiagramsequenceDiagram
participant Client
participant OTel as OpenTelemetry
participant Server
participant Jaeger as Jaeger Collector
rect rgb(200, 220, 255)
Note over Client,Jaeger: Request with Tracing
Client->>Server: HTTP Request
Server->>OTel: startActiveSpan (method + path)
Server->>OTel: setAttributes (http.method, http.url, route)
end
rect rgb(220, 240, 220)
Note over Server: Route Processing
Server->>Server: Load route data / fetch API
Server->>Server: Handle request
end
rect rgb(200, 220, 255)
Note over Client,Jaeger: Tracing Completion
Server->>OTel: setStatus (OK)
OTel->>Jaeger: Export span
Server->>Client: HTTP Response
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes While the PR introduces ~40 new files totaling substantial code, the changes follow consistent, repetitive patterns (route definitions, configuration files, utility functions) that are relatively straightforward to verify. The homogeneous nature of the route and component files reduces per-file review complexity, though the breadth of the addition and OpenTelemetry integration require careful attention to correctness and consistency across the example. Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
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: 13
🧹 Nitpick comments (18)
examples/react/start-otel/src/otel.ts (2)
8-12: Consider BatchSpanProcessor for production-like examples.
SimpleSpanProcessorexports spans synchronously, blocking the application thread. While acceptable for this initial example, consider documenting this limitation or usingBatchSpanProcessorto demonstrate production-ready patterns.If you'd like to show a more production-ready approach, apply this diff:
-import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node' +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node' const sdk = new NodeSDK({ serviceName: 'tanstack-start', spanProcessors: [ - new SimpleSpanProcessor( + new BatchSpanProcessor( new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces', }), ), ], })
16-16: Add error handling and graceful shutdown.The SDK starts without error handling, which could hide initialization failures. Additionally, there's no graceful shutdown handling to ensure spans are flushed before the process exits.
Consider adding error handling and shutdown hooks:
-sdk.start() +sdk.start().catch((error) => { + console.error('Error initializing OpenTelemetry SDK:', error) +}) + +// Gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk.shutdown() + .then(() => console.log('OpenTelemetry SDK shut down successfully')) + .catch((error) => console.error('Error shutting down OpenTelemetry SDK:', error)) + .finally(() => process.exit(0)) +})examples/react/start-otel/tailwind.config.mjs (1)
1-4: Config is fine; minor optional tweaks.Consider adding darkMode: 'class' and including MDX if you add MDX content later.
export default { - content: ['./src/**/*.{js,jsx,ts,tsx}'], + content: ['./src/**/*.{js,jsx,ts,tsx,md,mdx}'], + darkMode: 'class', }examples/react/start-otel/src/server.ts (1)
7-25: Use semantic-conventions, set span status, and avoid duplicate URL parsing.This improves portability across backends and adds error status. Also compute URL once.
export default { async fetch(request: Request) { - return tracer.startActiveSpan( - `${request.method} ${new URL(request.url).pathname}`, - async (span) => { + const url = new URL(request.url) + return tracer.startActiveSpan( + `${request.method} ${url.pathname}`, + async (span) => { try { - span.setAttribute('http.method', request.method) - span.setAttribute('http.url', request.url) - span.setAttribute('http.route', new URL(request.url).pathname) + // Prefer semantic-conventions constants if available + // (requires adding @opentelemetry/semantic-conventions) + span.setAttribute('http.method', request.method) + span.setAttribute('http.url', request.url) + span.setAttribute('http.route', url.pathname) const response = await handler.fetch(request) - span.setAttribute('http.status_code', response.status) + span.setAttribute('http.status_code', response.status) return response - } catch (error: any) { - span.recordException(error) + } catch (error: unknown) { + span.recordException(error as Error) + // Mark span as error for backends that rely on status + // import { SpanStatusCode } from '@opentelemetry/api' + // span.setStatus({ code: SpanStatusCode.ERROR }) throw error } finally { span.end() } }, ) }, }If you add semantic constants, replace the string keys with constants (e.g., HTTP_REQUEST_METHOD, URL_FULL, HTTP_RESPONSE_STATUS_CODE). As per coding guidelines.
examples/react/start-otel/tsconfig.json (1)
3-21: Great strict base; consider a few extra safety knobs.Optional additions that catch more issues in examples without extra noise.
"compilerOptions": { "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitOverride": true, + "verbatimModuleSyntax": true, "esModuleInterop": true, @@ - "allowJs": true, + "allowJs": false,Based on learnings.
examples/react/start-otel/src/components/UserError.tsx (1)
1-6: Consider consolidating error components.UserErrorComponent and PostErrorComponent (in PostError.tsx) are identical except for naming. For an example demonstrating patterns, having separate components shows flexibility, but you could also consolidate to a single reusable error component if preferred.
examples/react/start-otel/src/router.tsx (2)
11-12: Pass the component directly (no wrapper needed).defaultNotFoundComponent accepts a component. Passing NotFound directly keeps it simple.
- defaultNotFoundComponent: () => <NotFound />, + defaultNotFoundComponent: NotFound,
1-4: Import alias consistency.Elsewhere in this example you use the "~" alias for src. Consider aligning for consistency.
-import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' -import { NotFound } from './components/NotFound' +import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary' +import { NotFound } from '~/components/NotFound'examples/react/start-otel/src/routes/posts_.$postId.deep.tsx (1)
1-3: Add a notFoundComponent for consistent UX with /posts/$postId.fetchPost throws notFound() on 404. Define a local notFoundComponent so this deep route shows a friendly message like its sibling.
-import { Link, createFileRoute } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' +import { NotFound } from '~/components/NotFound' export const Route = createFileRoute('/posts_/$postId/deep')({ loader: async ({ params: { postId } }) => fetchPost({ data: postId, }), errorComponent: PostErrorComponent, - component: PostDeepComponent, + component: PostDeepComponent, + notFoundComponent: () => <NotFound>Post not found</NotFound>, })Also applies to: 5-12
examples/react/start-otel/src/routes/__root.tsx (1)
65-69: Add a lang attribute to .Improves a11y and i18n defaults.
- <html> + <html lang="en">examples/react/start-otel/src/components/DefaultCatchBoundary.tsx (3)
17-17: Guard error logs for production.Avoid leaking stack/errors in prod builds; gate under a dev check.
- console.error('DefaultCatchBoundary Error:', error) + if (import.meta.env.DEV) { + console.error('DefaultCatchBoundary Error:', error) + }
20-21: Improve error announcement for a11y.Expose the error to assistive tech.
- return ( - <div className="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6"> + return ( + <div + className="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6" + role="alert" + aria-live="assertive" + >
23-27: Set explicit button type.Prevents accidental form submission inside forms.
- <button + <button + type="button" onClick={() => { router.invalidate() }}examples/react/start-otel/src/utils/posts.tsx (3)
44-51: Set OTEL error status and useful attrs; prefer strict typing for error.Record count on success and set ERROR status on failures; avoid
any.- span.setStatus({ code: SpanStatusCode.OK }) - return (posts as Array<PostType>).slice(0, 10) - } catch (error: any) { - span.recordException(error) + span.setAttribute('posts.count', (posts as Array<PostType>).length) + span.setStatus({ code: SpanStatusCode.OK }) + return (posts as Array<PostType>).slice(0, 10) + } catch (error: unknown) { + span.recordException(error as any) + span.setStatus({ + code: SpanStatusCode.ERROR, + message: error instanceof Error ? error.message : String(error), + }) throw error } finally { span.end() }
1-1: Nit: use .ts (no JSX).The file contains no JSX; rename to posts.ts to reduce unnecessary TSX parsing.
17-25: Consider fetch timeouts.Network calls can hang; add AbortController with a short timeout (e.g., 5s) around fetch to improve resilience.
Also applies to: 37-41
examples/react/start-otel/src/routes/api/users.ts (2)
57-59: Add basic caching for list endpoint.Reduce upstream load and improve perf.
- return json( - list.map((u) => ({ id: u.id, name: u.name, email: u.email })), - ) + return json( + list.map((u) => ({ id: u.id, name: u.name, email: u.email })), + { headers: { 'cache-control': 'public, max-age=60' } }, + )
45-51: Optional: add a fetch timeout and trace child span.Use AbortController and create a child span (e.g., tracer.startActiveSpan('GET /api/users')) for consistency with server tracing.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (8)
examples/react/start-otel/public/android-chrome-192x192.pngis excluded by!**/*.pngexamples/react/start-otel/public/android-chrome-512x512.pngis excluded by!**/*.pngexamples/react/start-otel/public/apple-touch-icon.pngis excluded by!**/*.pngexamples/react/start-otel/public/favicon-16x16.pngis excluded by!**/*.pngexamples/react/start-otel/public/favicon-32x32.pngis excluded by!**/*.pngexamples/react/start-otel/public/favicon.icois excluded by!**/*.icoexamples/react/start-otel/public/favicon.pngis excluded by!**/*.pngpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (43)
examples/react/start-otel/.gitignore(1 hunks)examples/react/start-otel/.prettierignore(1 hunks)examples/react/start-otel/.vscode/settings.json(1 hunks)examples/react/start-otel/README.md(1 hunks)examples/react/start-otel/docker-compose.yml(1 hunks)examples/react/start-otel/package.json(1 hunks)examples/react/start-otel/postcss.config.mjs(1 hunks)examples/react/start-otel/public/site.webmanifest(1 hunks)examples/react/start-otel/src/components/DefaultCatchBoundary.tsx(1 hunks)examples/react/start-otel/src/components/NotFound.tsx(1 hunks)examples/react/start-otel/src/components/PostError.tsx(1 hunks)examples/react/start-otel/src/components/UserError.tsx(1 hunks)examples/react/start-otel/src/otel.ts(1 hunks)examples/react/start-otel/src/routeTree.gen.ts(1 hunks)examples/react/start-otel/src/router.tsx(1 hunks)examples/react/start-otel/src/routes/__root.tsx(1 hunks)examples/react/start-otel/src/routes/_pathlessLayout.tsx(1 hunks)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsx(1 hunks)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsx(1 hunks)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsx(1 hunks)examples/react/start-otel/src/routes/api/users.$userId.ts(1 hunks)examples/react/start-otel/src/routes/api/users.ts(1 hunks)examples/react/start-otel/src/routes/customScript[.]js.ts(1 hunks)examples/react/start-otel/src/routes/deferred.tsx(1 hunks)examples/react/start-otel/src/routes/index.tsx(1 hunks)examples/react/start-otel/src/routes/posts.$postId.tsx(1 hunks)examples/react/start-otel/src/routes/posts.index.tsx(1 hunks)examples/react/start-otel/src/routes/posts.tsx(1 hunks)examples/react/start-otel/src/routes/posts_.$postId.deep.tsx(1 hunks)examples/react/start-otel/src/routes/redirect.tsx(1 hunks)examples/react/start-otel/src/routes/users.$userId.tsx(1 hunks)examples/react/start-otel/src/routes/users.index.tsx(1 hunks)examples/react/start-otel/src/routes/users.tsx(1 hunks)examples/react/start-otel/src/server.ts(1 hunks)examples/react/start-otel/src/styles/app.css(1 hunks)examples/react/start-otel/src/utils/loggingMiddleware.tsx(1 hunks)examples/react/start-otel/src/utils/posts.tsx(1 hunks)examples/react/start-otel/src/utils/seo.ts(1 hunks)examples/react/start-otel/src/utils/tracer.ts(1 hunks)examples/react/start-otel/src/utils/users.tsx(1 hunks)examples/react/start-otel/tailwind.config.mjs(1 hunks)examples/react/start-otel/tsconfig.json(1 hunks)examples/react/start-otel/vite.config.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
examples/react/start-otel/src/utils/tracer.tsexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsxexamples/react/start-otel/src/routes/api/users.$userId.tsexamples/react/start-otel/src/routes/posts.tsxexamples/react/start-otel/src/components/UserError.tsxexamples/react/start-otel/src/routes/posts.index.tsxexamples/react/start-otel/src/routes/redirect.tsxexamples/react/start-otel/vite.config.tsexamples/react/start-otel/src/routes/users.tsxexamples/react/start-otel/src/router.tsxexamples/react/start-otel/src/utils/posts.tsxexamples/react/start-otel/src/utils/loggingMiddleware.tsxexamples/react/start-otel/src/routes/posts.$postId.tsxexamples/react/start-otel/src/components/NotFound.tsxexamples/react/start-otel/src/routes/_pathlessLayout.tsxexamples/react/start-otel/src/routes/__root.tsxexamples/react/start-otel/src/utils/users.tsxexamples/react/start-otel/src/server.tsexamples/react/start-otel/src/routes/deferred.tsxexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsxexamples/react/start-otel/src/components/PostError.tsxexamples/react/start-otel/src/routes/customScript[.]js.tsexamples/react/start-otel/src/routes/posts_.$postId.deep.tsxexamples/react/start-otel/src/routeTree.gen.tsexamples/react/start-otel/src/routes/users.$userId.tsxexamples/react/start-otel/src/routes/users.index.tsxexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsxexamples/react/start-otel/src/components/DefaultCatchBoundary.tsxexamples/react/start-otel/src/otel.tsexamples/react/start-otel/src/routes/api/users.tsexamples/react/start-otel/src/routes/index.tsxexamples/react/start-otel/src/utils/seo.ts
examples/{react,solid}/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep example applications under examples/react/ and examples/solid/
Files:
examples/react/start-otel/src/utils/tracer.tsexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsxexamples/react/start-otel/src/routes/api/users.$userId.tsexamples/react/start-otel/README.mdexamples/react/start-otel/tsconfig.jsonexamples/react/start-otel/src/routes/posts.tsxexamples/react/start-otel/postcss.config.mjsexamples/react/start-otel/src/components/UserError.tsxexamples/react/start-otel/src/routes/posts.index.tsxexamples/react/start-otel/src/routes/redirect.tsxexamples/react/start-otel/vite.config.tsexamples/react/start-otel/tailwind.config.mjsexamples/react/start-otel/src/routes/users.tsxexamples/react/start-otel/src/router.tsxexamples/react/start-otel/src/utils/posts.tsxexamples/react/start-otel/src/utils/loggingMiddleware.tsxexamples/react/start-otel/src/routes/posts.$postId.tsxexamples/react/start-otel/src/components/NotFound.tsxexamples/react/start-otel/src/routes/_pathlessLayout.tsxexamples/react/start-otel/src/routes/__root.tsxexamples/react/start-otel/public/site.webmanifestexamples/react/start-otel/src/utils/users.tsxexamples/react/start-otel/src/server.tsexamples/react/start-otel/src/routes/deferred.tsxexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsxexamples/react/start-otel/src/components/PostError.tsxexamples/react/start-otel/src/routes/customScript[.]js.tsexamples/react/start-otel/src/routes/posts_.$postId.deep.tsxexamples/react/start-otel/src/routeTree.gen.tsexamples/react/start-otel/package.jsonexamples/react/start-otel/src/routes/users.$userId.tsxexamples/react/start-otel/src/routes/users.index.tsxexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsxexamples/react/start-otel/src/components/DefaultCatchBoundary.tsxexamples/react/start-otel/src/otel.tsexamples/react/start-otel/src/routes/api/users.tsexamples/react/start-otel/src/styles/app.cssexamples/react/start-otel/docker-compose.ymlexamples/react/start-otel/src/routes/index.tsxexamples/react/start-otel/src/utils/seo.ts
**/src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Place file-based routes under src/routes/ directories
Files:
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsxexamples/react/start-otel/src/routes/api/users.$userId.tsexamples/react/start-otel/src/routes/posts.tsxexamples/react/start-otel/src/routes/posts.index.tsxexamples/react/start-otel/src/routes/redirect.tsxexamples/react/start-otel/src/routes/users.tsxexamples/react/start-otel/src/routes/posts.$postId.tsxexamples/react/start-otel/src/routes/_pathlessLayout.tsxexamples/react/start-otel/src/routes/__root.tsxexamples/react/start-otel/src/routes/deferred.tsxexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsxexamples/react/start-otel/src/routes/customScript[.]js.tsexamples/react/start-otel/src/routes/posts_.$postId.deep.tsxexamples/react/start-otel/src/routes/users.$userId.tsxexamples/react/start-otel/src/routes/users.index.tsxexamples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsxexamples/react/start-otel/src/routes/api/users.tsexamples/react/start-otel/src/routes/index.tsx
**/package.json
📄 CodeRabbit inference engine (AGENTS.md)
Use workspace:* protocol for internal dependencies in package.json files
Files:
examples/react/start-otel/package.json
🧠 Learnings (2)
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript in strict mode with extensive type safety across the codebase
Applied to files:
examples/react/start-otel/tsconfig.json
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.
Applied to files:
examples/react/start-otel/.vscode/settings.jsonexamples/react/start-otel/src/routeTree.gen.ts
🧬 Code graph analysis (25)
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsx (3)
examples/react/start-otel/src/routes/_pathlessLayout.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsx (1)
Route(3-7)
examples/react/start-otel/src/routes/api/users.$userId.ts (4)
examples/react/start-otel/src/routes/api/users.ts (1)
Route(41-63)examples/react/start-otel/src/routes/users.$userId.tsx (1)
Route(6-26)examples/react/start-otel/src/server.ts (1)
fetch(7-26)examples/react/start-otel/src/utils/users.tsx (1)
User(1-5)
examples/react/start-otel/src/routes/posts.tsx (2)
examples/react/start-otel/src/routes/posts.$postId.tsx (1)
Route(6-13)examples/react/start-otel/src/utils/posts.tsx (1)
fetchPosts(33-53)
examples/react/start-otel/src/routes/posts.index.tsx (4)
examples/react/start-otel/src/routes/index.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/posts.$postId.tsx (1)
Route(6-13)examples/react/start-otel/src/routes/posts.tsx (1)
Route(4-7)examples/react/start-otel/src/routes/users.index.tsx (1)
Route(3-5)
examples/react/start-otel/src/routes/redirect.tsx (2)
examples/react/start-otel/src/routes/posts.index.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/posts.tsx (1)
Route(4-7)
examples/react/start-otel/src/routes/users.tsx (3)
examples/react/start-otel/src/routes/api/users.ts (1)
Route(41-63)examples/react/start-otel/src/routes/users.$userId.tsx (1)
Route(6-26)examples/react/start-otel/src/utils/users.tsx (1)
User(1-5)
examples/react/start-otel/src/router.tsx (2)
examples/react/start-otel/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/react/start-otel/src/components/NotFound.tsx (1)
NotFound(3-25)
examples/react/start-otel/src/utils/posts.tsx (2)
examples/react/start-otel/src/server.ts (1)
fetch(7-26)examples/react/start-otel/src/utils/tracer.ts (1)
tracer(3-3)
examples/react/start-otel/src/routes/posts.$postId.tsx (4)
examples/react/start-otel/src/routes/posts_.$postId.deep.tsx (1)
Route(5-12)examples/react/start-otel/src/utils/posts.tsx (1)
fetchPost(12-31)examples/react/start-otel/src/components/PostError.tsx (1)
PostErrorComponent(4-6)examples/react/start-otel/src/components/NotFound.tsx (1)
NotFound(3-25)
examples/react/start-otel/src/components/NotFound.tsx (4)
e2e/react-start/basic-cloudflare/src/components/NotFound.tsx (1)
NotFound(3-25)e2e/react-start/server-routes/src/components/NotFound.tsx (1)
NotFound(3-25)examples/react/start-basic-cloudflare/src/components/NotFound.tsx (1)
NotFound(3-25)e2e/solid-start/server-routes/src/components/NotFound.tsx (1)
NotFound(3-25)
examples/react/start-otel/src/routes/_pathlessLayout.tsx (3)
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsx (1)
Route(3-7)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsx (1)
Route(3-7)
examples/react/start-otel/src/routes/__root.tsx (4)
examples/react/start-otel/src/routes/customScript[.]js.ts (1)
Route(3-15)examples/react/start-otel/src/utils/seo.ts (1)
seo(1-33)examples/react/start-otel/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/react/start-otel/src/components/NotFound.tsx (1)
NotFound(3-25)
examples/react/start-otel/src/server.ts (1)
examples/react/start-otel/src/utils/tracer.ts (1)
tracer(3-3)
examples/react/start-otel/src/routes/deferred.tsx (1)
examples/solid/start-basic/src/routes/deferred.tsx (2)
Deferred(31-62)deferredStuff(19-27)
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsx (3)
examples/react/start-otel/src/routes/_pathlessLayout.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsx (1)
Route(3-7)
examples/react/start-otel/src/components/PostError.tsx (1)
examples/react/start-basic/src/components/PostError.tsx (1)
PostErrorComponent(4-6)
examples/react/start-otel/src/routes/customScript[.]js.ts (1)
examples/react/start-otel/src/routes/__root.tsx (1)
Route(15-63)
examples/react/start-otel/src/routes/posts_.$postId.deep.tsx (3)
examples/react/start-otel/src/routes/posts.$postId.tsx (1)
Route(6-13)examples/react/start-otel/src/utils/posts.tsx (1)
fetchPost(12-31)examples/react/start-otel/src/components/PostError.tsx (1)
PostErrorComponent(4-6)
examples/react/start-otel/src/routeTree.gen.ts (2)
e2e/react-router/js-only-file-based/src/routeTree.gen.js (12)
PathlessLayoutRoute(21-24)IndexRoute(30-34)PostsIndexRoute(35-39)PostsPostIdRoute(40-44)PathlessLayoutNestedLayoutRoute(45-49)PathlessLayoutNestedLayoutRouteBRoute(50-55)PathlessLayoutNestedLayoutRouteARoute(56-61)PathlessLayoutRouteWithChildren(86-88)PathlessLayoutNestedLayoutRouteWithChildren(77-80)PathlessLayoutNestedLayoutRouteChildren(72-75)PathlessLayoutRouteChildren(82-84)rootRouteChildren(90-94)examples/react/start-otel/src/router.tsx (1)
getRouter(6-15)
examples/react/start-otel/src/routes/users.$userId.tsx (4)
examples/react/start-otel/src/routes/api/users.$userId.ts (1)
Route(5-32)examples/react/start-otel/src/utils/users.tsx (1)
User(1-5)examples/react/start-otel/src/components/UserError.tsx (1)
UserErrorComponent(4-6)examples/react/start-otel/src/components/NotFound.tsx (1)
NotFound(3-25)
examples/react/start-otel/src/routes/users.index.tsx (2)
examples/react/start-otel/src/routes/api/users.ts (1)
Route(41-63)examples/react/start-otel/src/routes/users.$userId.tsx (1)
Route(6-26)
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsx (3)
examples/react/start-otel/src/routes/_pathlessLayout.tsx (1)
Route(3-5)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsx (1)
Route(3-7)examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsx (1)
Route(3-7)
examples/react/start-otel/src/components/DefaultCatchBoundary.tsx (3)
e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/react/start-basic-cloudflare/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)packages/react-router/src/CatchBoundary.tsx (1)
CatchBoundaryImpl(31-78)
examples/react/start-otel/src/routes/api/users.ts (4)
packages/start-server-core/src/request-response.ts (1)
getRequestHeaders(77-80)examples/react/start-otel/src/routes/api/users.$userId.ts (1)
Route(5-32)examples/react/start-otel/src/server.ts (1)
fetch(7-26)examples/react/start-otel/src/utils/users.tsx (1)
User(1-5)
examples/react/start-otel/src/routes/index.tsx (1)
examples/react/start-otel/src/routes/__root.tsx (1)
Route(15-63)
🪛 Biome (2.1.2)
examples/react/start-otel/src/routes/deferred.tsx
[error] 43-43: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 53-53: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
🪛 LanguageTool
examples/react/start-otel/README.md
[uncategorized] ~25-~25: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...TanStack Router app. In production, the markdown doc pages are fetched from the GitHub r...
(MARKDOWN_NNP)
[style] ~71-~71: Using many exclamation marks might seem excessive (in this case: 8 exclamation marks for a text that’s 1725 characters long)
Context: ...e manually reloaded in the browser. > [!WARNING] > You will need to update the docs/config.json file (in the project's repo) if you add a new doc page!
(EN_EXCESSIVE_EXCLAMATION)
🪛 markdownlint-cli2 (0.18.1)
examples/react/start-otel/README.md
46-46: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
53-53: Blank line inside blockquote
(MD028, no-blanks-blockquote)
66-66: Bare URL used
(MD034, no-bare-urls)
70-70: Blank line inside blockquote
(MD028, no-blanks-blockquote)
🔇 Additional comments (29)
examples/react/start-otel/.gitignore (1)
20-20: Clarify the purpose of.tanstackentry.The
.tanstackentry is vague. Is this a cache directory or build artifact that should be ignored, or is this a leftover entry that should be removed?examples/react/start-otel/vite.config.ts (1)
10-18: No issues found — configuration is correct per official TanStack Start documentation.TanStack Start requires you to install and add a React Vite plugin (e.g. @vitejs/plugin-react) to vite.config.ts, with viteReact() placed after tanstackStart() in the plugins array. The current configuration follows this pattern correctly and requires no changes.
examples/react/start-otel/.vscode/settings.json (1)
1-11: LGTM!The VSCode configuration correctly excludes the autogenerated
routeTree.gen.tsfrom file watching, search indexing, and marks it readonly to prevent manual edits. This aligns with TanStack Router best practices.Based on learnings
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-b.tsx (1)
1-11: LGTM!The route component follows TanStack Router conventions and matches the pattern established by sibling routes in the example.
examples/react/start-otel/.prettierignore (1)
1-4: LGTM!The Prettier ignore patterns appropriately exclude generated files, build artifacts, and lock files from formatting.
examples/react/start-otel/postcss.config.mjs (1)
1-6: LGTM!Standard PostCSS configuration with Tailwind CSS and Autoprefixer using default plugin settings.
examples/react/start-otel/docker-compose.yml (2)
1-12: Docker Compose configuration is appropriate for the example.The Jaeger setup correctly exposes OTLP ports and enables the OTLP collector, which aligns with the OpenTelemetry configuration in
src/otel.tsthat targets port 4318.
4-4: Upgrade Jaeger image to latest stable version; verify security impact first.The latest stable Jaeger is 2.11.0 (v2 series), while version 1.57 is from the older v1 branch. CVE-2024-24787, a Go/agent build issue, was fixed in agent versions 1.57.0, so 1.57 itself should have this patch. However, CVE-2023-36656 (XSS in Jaeger UI) is fixed in jaeger-ui ≥ v1.31.0, and images bundling older UI may be affected. Before upgrading, manually verify whether the all-in-one:1.57 image contains a vulnerable UI version, and review the changelog between 1.57 and 2.11.0 for breaking changes.
examples/react/start-otel/src/utils/tracer.ts (1)
1-3: LGTM!The tracer utility correctly uses the OpenTelemetry API to obtain a named tracer instance, following OpenTelemetry best practices.
examples/react/start-otel/src/routes/posts.index.tsx (1)
3-9: LGTM for the index route.Path and component wiring look correct and consistent with other index routes.
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout/route-a.tsx (1)
3-11: LGTM for pathless nested route.Matches the pattern used by sibling routes; component is scoped and simple.
examples/react/start-otel/src/routes/api/users.$userId.ts (1)
8-29: LGTM with minor observations.The handler correctly fetches and validates user data, with appropriate error handling. The implementation is suitable for an example/demo application.
Note: The broad catch block returns 404 for all errors. In a production application, you might want to distinguish between actual "not found" responses (404) and other failures (500), but for a demonstration example this simplification is acceptable.
examples/react/start-otel/src/utils/loggingMiddleware.tsx (1)
3-41: LGTM!The middleware correctly measures client-to-server round-trip timing by capturing timestamps at each boundary and computing durations. The composition pattern using
preLogMiddlewareensures proper context flow between client and server.examples/react/start-otel/src/routes/deferred.tsx (1)
40-55: LGTM!The deferred loading pattern correctly uses
SuspensewithAwaitto handle progressive rendering of async data. The static analysis warnings about thechildrenprop can be safely ignored—theAwaitcomponent from TanStack Router intentionally accepts a render function via thechildrenprop, matching the established API pattern.examples/react/start-otel/src/utils/users.tsx (1)
1-5: LGTM!Clean and straightforward type definition for the User entity used throughout the example.
examples/react/start-otel/src/routes/index.tsx (1)
3-13: LGTM!Simple and appropriate home route for the example application.
examples/react/start-otel/src/routes/_pathlessLayout.tsx (1)
3-16: LGTM!Correctly implements a pathless layout route with proper
Outletplacement for nested routes. The implementation aligns with the nested pathless route structure shown in related files.examples/react/start-otel/src/routes/redirect.tsx (1)
1-9: LGTM!The redirect route is correctly implemented using TanStack Router's redirect pattern in beforeLoad.
examples/react/start-otel/src/routes/posts.tsx (2)
4-7: LGTM!Route configuration is correct with proper loader and component assignment.
9-38: LGTM!The component correctly renders the post list with navigation links. The addition of the non-existent post on line 15 is intentional for demonstrating error handling behavior in this example.
examples/react/start-otel/src/routes/users.index.tsx (1)
1-19: LGTM!Simple and clean index route implementation with proper navigation link.
examples/react/start-otel/src/components/NotFound.tsx (1)
1-25: LGTM!NotFound component follows the established pattern used across other examples in the codebase. Implementation is clean and correct.
examples/react/start-otel/src/components/PostError.tsx (1)
1-6: LGTM!PostErrorComponent correctly implements the error handling pattern consistent with other examples in the codebase.
examples/react/start-otel/src/routes/customScript[.]js.ts (1)
1-15: LGTM!The custom script route correctly demonstrates serving JavaScript dynamically with proper Content-Type headers. The script is properly referenced in the root route configuration.
examples/react/start-otel/src/routes/_pathlessLayout/_nested-layout.tsx (1)
1-34: LGTM!The nested layout correctly implements TanStack Router's pathless layout pattern with proper navigation and nested route rendering via Outlet.
examples/react/start-otel/src/routes/users.tsx (1)
25-28: Ensure the detail route surfaces NotFound (not generic error).This dummy entry is great for demoing 404s. Make sure /users/$userId throws notFound() on a 404 so the route’s notFoundComponent renders; otherwise this link will hit the error boundary. See suggested fix in users.$userId.tsx.
examples/react/start-otel/src/styles/app.css (1)
19-21: A11y toggle code is missing—the .using-mouse class is never applied.The CSS rule at lines 19–21 has no effect because no JavaScript in the codebase applies the
.using-mouseclass to the DOM. There are no event listeners for mouse/keyboard detection inexamples/react/start-otel/src/. ThecustomScript.jsendpoint only outputs a placeholder log, not a toggle implementation.Either remove this orphaned CSS rule, or implement the missing JavaScript toggle that detects mouse vs. keyboard input and applies
.using-mouseaccordingly.examples/react/start-otel/src/routes/posts.$postId.tsx (1)
6-13: Route wiring and loader look solid.Loader delegates to server fn, dedicated notFound and error components are set. Good separation of concerns.
examples/react/start-otel/src/routeTree.gen.ts (1)
1-10: Autogenerated file — no manual review.routeTree.gen.ts is generated and should not be edited.
Based on learnings
| /public/build# Sentry Config File | ||
| .env.sentry-build-plugin |
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.
Fix comment placement and formatting.
The Sentry Config File comment should be on its own line before .env.sentry-build-plugin, not appended to the end of /public/build. Additionally, .gitignore files should end with a newline.
-/public/build# Sentry Config File
+/public/build
+# Sentry Config File
.env.sentry-build-pluginEnsure the file ends with a newline after the last entry.
🤖 Prompt for AI Agents
In examples/react/start-otel/.gitignore around lines 14 to 15, the inline
comment "Sentry Config File" is appended to the `/public/build` entry and the
file is missing a trailing newline; move the comment to its own line immediately
above `.env.sentry-build-plugin` (e.g. a standalone line containing "Sentry
Config File") so it does not annotate `/public/build`, and ensure the file ends
with a single newline character after the last entry.
| "scripts": { | ||
| "dev": "vite dev", | ||
| "build": "vite build && tsc --noEmit", | ||
| "start": "node .output/server/index.mjs" | ||
| }, |
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.
🧩 Analysis chain
Verify the start script output path.
Vite usually emits to dist/ by default; the script starts .output/server/index.mjs. Ensure your build config writes to .output/server.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check for vite config and SSR/output settings
fd -HI '^vite\.config\.(ts|mjs|js)$' .
rg -n --no-messages -C2 'outDir|ssr|build' $(fd -HI '^vite\.config\.(ts|mjs|js)$' . || echo)
# Confirm referenced file exists (if already built)
test -f examples/react/start-otel/.output/server/index.mjs && echo "Found .output/server/index.mjs" || echo "Note: .output/server/index.mjs not present (build not run or outDir differs)."Length of output: 33906
#!/bin/bash
# Extract vite config for start-otel specifically
cat examples/react/start-otel/vite.config.ts
---
🏁 Script executed:
```shell
#!/bin/bash
cat examples/react/start-otel/vite.config.ts
Length of output: 416
---
🏁 Script executed:
```shell
#!/bin/bash
# Check the package.json for start-otel to confirm the start script
cat examples/react/start-otel/package.json
echo "---"
# Check a few other start examples to see the pattern
cat examples/react/start-basic/package.json 2>/dev/null | grep -A5 '"scripts"'
Length of output: 1180
---
🌐 Web query:
```web
TanStack Start tanstackStart vite plugin outDir .output configuration
💡 Result:
Short answer: TanStack Start's Vite plugin no longer always produces a .output folder by itself — to get the traditional .output server bundle you must enable Nitro in your Vite build (either Nitro v3's vite plugin or the compatibility @tanstack/nitro-v2-vite-plugin). Add the Nitro Vite plugin alongside tanstackStart and the framework plugin; Nitro will generate the .output/server entry (or a dist/ server bundle if you don't use the Nitro plugin). [1][2][3]
Example (vite.config.ts):
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import { nitro } from 'nitro/vite' // or use @tanstack/nitro-v2-vite-plugin
import viteReact from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
tanstackStart(),
nitro({ /* nitro config / preset */ }),
viteReact(),
],
})
Notes:
- Use @tanstack/nitro-v2-vite-plugin as a fallback for older Start RCs; Nitro v3's vite plugin is the official path. [1][2]
- If you run build without the Nitro plugin you may get dist/ (server + client) and not .output; adding the Nitro plugin makes .output/server/index.mjs available for Node/Bun deployment. [3][2]
Sources:
- TanStack Start hosting docs (Nitro + nitro-v2 plugin recommendation). [1][2]
- Community writeup on missing .output when Nitro isn't used. [3]
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for nitro references in start-otel vite config
echo "=== start-otel vite.config.ts ==="
grep -i nitro examples/react/start-otel/vite.config.ts || echo "(no nitro reference)"
echo ""
echo "=== Checking start-basic vite.config.ts ==="
cat examples/react/start-basic/vite.config.ts
echo ""
echo "=== Checking start-basic for nitro import ==="
grep -i nitro examples/react/start-basic/vite.config.ts || echo "(no nitro reference found)"
Length of output: 590
Add Nitro plugin to vite.config.ts or update the start script path to match actual build output.
The vite.config.ts for start-otel uses tanstackStart() but lacks the Nitro plugin required to produce the .output/server/index.mjs that the start script expects. Without Nitro, Vite builds to dist/ by default, causing the start script to fail. Either add the Nitro plugin (e.g., @tanstack/nitro-v2-vite-plugin or nitro/vite) to generate .output/server, or update the start script to reference the actual build output directory.
🤖 Prompt for AI Agents
In examples/react/start-otel/package.json around lines 6 to 10, the "start"
script expects .output/server/index.mjs but the Vite config uses tanstackStart()
without a Nitro plugin so the build produces dist/ instead; either add a Nitro
Vite plugin (for example @tanstack/nitro-v2-vite-plugin or nitro/vite) to
vite.config.ts so the build emits .output/server/index.mjs, or change the
"start" script to point to the actual build output (e.g., dist/ or the correct
server entry) to match what the current build produces.
| "dependencies": { | ||
| "@opentelemetry/api": "^1.9.0", | ||
| "@opentelemetry/exporter-trace-otlp-proto": "^0.206.0", | ||
| "@opentelemetry/sdk-node": "^0.206.0", | ||
| "@opentelemetry/sdk-trace-node": "^2.1.0", | ||
| "@tanstack/react-router": "^1.133.15", | ||
| "@tanstack/react-router-devtools": "^1.133.15", | ||
| "@tanstack/react-start": "^1.133.15", | ||
| "react": "^19.0.0", | ||
| "react-dom": "^19.0.0", | ||
| "tailwind-merge": "^2.6.0", | ||
| "zod": "^3.24.2" | ||
| }, |
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.
🛠️ Refactor suggestion | 🟠 Major
Use workspace: for internal TanStack packages in the monorepo.*
This keeps examples pinned to local workspace builds and avoids version drift.
- "@tanstack/react-router": "^1.133.15",
- "@tanstack/react-router-devtools": "^1.133.15",
- "@tanstack/react-start": "^1.133.15",
+ "@tanstack/react-router": "workspace:*",
+ "@tanstack/react-router-devtools": "workspace:*",
+ "@tanstack/react-start": "workspace:*",As per coding guidelines.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "dependencies": { | |
| "@opentelemetry/api": "^1.9.0", | |
| "@opentelemetry/exporter-trace-otlp-proto": "^0.206.0", | |
| "@opentelemetry/sdk-node": "^0.206.0", | |
| "@opentelemetry/sdk-trace-node": "^2.1.0", | |
| "@tanstack/react-router": "^1.133.15", | |
| "@tanstack/react-router-devtools": "^1.133.15", | |
| "@tanstack/react-start": "^1.133.15", | |
| "react": "^19.0.0", | |
| "react-dom": "^19.0.0", | |
| "tailwind-merge": "^2.6.0", | |
| "zod": "^3.24.2" | |
| }, | |
| "dependencies": { | |
| "@opentelemetry/api": "^1.9.0", | |
| "@opentelemetry/exporter-trace-otlp-proto": "^0.206.0", | |
| "@opentelemetry/sdk-node": "^0.206.0", | |
| "@opentelemetry/sdk-trace-node": "^2.1.0", | |
| "@tanstack/react-router": "workspace:*", | |
| "@tanstack/react-router-devtools": "workspace:*", | |
| "@tanstack/react-start": "workspace:*", | |
| "react": "^19.0.0", | |
| "react-dom": "^19.0.0", | |
| "tailwind-merge": "^2.6.0", | |
| "zod": "^3.24.2" | |
| }, |
🤖 Prompt for AI Agents
In examples/react/start-otel/package.json around lines 11 to 23, the three
internal TanStack packages (@tanstack/react-router,
@tanstack/react-router-devtools, @tanstack/react-start) are pinned to external
versions; replace their version strings with "workspace:*" so the example uses
local monorepo builds, then run the workspace package manager (pnpm/npm/yarn) to
refresh lockfiles; keep other dependencies unchanged.
🧩 Analysis chain
Align OpenTelemetry package versions (avoid mixing 0.x, 1.x, 2.x).
Mixing majors across OTel core packages can cause runtime incompatibilities. Align all @opentelemetry/* core packages to a single compatible line and avoid redundant SDKs (sdk-node vs sdk-trace-node).
Example (illustrative — pick one coherent version set used by the repo):
- "@opentelemetry/api": "^1.9.0",
- "@opentelemetry/exporter-trace-otlp-proto": "^0.206.0",
- "@opentelemetry/sdk-node": "^0.206.0",
- "@opentelemetry/sdk-trace-node": "^2.1.0",
+ "@opentelemetry/api": "^1.9.0",
+ "@opentelemetry/sdk-node": "<aligned-version>",
+ "@opentelemetry/exporter-trace-otlp-proto": "<aligned-version>",
+ // Remove if using sdk-node:
+ // "@opentelemetry/sdk-trace-node": "<aligned-version>",
+ // Optional but recommended for stable keys:
+ "@opentelemetry/semantic-conventions": "<aligned-version>",🏁 Script executed:
#!/bin/bash
set -euo pipefail
pkg=examples/react/start-otel/package.json
echo "OTel package versions in $pkg"
jq -r '
[.dependencies,.devDependencies]|add|to_entries
| map(select(.key|startswith("@opentelemetry/")))
| .[] | "\(.key) = \(.value)"
' "$pkg" | sort
echo
echo "Distinct major.minor groups:"
jq -r '
[.dependencies,.devDependencies]|add|to_entries
| map(select(.key|startswith("@opentelemetry/")))
| .[] | .value
' "$pkg" | sed -E 's/[^0-9]*([0-9]+)\.([0-9]+).*/\1.\2/' | sort -uLength of output: 261
</web_search>
🏁 Script executed:
#!/bin/bash
# Check for internal workspace dependencies in this package.json
echo "=== Internal dependencies in examples/react/start-otel/package.json ==="
jq -r '.dependencies | to_entries[] | select(.value | startswith("workspace:")) | "\(.key): \(.value)"' examples/react/start-otel/package.json 2>/dev/null || echo "No workspace dependencies found"
echo
# Find all package.json files in the repo and extract OTel versions
echo "=== OpenTelemetry versions across all package.json files ==="
find . -name "package.json" -type f | while read -r pf; do
otel_count=$(jq -r '[.dependencies,.devDependencies]|add|to_entries|map(select(.key|startswith("@opentelemetry/")))|length' "$pf" 2>/dev/null || echo "0")
if [ "$otel_count" -gt 0 ]; then
echo "$pf:"
jq -r '[.dependencies,.devDependencies]|add|to_entries|map(select(.key|startswith("@opentelemetry/")))|.[]|"\(.key) = \(.value)"' "$pf" 2>/dev/null | sort
echo
fi
done | head -100Length of output: 366
🌐 Web query:
OpenTelemetry compatibility matrix versions 0.206 1.9 2.1
💡 Result:
Do you mean the OpenTelemetry package compatibilities between the "experimental" 0.* series and the "stable" 1.* / 2.* series (SDK/API/semantic-conventions packages)? If so, can you confirm which language or package (Java, JS, Python, semantic-conventions, Collector, etc.) you need the matrix for?
🌐 Web query:
JavaScript Node.js OpenTelemetry sdk-node sdk-trace-node compatibility
💡 Result:
Short answer
- @opentelemetry/sdk-node (NodeSDK) is a wrapper/kit that composes the OpenTelemetry JS SDK pieces for Node — it commonly uses @opentelemetry/sdk-trace-node for tracing functionality. Use sdk-node + sdk-trace-node together (they’re designed to interoperate). [1][2]
Compatibility guidance
- Match major versions: keep @opentelemetry/* packages on the same major (e.g., 2.x) to avoid API/behavior mismatches. The 2.0 SDK release introduces breaking changes, so mix-and-matching 1.x and 2.x packages is not recommended. [1]
- Node runtime and TS requirements changed in SDK 2.x: minimum Node.js is ^18.19.0 || >=20.6.0 and TypeScript >=5.0.4; check the SDK 2.x migration guide before upgrading. [1]
Practical notes
- If you use the Node getting-started examples, NodeSDK imports ConsoleSpanExporter (or other exporters) from @opentelemetry/sdk-trace-node and registers auto-instrumentations via @opentelemetry/auto-instrumentations-node — that’s the normal composition. [2][3]
- For custom/manual tracing you can use @opentelemetry/sdk-trace-base directly, but sdk-trace-node provides Node-specific defaults (async_hooks context manager, resource detection, instrumentations). [1][2]
If you want I can:
- check your package.json and recommend exact versions to align (e.g., upgrade path from 1.x → 2.x), or
- fetch the 2.x migration guide and list the breaking-change steps.
Sources
[1] OpenTelemetry blog — Announcing the OpenTelemetry JavaScript SDK 2.0 (release notes & migration).
[2] OpenTelemetry JS docs — Node.js getting started / package list (describes @opentelemetry/sdk-node and @opentelemetry/sdk-trace-node).
[3] OpenTelemetry JS repo/docs — @opentelemetry/sdk-trace-base vs sdk-trace-node (module descriptions).
Align OpenTelemetry package versions to a single major (currently mixing 0.x, 1.x, 2.x).
Matching major versions is required to avoid API/behavior mismatches, and mix-and-matching 1.x and 2.x packages is not recommended. Your package.json currently has:
@opentelemetry/api: ^1.9.0 (1.x)@opentelemetry/exporter-trace-otlp-proto: ^0.206.0 (0.x)@opentelemetry/sdk-node: ^0.206.0 (0.x)@opentelemetry/sdk-trace-node: ^2.1.0 (2.x)
@opentelemetry/sdk-node and @opentelemetry/sdk-trace-node are designed to interoperate and should be used together. Align all @opentelemetry/* packages to the same major version (recommend 2.x as the latest stable).
🤖 Prompt for AI Agents
In examples/react/start-otel/package.json around lines 11 to 23, the
OpenTelemetry dependencies use mixed major versions (0.x, 1.x, 2.x) which can
cause API/behavior mismatches; update all @opentelemetry/* packages to the same
major (recommend migrating @opentelemetry/api,
@opentelemetry/exporter-trace-otlp-proto, @opentelemetry/sdk-node, and
@opentelemetry/sdk-trace-node to matching 2.x releases), adjust their version
ranges consistently (e.g., ^2.x), run npm install to update lockfile, and run
tests/build to verify compatibility.
| "name": "", | ||
| "short_name": "", |
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.
Add placeholder values for PWA manifest names.
The name and short_name fields are empty strings, which may cause PWA installation prompts to display incorrectly or fail to appear. For an example project, consider adding descriptive placeholder values.
Apply this diff to add example values:
- "name": "",
- "short_name": "",
+ "name": "TanStack Router OTEL Example",
+ "short_name": "OTEL Example",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "name": "", | |
| "short_name": "", | |
| "name": "TanStack Router OTEL Example", | |
| "short_name": "OTEL Example", |
🤖 Prompt for AI Agents
In examples/react/start-otel/public/site.webmanifest around lines 2 to 3, the
PWA manifest's "name" and "short_name" fields are empty; update them to
meaningful placeholder values so install prompts and UI show correctly (e.g.,
set "name" to a descriptive project title and "short_name" to a shorter display
label). Ensure the values are plain strings, keep short_name under ~12
characters for display, and save the manifest.
| # Welcome to TanStack.com! | ||
|
|
||
| This site is built with TanStack Router! | ||
|
|
||
| - [TanStack Router Docs](https://tanstack.com/router) | ||
|
|
||
| It's deployed automagically with Netlify! | ||
|
|
||
| - [Netlify](https://netlify.com/) | ||
|
|
||
| ## Development | ||
|
|
||
| From your terminal: | ||
|
|
||
| ```sh | ||
| pnpm install | ||
| pnpm dev | ||
| ``` | ||
|
|
||
| This starts your app in development mode, rebuilding assets on file changes. | ||
|
|
||
| ## Editing and previewing the docs of TanStack projects locally | ||
|
|
||
| The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app. | ||
| In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system. | ||
|
|
||
| Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally : | ||
|
|
||
| 1. Create a new directory called `tanstack`. | ||
|
|
||
| ```sh | ||
| mkdir tanstack | ||
| ``` | ||
|
|
||
| 2. Enter the directory and clone this repo and the repo of the project there. | ||
|
|
||
| ```sh | ||
| cd tanstack | ||
| git clone git@github.com:TanStack/tanstack.com.git | ||
| git clone git@github.com:TanStack/form.git | ||
| ``` | ||
|
|
||
| > [!NOTE] | ||
| > Your `tanstack` directory should look like this: | ||
| > | ||
| > ``` | ||
| > tanstack/ | ||
| > | | ||
| > +-- form/ | ||
| > | | ||
| > +-- tanstack.com/ | ||
| > ``` | ||
|
|
||
| > [!WARNING] | ||
| > Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found. | ||
|
|
||
| 3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode: | ||
|
|
||
| ```sh | ||
| cd tanstack.com | ||
| pnpm i | ||
| # The app will run on https://localhost:3000 by default | ||
| pnpm dev | ||
| ``` | ||
|
|
||
| 4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`. | ||
|
|
||
| > [!NOTE] | ||
| > The updated pages need to be manually reloaded in the browser. | ||
|
|
||
| > [!WARNING] | ||
| > You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page! |
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.
Replace README content to match the OTEL example.
The README describes the TanStack.com documentation site workflow, including instructions for editing docs across TanStack projects. However, this is the start-otel example demonstrating OpenTelemetry integration with TanStack Router, not the tanstack.com website.
The README should describe:
- What the OTEL example demonstrates
- How to run the example (
pnpm install,pnpm dev) - How to start the Jaeger instance (Docker Compose)
- How to view traces
- Any specific instrumentation patterns demonstrated
Apply this diff to provide appropriate documentation:
-# Welcome to TanStack.com!
+# TanStack Start + OpenTelemetry Example
-This site is built with TanStack Router!
+This example demonstrates OpenTelemetry tracing integration with TanStack Router and React Start.
-- [TanStack Router Docs](https://tanstack.com/router)
-
-It's deployed automagically with Netlify!
-
-- [Netlify](https://netlify.com/)
+## Features
+
+- Manual OpenTelemetry instrumentation
+- Jaeger for trace visualization
+- Server-side tracing with TanStack Start
## Development
-From your terminal:
+1. Start the Jaeger instance:
```sh
-pnpm install
-pnpm dev
+docker-compose up -d-This starts your app in development mode, rebuilding assets on file changes.
-## Editing and previewing the docs of TanStack projects locally
-The documentations for all TanStack projects except for React Charts are hosted on https://tanstack.com, powered by this TanStack Router app.
-In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system.
-Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's TanStack/form) and preview them locally :
-1. Create a new directory called tanstack.
-sh -mkdir tanstack -
-2. Enter the directory and clone this repo and the repo of the project there.
+2. Install dependencies and start the dev server:
-cd tanstack
-git clone git@github.com:TanStack/tanstack.com.git
-git clone git@github.com:TanStack/form.git
+pnpm install
+pnpm dev-> [!NOTE]
-> Your tanstack directory should look like this:
->
-> -> tanstack/ -> | -> +-- form/ -> | -> +-- tanstack.com/ ->
-> [!WARNING]
-> Make sure the name of the directory in your local file system matches the name of the project's repo. For example, tanstack/form must be cloned into form (this is the default) instead of some-other-name, because that way, the doc pages won't be found.
-3. Enter the tanstack/tanstack.com directory, install the dependencies and run the app in dev mode:
+3. Open the application at http://localhost:3000
-sh -cd tanstack.com -pnpm i -# The app will run on https://localhost:3000 by default -pnpm dev -
+4. View traces in Jaeger at http://localhost:16686
-4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in tanstack/form/docs.
+## Resources
-> [!NOTE]
-> The updated pages need to be manually reloaded in the browser.
-> [!WARNING]
-> You will need to update the docs/config.json file (in the project's repo) if you add a new doc page!
+- TanStack Router Docs
+- OpenTelemetry JavaScript
+- Jaeger
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 LanguageTool</summary>
[uncategorized] ~25-~25: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...TanStack Router app. In production, the markdown doc pages are fetched from the GitHub r...
(MARKDOWN_NNP)
---
[style] ~71-~71: Using many exclamation marks might seem excessive (in this case: 8 exclamation marks for a text that’s 1725 characters long)
Context: ...e manually reloaded in the browser. > [!WARNING] > You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page!
(EN_EXCESSIVE_EXCLAMATION)
</details>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>
46-46: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
---
53-53: Blank line inside blockquote
(MD028, no-blanks-blockquote)
---
66-66: Bare URL used
(MD034, no-bare-urls)
---
70-70: Blank line inside blockquote
(MD028, no-blanks-blockquote)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
In examples/react/start-otel/README.md around lines 1-72, the README currently
contains the tanstack.com docs workflow instead of documentation for the
OpenTelemetry example; replace that top section with a focused OTEL README that
explains what the example demonstrates (OpenTelemetry + TanStack Router
instrumentation patterns), how to run it (pnpm install, pnpm dev), how to start
Jaeger (docker-compose up -d), where to view traces (http://localhost:16686) and
any specific instrumentation patterns used (e.g., router span creation, manual
spans for loaders/actions), and add a short Resources section linking to
TanStack Router docs, OpenTelemetry JS, and Jaeger.
</details>
<!-- This is an auto-generated comment by CodeRabbit -->
| import { createFileRoute } from '@tanstack/react-router' | ||
| import { NotFound } from 'src/components/NotFound' | ||
| import { UserErrorComponent } from 'src/components/UserError' | ||
| import type { User } from '../utils/users' |
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.
Make notFoundComponent reachable and align import aliases.
On a 404, throw notFound() and preserve it through the catch; otherwise the notFoundComponent never renders. Also align imports to the "~" alias used elsewhere.
-import { createFileRoute } from '@tanstack/react-router'
-import { NotFound } from 'src/components/NotFound'
-import { UserErrorComponent } from 'src/components/UserError'
+import { createFileRoute, isNotFound, notFound } from '@tanstack/react-router'
+import { NotFound } from '~/components/NotFound'
+import { UserErrorComponent } from '~/components/UserError'
import type { User } from '../utils/users'
export const Route = createFileRoute('/users/$userId')({
loader: async ({ params: { userId } }) => {
- try {
- const res = await fetch('/api/users/' + userId)
- if (!res.ok) {
- throw new Error('Unexpected status code')
- }
-
- const data = await res.json()
-
- return data as User
- } catch {
- throw new Error('Failed to fetch user')
- }
+ try {
+ const res = await fetch('/api/users/' + userId)
+ if (res.status === 404) {
+ throw notFound()
+ }
+ if (!res.ok) {
+ throw new Error('Unexpected status code')
+ }
+ const data = (await res.json()) as User
+ return data
+ } catch (e) {
+ if (isNotFound(e)) throw e
+ throw new Error('Failed to fetch user')
+ }
},
errorComponent: UserErrorComponent,
component: UserComponent,
notFoundComponent: () => {
return <NotFound>User not found</NotFound>
},
})Also applies to: 6-20
🤖 Prompt for AI Agents
In examples/react/start-otel/src/routes/users.$userId.tsx around lines 1-4 (and
also apply to lines 6-20), the route loader currently doesn't throw notFound()
on a 404 and the imports use relative paths instead of the project "~" alias;
update the loader to detect a 404 and throw notFound() so the react-router
notFoundComponent can render, and ensure any try/catch preserves and rethrows
notFound() (i.e., if (response.status === 404) throw notFound(); catch(e){ if
(isNotFound(e)) throw e; throw e; }), and change imports to use the "~" alias
(e.g., import { NotFound } from '~/components/NotFound' and import {
UserErrorComponent } from '~/components/UserError') so aliasing matches the rest
of the codebase.
| import handler from '@tanstack/react-start/server-entry' | ||
| import { tracer } from './utils/tracer' | ||
|
|
||
| import './otel' | ||
|
|
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.
🛠️ Refactor suggestion | 🟠 Major
Initialize OTEL before obtaining a tracer.
Import './otel' first to ensure the SDK/provider is registered before any tracer is requested.
-import handler from '@tanstack/react-start/server-entry'
-import { tracer } from './utils/tracer'
-
-import './otel'
+import './otel'
+import handler from '@tanstack/react-start/server-entry'
+import { tracer } from './utils/tracer'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import handler from '@tanstack/react-start/server-entry' | |
| import { tracer } from './utils/tracer' | |
| import './otel' | |
| import './otel' | |
| import handler from '@tanstack/react-start/server-entry' | |
| import { tracer } from './utils/tracer' |
🤖 Prompt for AI Agents
In examples/react/start-otel/src/server.ts around lines 1 to 5, the
OpenTelemetry SDK is initialized after the tracer is imported which can cause
the tracer to be obtained before the provider is registered; move the
side-effect import import './otel' to the top of the file so it runs before
importing or using the tracer (i.e., ensure './otel' is imported first, then
import { tracer } and the handler).
| export const fetchPost = createServerFn({ method: 'POST' }) | ||
| .inputValidator((d: string) => d) | ||
| .handler(async ({ data, context }) => { |
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.
🛠️ Refactor suggestion | 🟠 Major
Strengthen input validation for postId.
Coerce to positive int; reject invalid values.
export const fetchPost = createServerFn({ method: 'POST' })
- .inputValidator((d: string) => d)
+ .inputValidator((d: string) => {
+ const id = Number.parseInt(d, 10)
+ if (!Number.isFinite(id) || id <= 0) {
+ throw new Error('Invalid post id')
+ }
+ return id
+ })
.handler(async ({ data, context }) => {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const fetchPost = createServerFn({ method: 'POST' }) | |
| .inputValidator((d: string) => d) | |
| .handler(async ({ data, context }) => { | |
| export const fetchPost = createServerFn({ method: 'POST' }) | |
| .inputValidator((d: string) => { | |
| const id = Number.parseInt(d, 10) | |
| if (!Number.isFinite(id) || id <= 0) { | |
| throw new Error('Invalid post id') | |
| } | |
| return id | |
| }) | |
| .handler(async ({ data, context }) => { |
🤖 Prompt for AI Agents
In examples/react/start-otel/src/utils/posts.tsx around lines 12 to 14, the
inputValidator currently accepts any string; change it to coerce the incoming
postId to a positive integer and reject invalid values. Update the
inputValidator to parse the string (e.g., Number or parseInt), ensure it is an
integer > 0, and return the coerced number; if parsing fails or the value is not
a positive integer, throw or return a validation error so the handler never
receives invalid postId. Also adjust the handler signature/use to expect the
validated numeric postId rather than a raw string.
| export const fetchPost = createServerFn({ method: 'POST' }) | ||
| .inputValidator((d: string) => d) | ||
| .handler(async ({ data, context }) => { | ||
| console.log('Request context:', context) |
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.
🧩 Analysis chain
Do not log entire request context (may contain sensitive data).
This risks leaking headers/cookies/PII into logs.
- console.log('Request context:', context)
+ if (import.meta.env.DEV) {
+ console.log('Request context:', context)
+ }Run to find any other sensitive context/header logs:
🏁 Script executed:
#!/bin/bash
rg -n --glob "examples/react/start-otel/**" -C2 -e "Request context:" -e "getRequestHeaders\("Length of output: 954
Now I need to inspect the actual code to understand context better:
Based on my verification, I have confirmed that:
- The context object may include cookies used in authentication, and the request object can be used to get the url, headers, method, and even the body of the request.
- The script execution found two locations with sensitive logging:
examples/react/start-otel/src/utils/posts.tsx:15andexamples/react/start-otel/src/routes/api/users.ts:8.
The original review comment only addresses one location. However, the verification revealed a second identical issue in users.ts that should also be fixed.
Sensitive logging in request context and headers must be restricted to development only.
Both locations log complete request objects containing authentication headers and cookies:
examples/react/start-otel/src/utils/posts.tsxline 15:console.log('Request context:', context)examples/react/start-otel/src/routes/api/users.tsline 8:console.info('Request Headers:', getRequestHeaders())
Wrap both logs in a development check to prevent leaking sensitive credentials:
- console.log('Request context:', context)
+ if (import.meta.env.DEV) {
+ console.log('Request context:', context)
+ }- console.info('Request Headers:', getRequestHeaders())
+ if (import.meta.env.DEV) {
+ console.info('Request Headers:', getRequestHeaders())
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.log('Request context:', context) | |
| if (import.meta.env.DEV) { | |
| console.log('Request context:', context) | |
| } |
🤖 Prompt for AI Agents
In examples/react/start-otel/src/utils/posts.tsx around line 15 and
examples/react/start-otel/src/routes/api/users.ts around line 8, two console
logs output full request context/headers (which may include auth cookies and
headers); wrap both logging statements in an environment check so they only run
in development (e.g., if process.env.NODE_ENV === 'development' or
import.meta.env.DEV depending on runtime), and limit what is logged to
non-sensitive fields or a redacted summary instead of the full request object.
| const tags = [ | ||
| { title }, | ||
| { name: 'description', content: description }, | ||
| { name: 'keywords', content: keywords }, | ||
| { name: 'twitter:title', content: title }, | ||
| { name: 'twitter:description', content: description }, | ||
| { name: 'twitter:creator', content: '@tannerlinsley' }, | ||
| { name: 'twitter:site', content: '@tannerlinsley' }, | ||
| { name: 'og:type', content: 'website' }, | ||
| { name: 'og:title', content: title }, | ||
| { name: 'og:description', content: description }, | ||
| ...(image | ||
| ? [ | ||
| { name: 'twitter:image', content: image }, | ||
| { name: 'twitter:card', content: 'summary_large_image' }, | ||
| { name: 'og:image', content: image }, | ||
| ] | ||
| : []), | ||
| ] |
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.
Filter out meta tags with undefined content values.
The function includes meta tags with potentially undefined content values (description, keywords) in the returned array. Most meta tag implementations expect either the tag to be omitted or the content to be a string, not undefined.
Apply this diff to filter out tags with undefined content:
export const seo = ({
title,
description,
keywords,
image,
}: {
title: string
description?: string
image?: string
keywords?: string
}) => {
const tags = [
{ title },
- { name: 'description', content: description },
- { name: 'keywords', content: keywords },
+ ...(description ? [{ name: 'description', content: description }] : []),
+ ...(keywords ? [{ name: 'keywords', content: keywords }] : []),
{ name: 'twitter:title', content: title },
- { name: 'twitter:description', content: description },
+ ...(description ? [{ name: 'twitter:description', content: description }] : []),
{ name: 'twitter:creator', content: '@tannerlinsley' },
{ name: 'twitter:site', content: '@tannerlinsley' },
{ name: 'og:type', content: 'website' },
{ name: 'og:title', content: title },
- { name: 'og:description', content: description },
+ ...(description ? [{ name: 'og:description', content: description }] : []),
...(image
? [
{ name: 'twitter:image', content: image },
{ name: 'twitter:card', content: 'summary_large_image' },
{ name: 'og:image', content: image },
]
: []),
]
return tags
}🤖 Prompt for AI Agents
In examples/react/start-otel/src/utils/seo.ts around lines 12-30, the tags array
currently includes objects whose content may be undefined (e.g., description,
keywords); update the function to remove any tag objects that have an undefined
or null content before returning/using the array. Concretely, build the tags as
you do now, then filter them to only include entries where the content property
is a defined string (keep the title object as-is if it has no content field), so
meta tags with undefined content are omitted.
|
Update on this? |
Before taking a look at how Start can automatically instrument applications with OpenTelemetry I wanted to create a simple example with manual instrumentation.
This is a copy/paste of
react/start-basicwith:docker-compose.ymlthat runs Jaeger.otel.tsthat inits the SDK.Next Steps
Summary by CodeRabbit