Observability with Sentry
Sentry server/edge/browser, source maps, /monitoring tunnel
Observability with Sentry
The odys application integrates Sentry to provide comprehensive error tracking and performance monitoring across its various runtime environments: server, edge, and client (browser). This setup is crucial for identifying and diagnosing issues quickly, offering insights into application health, and ensuring a smooth user experience. This document explores the architecture and configuration of Sentry within odys, detailing how it captures errors, monitors performance, and manages source maps.
Overview
Sentry's integration in odys is designed to adapt to Next.js's hybrid rendering model, where code can execute in Node.js (server), Edge runtimes, or the client's browser. This requires a multi-faceted configuration to ensure all potential error sources are covered. The core of this setup involves:
- Runtime-specific initialization:
src/instrumentation.tsdynamically loads Sentry configurations based on the execution environment (nodejsoredge). - Client-side configuration:
src/instrumentation-client.tssets up Sentry for browser-side operations, including error capturing, performance tracing, and session replays. - Global error handling:
src/app/global-error.tsxensures that unhandled exceptions in React components are reported to Sentry. - Build-time and network configuration:
next.config.tswraps the Next.js configuration withwithSentryConfigto manage source map uploads, define atunnelRoutefor secure data transmission, and enable automatic monitoring for Vercel Cron Jobs.
This layered approach allows odys to maintain a consistent observability posture across its entire stack, from backend API routes to interactive frontend components.
Runtime-Specific Initialization
The src/instrumentation.ts file plays a pivotal role in initializing Sentry for server-side and edge environments. Next.js's instrumentation.ts is a special file that runs once per server or edge instance, making it an ideal place for global setup tasks like Sentry initialization.
The code dynamically imports configuration files based on the NEXT_RUNTIME environment variable:
- If
process.env.NEXT_RUNTIMEis"nodejs", it imports../sentry.server.config. This ensures that Sentry is configured appropriately for Node.js environments, which handle server-side rendering and API routes. - If
process.env.NEXT_RUNTIMEis"edge", it imports../sentry.edge.config. This caters to Next.js's Edge Runtime, used for middleware and certain API routes, which have different execution constraints than Node.js.
This conditional loading allows for tailored Sentry configurations, optimizing resource usage and ensuring compatibility with each runtime's specific APIs and limitations. Additionally, onRequestError is exported directly from Sentry.captureRequestError, providing a standardized way to capture errors that occur during request processing in these environments.
Client-Side Configuration
For errors and performance issues originating in the user's browser, src/instrumentation-client.ts configures Sentry. This file is loaded whenever a user accesses a page, ensuring client-side observability.
The Sentry.init call in this file uses the DSN https://4c49acd323505a3be3603c5c4c7245ef@o4511124807417856.ingest.us.sentry.io/4511124816658432 to direct events to the correct Sentry project. It includes Sentry.replayIntegration(), which enables session replays, allowing developers to visually reconstruct user interactions leading up to an error.
To manage noise, a beforeSend hook filters out specific, less actionable errors:
- Messages containing
"Object Not Found Matching"are dropped, likely to ignore common, expected client-side errors that do not indicate a critical application failure. - Messages containing
"Non-Error promise rejection captured"are also filtered, as these often lack sufficient context for debugging and can clutter error reports.
Performance monitoring is configured with tracesSampleRate: 1, meaning 100% of transactions are sampled for tracing. This high rate is suitable for detailed performance analysis, especially in development or for critical applications. Session replays are sampled at replaysSessionSampleRate: 0.1 (10% of sessions) and replaysOnErrorSampleRate: 1.0 (100% of sessions with errors), balancing data collection with performance impact. The enableLogs: true setting ensures that console logs are also sent to Sentry, providing additional context.
Notably, sendDefaultPii: true is enabled, which instructs Sentry to send Personally Identifiable Information (PII) by default. This can include user IDs, IP addresses, and other sensitive data, which is valuable for debugging user-specific issues but requires careful consideration regarding privacy regulations.
Finally, onRouterTransitionStart is exported as Sentry.captureRouterTransitionStart, allowing Sentry to track navigation changes within the Next.js application, providing insights into page load performance and user flow.
Global Error Handling
The src/app/global-error.tsx component is a critical part of odys's error handling strategy for the App Router. When an unhandled error occurs within a React component tree, Next.js renders this global error boundary.
The component uses a useEffect hook to call Sentry.captureException(error) whenever an error is passed to it. This ensures that any unhandled exceptions that bubble up to the top-level error boundary are reported to Sentry, providing visibility into critical application failures that might otherwise go unnoticed. The component then renders a generic NextError page with statusCode={0}, as the App Router does not expose specific HTTP status codes for errors in this context.
Build-Time Configuration and Tunneling
The next.config.ts file integrates Sentry at the build level using withSentryConfig from @sentry/nextjs. This wrapper extends the standard Next.js configuration with Sentry-specific options.
Key Sentry configurations within next.config.ts include:
org: "odys-hx"andproject: "javascript-nextjs": These identify the Sentry organization and project where events and source maps should be sent.silent: !process.env.CI: This setting controls the verbosity of Sentry's webpack plugin. Logs for uploading source maps are only printed when not in a Continuous Integration environment, reducing build output noise during local development.widenClientFileUpload: true: This option instructs Sentry to upload a broader set of source maps for client-side files. While it can increase build times, it significantly improves the quality of stack traces in Sentry, making debugging much more efficient by providing original, unminified code context.tunnelRoute: "/monitoring": This is a crucial security and reliability feature. Instead of sending Sentry events directly from the browser to Sentry's ingest servers, client-side events are routed through a same-origin Next.js API route,/monitoring. This circumvents ad-blockers that might otherwise block direct Sentry requests, ensuring that client-side errors are consistently reported. It also helps maintain Content Security Policy (CSP) compliance, as the/monitoringroute is covered by the'self'directive in theconnect-srcpolicy.webpack.automaticVercelMonitors: true: This enables automatic instrumentation of Vercel Cron Monitors. Forodys, this means that the two defined cron jobs,/api/cron/remindersand/api/cron/whatsapp-watchdog, are automatically monitored by Sentry, providing visibility into their execution status and any failures.webpack.treeshake.removeDebugLogging: true: This option automatically removes Sentry's internal debug logging statements during the build process, helping to reduce the final bundle size for production deployments.
The next.config.ts also defines a comprehensive Content Security Policy (CSP) via securityHeaders. The connect-src directive explicitly allows 'self' and various third-party domains, including https://*.supabase.co, https://api.stripe.com, and https://us.i.posthog.com. The tunnelRoute: "/monitoring" is explicitly mentioned in the CSP rationale as being covered by 'self', reinforcing its role in secure and reliable error reporting.
Design decisions
The Sentry integration in odys reflects several deliberate design choices aimed at balancing comprehensive observability with performance and maintainability:
- Multi-runtime configuration: The use of
src/instrumentation.tsto conditionally loadsentry.server.configandsentry.edge.configis a direct response to Next.js's architecture. This avoids loading unnecessary Sentry SDKs or configurations into environments where they are not applicable, reducing bundle size and startup overhead for each runtime. - Client-side noise reduction: The
beforeSendfilter insrc/instrumentation-client.tsto drop "Object Not Found Matching" and "Non-Error promise rejection captured" messages is a pragmatic choice. These errors often represent transient or non-critical issues that can flood Sentry with unhelpful data, making it harder to identify genuine problems. Filtering them improves the signal-to-noise ratio in Sentry. - Aggressive tracing and replay sampling: Setting
tracesSampleRate: 1for performance monitoring indicates a high priority on understanding application performance in detail. This is balanced by a lowerreplaysSessionSampleRate: 0.1for general sessions, to manage the storage and processing costs of video replays, whilereplaysOnErrorSampleRate: 1.0ensures that every session leading to an error is fully recorded, providing maximum context for debugging critical issues. tunnelRoutefor reliability: The/monitoringtunnelRouteis a critical decision to ensure consistent error reporting. Ad-blockers frequently block direct requests to known analytics and error reporting domains. By proxying these requests through a same-origin route,odyssignificantly increases the likelihood that client-side errors reach Sentry, even for users with strict privacy settings. This also simplifies CSP configuration by keeping Sentry'sconnect-srcwithin the application's origin.sendDefaultPii: true: This choice prioritizes debugging efficiency by providing maximum user context with each error. While beneficial for understanding specific user issues, it introduces a trade-off regarding data privacy and compliance, requiring careful consideration of data handling policies.- Automatic Vercel Cron Monitors: Enabling
webpack.automaticVercelMonitors: truesimplifies the monitoring of scheduled tasks. This reduces manual configuration effort and provides immediate visibility into the health and execution of critical background processes like/api/cron/remindersand/api/cron/whatsapp-watchdog.
Potential improvements
- Refine
beforeSendfiltering: The currentbeforeSendfilter insrc/instrumentation-client.tsuses broad string matching (message.includes(...)). This could potentially filter out legitimate errors if their messages happen to contain the filtered phrases. A more precise approach would be to check the error type or specific error codes, if available, to ensure only known, ignorable errors are dropped. - Dynamic Sampling Rates: The
tracesSampleRateandreplaysSessionSampleRateinsrc/instrumentation-client.tsare currently static. For production environments, it might be beneficial to dynamically adjust these rates based on factors like user load, specific user segments (e.g., premium users), or even A/B testing. This could involve reading environment variables or fetching configuration from a remote source to optimize data collection costs versus observability needs. - Review
sendDefaultPii: WithsendDefaultPii: trueinsrc/instrumentation-client.ts, the application is sending Personally Identifiable Information to Sentry. While useful for debugging, this has privacy implications. A concrete improvement would be to review what specific PII is being sent and, if possible, selectively redact or anonymize sensitive fields that are not strictly necessary for debugging, or implement a more granular PII control mechanism. - Stricter Content Security Policy: The
next.config.tscurrently uses'unsafe-inline'forscript-src. While acknowledged as a Next.js requirement, exploring options to move to a nonce-based CSP or a stricter hash-based policy for scripts would significantly enhance the application's security posture against cross-site scripting (XSS) attacks. This would require careful auditing of all inline scripts and potentially changes to how Next.js injects its bootstrap code.
References
src/instrumentation.tssrc/instrumentation-client.tssrc/app/global-error.tsxnext.config.ts