@@ -15,24 +15,72 @@ export type MonitoringConfig = {
1515
1616let initialized = false ;
1717
18- // IMPORTANT! This initialization function must be called first thing when a server starts. If it's called after server
19- // frameworks initialized instrumentation might not work properly.
2018/**
2119 * Initializes modules related to error monitoring, performance monitoring, and tracing.
22- * @param opts - Initialization options. See underlying implementations for more details.
20+ *
21+ * IMPORTANT: This function must be called as early as possible during server startup, before
22+ * any server framework (e.g. Hapi, Express) registers its own instrumentation. Calling it
23+ * after framework initialization may result in incomplete or broken tracing.
24+ *
25+ * Initialization is guarded so that calling this function more than once is a no-op (a
26+ * warning is emitted on subsequent calls).
27+ *
28+ * @param opts - Monitoring initialization options, including an optional logger and a config
29+ * object that may contain both Sentry and tracing settings.
2330 */
2431export function initMonitoring ( opts : MonitoringConfig ) {
25- const { log, config } = opts ;
32+ const { log : logger , config } = opts ;
33+ const log = logger || console ;
34+
2635 if ( initialized ) {
27- opts . log ? .warn ( 'monitoring' , 'Monitoring can only be initialized once' ) ;
36+ log . warn ( 'monitoring' , 'Monitoring can only be initialized once' ) ;
2837 return ;
2938 }
3039 initialized = true ;
3140
41+ /**
42+ * IMPORTANT!
43+ *
44+ * Sentry also uses OTEL under the hood. Which means:
45+ * - Mind the order of initialization. Otel should be first, if configured!
46+ * - Mind the config.tracing.sentry.enabled flag
47+ * - Mind the config.sentry.skipOpenTelemetrySetup flag
48+ *
49+ * If the order or flags aren't correct the following could happen:
50+ * - Traces disappear
51+ * - Sentry errors don't get recorded
52+ * - Sentry context bleeds between requests (ie breadcrumbs, stack traces, etc. seem off)
53+ */
54+
55+ let nodeTracingInitialized = false ;
3256 if ( config . tracing ) {
33- initTracing ( config . tracing , log || console ) ;
57+ // Important! Sentry also uses OTEL under the hood. Flip this flag so the two can co-exist!
58+ // If you start seeing funny stack traces, or cross pollinating bread crumbs, something went
59+ // sideways here.
60+ if ( config . sentry ?. dsn ) {
61+ config . tracing . sentry = { enabled : true } ;
62+ }
63+
64+ log . info ( 'monitoring' , {
65+ msg : `Initialize Tracing with: ${ JSON . stringify ( config . tracing ) } ` ,
66+ } ) ;
67+ nodeTracingInitialized = ! ! initTracing ( config . tracing , log ) ;
3468 }
69+
70+ // Important! Order matters here. If OTEL is configured, Sentry must be initialized after OTEL.
3571 if ( config && config . sentry ) {
36- initSentry ( config , log || console ) ;
72+ if ( nodeTracingInitialized ) {
73+ config . sentry . skipOpenTelemetrySetup = true ;
74+ }
75+
76+ log . info ( 'monitoring' , {
77+ msg : `Initializing Sentry: ${ JSON . stringify ( {
78+ env : config . sentry ?. env ,
79+ clientName : config . sentry . clientName ,
80+ serverName : config . sentry . serverName ,
81+ hasDsn : ! ! config . sentry ?. dsn ,
82+ } ) } `,
83+ } ) ;
84+ initSentry ( config , log ) ;
3785 }
3886}
0 commit comments