@od-oneapp/observability 2026.1.1301
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +523 -0
- package/dist/client-next.d.mts +20 -0
- package/dist/client-next.d.mts.map +1 -0
- package/dist/client-next.mjs +64 -0
- package/dist/client-next.mjs.map +1 -0
- package/dist/client.d.mts +11 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +47 -0
- package/dist/client.mjs.map +1 -0
- package/dist/env.d.mts +15 -0
- package/dist/env.d.mts.map +1 -0
- package/dist/env.mjs +45 -0
- package/dist/env.mjs.map +1 -0
- package/dist/factory-DkY353r8.mjs +380 -0
- package/dist/factory-DkY353r8.mjs.map +1 -0
- package/dist/hooks-useObservability.d.mts +11 -0
- package/dist/hooks-useObservability.d.mts.map +1 -0
- package/dist/hooks-useObservability.mjs +174 -0
- package/dist/hooks-useObservability.mjs.map +1 -0
- package/dist/index-CpcdzWrF.d.mts +24 -0
- package/dist/index-CpcdzWrF.d.mts.map +1 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +97 -0
- package/dist/index.mjs.map +1 -0
- package/dist/manager-BxQqOPEg.d.mts +33 -0
- package/dist/manager-BxQqOPEg.d.mts.map +1 -0
- package/dist/plugin-Bfq-o3nr.d.mts +60 -0
- package/dist/plugin-Bfq-o3nr.d.mts.map +1 -0
- package/dist/plugin-Bt-ygG1m.d.mts +254 -0
- package/dist/plugin-Bt-ygG1m.d.mts.map +1 -0
- package/dist/plugin-CLFwRERa.mjs +593 -0
- package/dist/plugin-CLFwRERa.mjs.map +1 -0
- package/dist/plugin-CP895lBx.mjs +534 -0
- package/dist/plugin-CP895lBx.mjs.map +1 -0
- package/dist/plugin-CaQxviDs.d.mts +61 -0
- package/dist/plugin-CaQxviDs.d.mts.map +1 -0
- package/dist/plugin-lPdJirTY.mjs +234 -0
- package/dist/plugin-lPdJirTY.mjs.map +1 -0
- package/dist/plugins-betterstack-env.d.mts +29 -0
- package/dist/plugins-betterstack-env.d.mts.map +1 -0
- package/dist/plugins-betterstack-env.mjs +75 -0
- package/dist/plugins-betterstack-env.mjs.map +1 -0
- package/dist/plugins-betterstack.d.mts +4 -0
- package/dist/plugins-betterstack.mjs +4 -0
- package/dist/plugins-console.d.mts +37 -0
- package/dist/plugins-console.d.mts.map +1 -0
- package/dist/plugins-console.mjs +196 -0
- package/dist/plugins-console.mjs.map +1 -0
- package/dist/plugins-sentry-env.d.mts +37 -0
- package/dist/plugins-sentry-env.d.mts.map +1 -0
- package/dist/plugins-sentry-env.mjs +79 -0
- package/dist/plugins-sentry-env.mjs.map +1 -0
- package/dist/plugins-sentry-microfrontend-env.d.mts +49 -0
- package/dist/plugins-sentry-microfrontend-env.d.mts.map +1 -0
- package/dist/plugins-sentry-microfrontend-env.mjs +80 -0
- package/dist/plugins-sentry-microfrontend-env.mjs.map +1 -0
- package/dist/plugins-sentry-microfrontend.d.mts +2 -0
- package/dist/plugins-sentry-microfrontend.mjs +3 -0
- package/dist/plugins-sentry.d.mts +5 -0
- package/dist/plugins-sentry.mjs +6 -0
- package/dist/server-edge.d.mts +15 -0
- package/dist/server-edge.d.mts.map +1 -0
- package/dist/server-edge.mjs +53 -0
- package/dist/server-edge.mjs.map +1 -0
- package/dist/server-next.d.mts +17 -0
- package/dist/server-next.d.mts.map +1 -0
- package/dist/server-next.mjs +64 -0
- package/dist/server-next.mjs.map +1 -0
- package/dist/server.d.mts +11 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +48 -0
- package/dist/server.mjs.map +1 -0
- package/dist/utils-CuGrTcD6.d.mts +77 -0
- package/dist/utils-CuGrTcD6.d.mts.map +1 -0
- package/env.ts +67 -0
- package/package.json +147 -0
- package/src/client-next.ts +131 -0
- package/src/client.ts +70 -0
- package/src/core/index.ts +15 -0
- package/src/core/manager.ts +361 -0
- package/src/core/plugin.ts +61 -0
- package/src/core/types.ts +151 -0
- package/src/factory/builder.ts +132 -0
- package/src/factory/index.ts +67 -0
- package/src/factory/presets.ts +78 -0
- package/src/hooks/useObservability.ts +206 -0
- package/src/plugins/betterstack/env.ts +101 -0
- package/src/plugins/betterstack/index.ts +15 -0
- package/src/plugins/betterstack/plugin.ts +373 -0
- package/src/plugins/console/index.ts +323 -0
- package/src/plugins/sentry/__tests__/plugin-tracing.test.ts +511 -0
- package/src/plugins/sentry/env.ts +93 -0
- package/src/plugins/sentry/index.ts +28 -0
- package/src/plugins/sentry/plugin.ts +953 -0
- package/src/plugins/sentry/types.ts +252 -0
- package/src/plugins/sentry-microfrontend/env.ts +105 -0
- package/src/plugins/sentry-microfrontend/index.ts +12 -0
- package/src/plugins/sentry-microfrontend/multiplexed-transport.ts +221 -0
- package/src/plugins/sentry-microfrontend/plugin.ts +500 -0
- package/src/plugins/sentry-microfrontend/sentry-types.ts +140 -0
- package/src/plugins/sentry-microfrontend/types.ts +130 -0
- package/src/plugins/sentry-microfrontend/utils.ts +326 -0
- package/src/server-edge.ts +113 -0
- package/src/server-next.ts +114 -0
- package/src/server.ts +71 -0
- package/src/shared.ts +148 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Types for Sentry Micro Frontend Plugin
|
|
3
|
+
* Types for Sentry Micro Frontend Plugin
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { SentryPluginConfig } from '../sentry';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for a specific micro frontend backstageApp
|
|
10
|
+
*/
|
|
11
|
+
export interface BackstageAppConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Name of the backstageApp (e.g., 'cms', 'workflows', 'authmgmt')
|
|
14
|
+
*/
|
|
15
|
+
name: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Sentry DSN for this backstageApp (optional, uses default if not provided)
|
|
19
|
+
*/
|
|
20
|
+
dsn?: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Release version for this backstageApp
|
|
24
|
+
*/
|
|
25
|
+
release?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Additional tags to apply to events from this backstageApp
|
|
29
|
+
*/
|
|
30
|
+
tags?: Record<string, string>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Path patterns that identify this backstageApp
|
|
34
|
+
*/
|
|
35
|
+
pathPatterns?: (string | RegExp)[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Mode of operation for the micro frontend
|
|
40
|
+
*/
|
|
41
|
+
export type MicroFrontendMode = 'host' | 'child' | 'standalone';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Configuration for Sentry Micro Frontend Plugin
|
|
45
|
+
*/
|
|
46
|
+
export interface SentryMicroFrontendConfig extends Omit<SentryPluginConfig, 'transport'> {
|
|
47
|
+
/**
|
|
48
|
+
* Whether this is the host application that manages routing
|
|
49
|
+
*/
|
|
50
|
+
isHost?: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The backstageApp identifier for this micro frontend (e.g., 'cms', 'workflows')
|
|
54
|
+
*/
|
|
55
|
+
backstageApp?: string;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Configuration for all backstage (only used in host mode)
|
|
59
|
+
*/
|
|
60
|
+
backstageApps?: BackstageAppConfig[];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether to detect and use parent Sentry instance
|
|
64
|
+
* @default true
|
|
65
|
+
*/
|
|
66
|
+
detectParent?: boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Custom function to detect the current backstageApp
|
|
70
|
+
*/
|
|
71
|
+
backstageAppDetector?: () => string | undefined;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether to add backstageApp information to all events
|
|
75
|
+
* @default true
|
|
76
|
+
*/
|
|
77
|
+
addBackstageContext?: boolean;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Fallback DSN to use if backstageApp-specific DSN is not found
|
|
81
|
+
*/
|
|
82
|
+
fallbackDsn?: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Whether to use multiplexed transport in host mode
|
|
86
|
+
* @default true
|
|
87
|
+
*/
|
|
88
|
+
useMultiplexedTransport?: boolean;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Global tags to apply to all events
|
|
92
|
+
*/
|
|
93
|
+
globalTags?: Record<string, string>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether to prevent initialization if parent exists
|
|
97
|
+
* @default true
|
|
98
|
+
*/
|
|
99
|
+
preventDuplicateInit?: boolean;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Integration configuration to be handled during initialization
|
|
103
|
+
*/
|
|
104
|
+
integrationConfig?: {
|
|
105
|
+
nodeProfilingIntegration?: boolean;
|
|
106
|
+
captureConsoleIntegration?: boolean;
|
|
107
|
+
vercelAIIntegration?: boolean;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Sentry experimental features
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
_experiments?: {
|
|
115
|
+
enableLogs?: boolean;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Extended window interface for Sentry
|
|
121
|
+
*/
|
|
122
|
+
declare global {
|
|
123
|
+
interface Window {
|
|
124
|
+
Sentry?: any;
|
|
125
|
+
|
|
126
|
+
__SENTRY_MICRO_FRONTEND_HOST__?: boolean;
|
|
127
|
+
|
|
128
|
+
__SENTRY_MICRO_FRONTEND_APP__?: string;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utility functions for Sentry Micro Frontend Plugin
|
|
3
|
+
* Utility functions for Sentry Micro Frontend Plugin
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { logError } from '@repo/shared/logs';
|
|
7
|
+
|
|
8
|
+
import type { BackstageAppConfig } from './types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect the current micro frontend backstageApp based on URL path.
|
|
12
|
+
*
|
|
13
|
+
* Analyzes the current URL pathname to determine which micro frontend application
|
|
14
|
+
* is active. Supports custom path patterns and falls back to default patterns.
|
|
15
|
+
*
|
|
16
|
+
* @param customPatterns - Optional array of custom backstage app configurations with path patterns
|
|
17
|
+
* @returns Detected backstage app name, or undefined if not detected
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const app = detectCurrentBackstageApp([
|
|
22
|
+
* { name: 'admin', pathPatterns: ['/admin', '/manage'] },
|
|
23
|
+
* { name: 'dashboard', pathPatterns: [/^\/dashboard/] }
|
|
24
|
+
* ]);
|
|
25
|
+
* // Returns: 'admin' if pathname starts with '/admin' or '/manage'
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function detectCurrentBackstageApp(
|
|
29
|
+
customPatterns?: BackstageAppConfig[],
|
|
30
|
+
): string | undefined {
|
|
31
|
+
// Edge runtime compatible check
|
|
32
|
+
if (
|
|
33
|
+
typeof globalThis === 'undefined' ||
|
|
34
|
+
!(globalThis as { location?: { pathname?: string; href?: string } }).location
|
|
35
|
+
) {
|
|
36
|
+
// Server-side or edge: check Next.js basePath or environment variables
|
|
37
|
+
// Note: process.env is not available in edge runtime
|
|
38
|
+
if (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_BASE_PATH) {
|
|
39
|
+
return process.env.NEXT_PUBLIC_BASE_PATH.replace('/', '');
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const path = (globalThis as { location?: { pathname?: string } }).location?.pathname;
|
|
45
|
+
if (!path) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check custom patterns first
|
|
50
|
+
if (customPatterns) {
|
|
51
|
+
for (const config of customPatterns) {
|
|
52
|
+
if (config.pathPatterns) {
|
|
53
|
+
for (const pattern of config.pathPatterns) {
|
|
54
|
+
if (typeof pattern === 'string' && path.startsWith(pattern)) {
|
|
55
|
+
return config.name;
|
|
56
|
+
} else if (pattern instanceof RegExp && pattern.test(path)) {
|
|
57
|
+
return config.name;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Default patterns for common microfrontend zones
|
|
65
|
+
if (path.startsWith('/admin')) return 'admin';
|
|
66
|
+
if (path.startsWith('/dashboard')) return 'dashboard';
|
|
67
|
+
if (path.startsWith('/settings')) return 'settings';
|
|
68
|
+
|
|
69
|
+
// Check for backstageApp in global object (set by host)
|
|
70
|
+
|
|
71
|
+
if ((globalThis as any).__SENTRY_MICRO_FRONTEND_APP__) {
|
|
72
|
+
return (globalThis as any).__SENTRY_MICRO_FRONTEND_APP__;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if ((globalThis as any).__SENTRY_MICRO_FRONTEND_ZONE__) {
|
|
76
|
+
// Backwards compatibility with legacy zone naming
|
|
77
|
+
|
|
78
|
+
return (globalThis as any).__SENTRY_MICRO_FRONTEND_ZONE__;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return 'main';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if running in a host environment.
|
|
86
|
+
*
|
|
87
|
+
* Determines whether the current application is acting as a host for micro frontends
|
|
88
|
+
* or as a child micro frontend. Checks for explicit host markers and Sentry initialization state.
|
|
89
|
+
*
|
|
90
|
+
* @returns True if running as host, false if child or standalone
|
|
91
|
+
*/
|
|
92
|
+
export function isHostEnvironment(): boolean {
|
|
93
|
+
if (typeof globalThis === 'undefined') {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if explicitly marked as host
|
|
98
|
+
|
|
99
|
+
if ((globalThis as any).__SENTRY_MICRO_FRONTEND_HOST__) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check if Sentry is already initialized (likely means we're a child)
|
|
104
|
+
if (
|
|
105
|
+
(globalThis as any).Sentry &&
|
|
106
|
+
typeof (globalThis as any).Sentry.getCurrentHub === 'function'
|
|
107
|
+
) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Default to false (safer to assume child mode)
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create a Sentry scope with backstageApp-specific context.
|
|
117
|
+
*
|
|
118
|
+
* Creates a new Sentry scope configured with micro frontend tags and context.
|
|
119
|
+
* Includes backstageApp tag, microFrontend context, and any additional tags provided.
|
|
120
|
+
*
|
|
121
|
+
* @param backstageApp - Name of the backstage app
|
|
122
|
+
* @param additionalTags - Optional additional tags to set on the scope
|
|
123
|
+
* @returns Sentry scope instance, or null if Sentry is not available
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
export function createBackstageScope(
|
|
127
|
+
backstageApp: string,
|
|
128
|
+
additionalTags?: Record<string, string>,
|
|
129
|
+
): unknown {
|
|
130
|
+
if (typeof globalThis === 'undefined' || !(globalThis as any).Sentry) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const { Sentry } = globalThis as any;
|
|
135
|
+
|
|
136
|
+
// Check if Scope constructor is available
|
|
137
|
+
if (!Sentry.Scope || typeof Sentry.Scope !== 'function') {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const scope = new Sentry.Scope();
|
|
143
|
+
|
|
144
|
+
// Set backstageApp tag
|
|
145
|
+
scope.setTag('backstageApp', backstageApp);
|
|
146
|
+
scope.setTag('microFrontend', true);
|
|
147
|
+
|
|
148
|
+
// Set backstageApp context
|
|
149
|
+
scope.setContext('microFrontend', {
|
|
150
|
+
backstageApp,
|
|
151
|
+
|
|
152
|
+
isHost: (globalThis as any).__SENTRY_MICRO_FRONTEND_HOST__ ?? false,
|
|
153
|
+
url: (globalThis as { location?: { href?: string } }).location?.href ?? undefined,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Add additional tags if provided
|
|
157
|
+
if (additionalTags) {
|
|
158
|
+
Object.entries(additionalTags).forEach(([key, value]) => {
|
|
159
|
+
scope.setTag(key, value);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return scope;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
logError('[SentryMicroFrontendPlugin] Failed to create Backstage scope', { error });
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if Sentry is already initialized by a parent application.
|
|
172
|
+
*
|
|
173
|
+
* Verifies whether a parent Sentry instance exists in the global scope.
|
|
174
|
+
* Used to determine if the current micro frontend should use the parent's Sentry
|
|
175
|
+
* or initialize its own instance.
|
|
176
|
+
*
|
|
177
|
+
* @returns True if parent Sentry is available, false otherwise
|
|
178
|
+
*/
|
|
179
|
+
export function hasParentSentry(): boolean {
|
|
180
|
+
if (typeof globalThis === 'undefined') {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check for Sentry global
|
|
185
|
+
|
|
186
|
+
const { Sentry } = globalThis as any;
|
|
187
|
+
if (!Sentry) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Verify it's a real Sentry instance with expected methods
|
|
192
|
+
const requiredMethods = ['captureException', 'captureMessage', 'withScope'];
|
|
193
|
+
return requiredMethods.every(method => typeof Sentry[method] === 'function');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get the parent Sentry instance if available.
|
|
198
|
+
*
|
|
199
|
+
* Returns the global Sentry instance if it exists and has been initialized
|
|
200
|
+
* by a parent application. Returns null if no parent Sentry is available.
|
|
201
|
+
*
|
|
202
|
+
* @returns Parent Sentry SDK instance, or null if not available
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
export function getParentSentry(): any {
|
|
206
|
+
if (hasParentSentry()) {
|
|
207
|
+
return (globalThis as any).Sentry;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Mark the current environment as a host.
|
|
214
|
+
*
|
|
215
|
+
* Sets global flags indicating that this application is acting as a host
|
|
216
|
+
* for micro frontends. This affects how Sentry events are routed and tagged.
|
|
217
|
+
*
|
|
218
|
+
* @param backstageApp - Optional backstage app name to set
|
|
219
|
+
*/
|
|
220
|
+
export function markAsHost(backstageApp?: string): void {
|
|
221
|
+
if (typeof globalThis !== 'undefined') {
|
|
222
|
+
(globalThis as any).__SENTRY_MICRO_FRONTEND_HOST__ = true;
|
|
223
|
+
if (backstageApp) {
|
|
224
|
+
(globalThis as any).__SENTRY_MICRO_FRONTEND_APP__ = backstageApp;
|
|
225
|
+
|
|
226
|
+
(globalThis as any).__SENTRY_MICRO_FRONTEND_ZONE__ = backstageApp;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get backstageApp-specific configuration.
|
|
233
|
+
*
|
|
234
|
+
* Finds the configuration for a specific backstage app from the provided array.
|
|
235
|
+
*
|
|
236
|
+
* @param backstageApp - Name of the backstage app to find
|
|
237
|
+
* @param backstage - Array of backstage app configurations
|
|
238
|
+
* @returns Configuration for the specified app, or undefined if not found
|
|
239
|
+
*/
|
|
240
|
+
export function getBackstageAppConfig(
|
|
241
|
+
backstageApp: string,
|
|
242
|
+
backstage: BackstageAppConfig[],
|
|
243
|
+
): BackstageAppConfig | undefined {
|
|
244
|
+
return backstage.find(app => app.name === backstageApp);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Ensure only one Sentry initialization happens (thread-safe).
|
|
249
|
+
*
|
|
250
|
+
* Prevents multiple Sentry initializations using global state tracking.
|
|
251
|
+
* Uses timestamps and unique IDs to detect concurrent initialization attempts.
|
|
252
|
+
* Throws an error if Sentry is already initialized or if concurrent initialization is detected.
|
|
253
|
+
*
|
|
254
|
+
* @throws Error if Sentry is already initialized or concurrent initialization detected
|
|
255
|
+
*/
|
|
256
|
+
export function ensureSingleInit(): void {
|
|
257
|
+
// Use a more robust initialization check with timestamps
|
|
258
|
+
const initKey = '__SENTRY_INIT_STATE__';
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
|
|
261
|
+
if (typeof globalThis !== 'undefined') {
|
|
262
|
+
const globalState = (globalThis as any)[initKey];
|
|
263
|
+
|
|
264
|
+
// Check if already initialized or currently initializing
|
|
265
|
+
if (globalState) {
|
|
266
|
+
const { status, timestamp, id } = globalState;
|
|
267
|
+
|
|
268
|
+
// If initialized, throw error
|
|
269
|
+
if (status === 'initialized') {
|
|
270
|
+
throw new Error(
|
|
271
|
+
'Sentry has already been initialized! Use hasParentSentry() to check before initializing.',
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// If initializing and it's been less than 5 seconds, assume another process is initializing
|
|
276
|
+
if (status === 'initializing' && now - timestamp < 5000) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Sentry initialization already in progress (started ${now - timestamp}ms ago by ${id})`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Mark as initializing with timestamp and unique ID
|
|
284
|
+
const initId = `${now}-${Math.random().toString(36).substr(2, 9)}`;
|
|
285
|
+
|
|
286
|
+
(globalThis as any)[initKey] = {
|
|
287
|
+
status: 'initializing',
|
|
288
|
+
timestamp: now,
|
|
289
|
+
id: initId,
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// Small delay to allow for race condition detection
|
|
293
|
+
// In a real thread-safe implementation, this would use proper locking
|
|
294
|
+
|
|
295
|
+
const checkState = (globalThis as any)[initKey];
|
|
296
|
+
if (checkState.id !== initId) {
|
|
297
|
+
throw new Error('Concurrent Sentry initialization detected');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Mark as initialized
|
|
301
|
+
|
|
302
|
+
(globalThis as any)[initKey] = {
|
|
303
|
+
status: 'initialized',
|
|
304
|
+
timestamp: now,
|
|
305
|
+
id: initId,
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// Also set legacy flag for backward compatibility
|
|
309
|
+
|
|
310
|
+
(globalThis as any).__SENTRY_INITIALIZED__ = true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Reset initialization flag (mainly for testing).
|
|
316
|
+
*
|
|
317
|
+
* Clears the global Sentry initialization state. Useful for testing scenarios
|
|
318
|
+
* where you need to test initialization multiple times.
|
|
319
|
+
*/
|
|
320
|
+
export function resetInitFlag(): void {
|
|
321
|
+
if (typeof globalThis !== 'undefined') {
|
|
322
|
+
delete (globalThis as any).__SENTRY_INITIALIZED__;
|
|
323
|
+
|
|
324
|
+
delete (globalThis as any).__SENTRY_INIT_STATE__;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Next.js edge runtime observability export
|
|
3
|
+
* Next.js edge runtime observability export
|
|
4
|
+
* Limited to fetch-based implementations only
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { env } from '../env';
|
|
8
|
+
|
|
9
|
+
import { ObservabilityBuilder } from './factory/builder';
|
|
10
|
+
import { createBetterStackPlugin } from './plugins/betterstack';
|
|
11
|
+
import { env as betterStackEnv } from './plugins/betterstack/env';
|
|
12
|
+
import { createConsolePlugin } from './plugins/console';
|
|
13
|
+
import { shouldEnableConsole } from './shared';
|
|
14
|
+
|
|
15
|
+
import type { ObservabilityManager } from './core/manager';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create auto-configured observability for Next.js edge runtime
|
|
19
|
+
* Note: Sentry is not included here as it requires Node.js APIs
|
|
20
|
+
*/
|
|
21
|
+
export async function createEdgeObservability() {
|
|
22
|
+
const builder = ObservabilityBuilder.create().withAutoInitialize(false); // Manual init in edge runtime
|
|
23
|
+
|
|
24
|
+
// Console logging control using shared utility
|
|
25
|
+
const enableConsole = shouldEnableConsole(
|
|
26
|
+
env.NEXT_PUBLIC_NODE_ENV,
|
|
27
|
+
env.NEXT_PUBLIC_OBSERVABILITY_CONSOLE_ENABLED,
|
|
28
|
+
env.NEXT_PUBLIC_OBSERVABILITY_DEBUG,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Always add console plugin, control via enabled flag
|
|
32
|
+
builder.withPlugin(
|
|
33
|
+
createConsolePlugin({
|
|
34
|
+
prefix: '[Next.js Edge]',
|
|
35
|
+
enabled: enableConsole,
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Auto-activate Better Stack if source token is provided (uses fetch API)
|
|
40
|
+
const betterStackToken =
|
|
41
|
+
betterStackEnv.BETTER_STACK_SOURCE_TOKEN ??
|
|
42
|
+
betterStackEnv.BETTERSTACK_SOURCE_TOKEN ??
|
|
43
|
+
betterStackEnv.LOGTAIL_SOURCE_TOKEN;
|
|
44
|
+
|
|
45
|
+
if (betterStackToken) {
|
|
46
|
+
builder.withPlugin(
|
|
47
|
+
createBetterStackPlugin({
|
|
48
|
+
sourceToken: betterStackToken,
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Note: Sentry requires Node.js APIs and isn't available in edge runtime
|
|
54
|
+
// Better Stack is the recommended solution for edge runtime logging
|
|
55
|
+
|
|
56
|
+
return builder.build();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Lazy initialization for the observability instance using promise-based singleton
|
|
60
|
+
let initializationPromise: Promise<ObservabilityManager> | null = null;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get or create the observability instance
|
|
64
|
+
* Uses promise-based singleton pattern to prevent race conditions in concurrent scenarios
|
|
65
|
+
* @returns Promise resolving to the ObservabilityManager instance
|
|
66
|
+
*/
|
|
67
|
+
export async function getObservability(): Promise<ObservabilityManager> {
|
|
68
|
+
if (!initializationPromise) {
|
|
69
|
+
initializationPromise = (async () => {
|
|
70
|
+
try {
|
|
71
|
+
return await createEdgeObservability();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// Reset cached promise on failure so callers can retry
|
|
74
|
+
initializationPromise = null;
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
78
|
+
}
|
|
79
|
+
return initializationPromise;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Export types and utilities
|
|
83
|
+
export * from './core/types';
|
|
84
|
+
export type {
|
|
85
|
+
ObservabilityPlugin,
|
|
86
|
+
ObservabilityServerPlugin,
|
|
87
|
+
PluginFactory,
|
|
88
|
+
PluginLifecycle,
|
|
89
|
+
} from './core/plugin';
|
|
90
|
+
export { createObservability } from './factory';
|
|
91
|
+
export { ObservabilityBuilder } from './factory/builder';
|
|
92
|
+
|
|
93
|
+
// Re-export edge-compatible plugins only (explicit to avoid duplicate ObservabilityPlugin/ObservabilityServerPlugin)
|
|
94
|
+
export { BetterStackPlugin, createBetterStackPlugin, env, safeEnv } from './plugins/betterstack';
|
|
95
|
+
export type { BetterStackPluginConfig } from './plugins/betterstack';
|
|
96
|
+
export {
|
|
97
|
+
ConsolePlugin,
|
|
98
|
+
ConsoleServerPlugin,
|
|
99
|
+
createConsolePlugin,
|
|
100
|
+
createConsoleServerPlugin,
|
|
101
|
+
} from './plugins/console';
|
|
102
|
+
export type { ConsolePluginConfig } from './plugins/console';
|
|
103
|
+
|
|
104
|
+
// Legacy function for backward compatibility (no-op)
|
|
105
|
+
/**
|
|
106
|
+
* @deprecated Configuration now happens through the observability system
|
|
107
|
+
*/
|
|
108
|
+
export const configureLogger = () => {
|
|
109
|
+
// No-op: Configuration now happens through the observability system
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Re-export type
|
|
113
|
+
export type LogContext = Record<string, unknown>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Next.js server-specific observability export
|
|
3
|
+
* Next.js server-specific observability export
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { env } from '../env';
|
|
7
|
+
|
|
8
|
+
import { ObservabilityBuilder } from './factory/builder';
|
|
9
|
+
import { createBetterStackPlugin } from './plugins/betterstack';
|
|
10
|
+
import { env as betterStackEnv } from './plugins/betterstack/env';
|
|
11
|
+
import { createConsoleServerPlugin } from './plugins/console';
|
|
12
|
+
import { createSentryPlugin } from './plugins/sentry';
|
|
13
|
+
import { env as sentryEnv } from './plugins/sentry/env';
|
|
14
|
+
import { shouldEnableConsole } from './shared';
|
|
15
|
+
|
|
16
|
+
import type { ObservabilityManager } from './core/manager';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create auto-configured observability for Next.js server
|
|
20
|
+
*/
|
|
21
|
+
export async function createServerObservability() {
|
|
22
|
+
const builder = ObservabilityBuilder.create();
|
|
23
|
+
|
|
24
|
+
// Console logging control using shared utility
|
|
25
|
+
const enableConsole = shouldEnableConsole(
|
|
26
|
+
env.NEXT_PUBLIC_NODE_ENV,
|
|
27
|
+
env.NEXT_PUBLIC_OBSERVABILITY_CONSOLE_ENABLED,
|
|
28
|
+
env.NEXT_PUBLIC_OBSERVABILITY_DEBUG,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Always add console plugin, control via enabled flag
|
|
32
|
+
builder.withPlugin(
|
|
33
|
+
createConsoleServerPlugin({
|
|
34
|
+
prefix: '[Next.js Server]',
|
|
35
|
+
enabled: enableConsole,
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Auto-activate Sentry if DSN is provided
|
|
40
|
+
// Only use client-accessible DSN for auto-detection to avoid server/client boundary issues
|
|
41
|
+
const sentryDSN = sentryEnv.NEXT_PUBLIC_SENTRY_DSN;
|
|
42
|
+
if (sentryDSN) {
|
|
43
|
+
const sentry = createSentryPlugin({
|
|
44
|
+
dsn: sentryDSN,
|
|
45
|
+
});
|
|
46
|
+
await sentry.initialize();
|
|
47
|
+
builder.withPlugin(sentry);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Auto-activate Better Stack if source token is provided
|
|
51
|
+
const betterStackToken =
|
|
52
|
+
betterStackEnv.BETTER_STACK_SOURCE_TOKEN ??
|
|
53
|
+
betterStackEnv.BETTERSTACK_SOURCE_TOKEN ??
|
|
54
|
+
betterStackEnv.LOGTAIL_SOURCE_TOKEN;
|
|
55
|
+
|
|
56
|
+
if (betterStackToken) {
|
|
57
|
+
const betterstack = createBetterStackPlugin({
|
|
58
|
+
sourceToken: betterStackToken,
|
|
59
|
+
});
|
|
60
|
+
await betterstack.initialize();
|
|
61
|
+
builder.withPlugin(betterstack);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return builder.build();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Lazy initialization for the observability instance using promise-based singleton
|
|
68
|
+
let initializationPromise: Promise<ObservabilityManager> | null = null;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get or create the observability instance
|
|
72
|
+
* Uses promise-based singleton pattern to prevent race conditions in concurrent scenarios
|
|
73
|
+
* @returns Promise resolving to the ObservabilityManager instance
|
|
74
|
+
*/
|
|
75
|
+
export async function getObservability(): Promise<ObservabilityManager> {
|
|
76
|
+
if (!initializationPromise) {
|
|
77
|
+
initializationPromise = (async () => {
|
|
78
|
+
try {
|
|
79
|
+
return await createServerObservability();
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Reset cached promise on failure so callers can retry
|
|
82
|
+
initializationPromise = null;
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
}
|
|
87
|
+
return initializationPromise;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Export types and utilities
|
|
91
|
+
export * from './core/types';
|
|
92
|
+
export { createObservability } from './factory';
|
|
93
|
+
export { ObservabilityBuilder } from './factory/builder';
|
|
94
|
+
export { ObservabilityManager } from "./factory";
|
|
95
|
+
|
|
96
|
+
// Re-export plugins for direct access (excluding conflicting exports)
|
|
97
|
+
export { BetterStackPlugin } from './plugins/betterstack';
|
|
98
|
+
export { ConsolePlugin } from './plugins/console';
|
|
99
|
+
export { SentryPlugin } from './plugins/sentry';
|
|
100
|
+
export { SentryMicroFrontendPlugin } from './plugins/sentry-microfrontend';
|
|
101
|
+
|
|
102
|
+
// Re-export plugin-specific types with aliases to avoid conflicts
|
|
103
|
+
// Removed for Rollup compatibility
|
|
104
|
+
// Removed for Rollup compatibility
|
|
105
|
+
|
|
106
|
+
// Legacy function for backward compatibility (no-op)
|
|
107
|
+
/**
|
|
108
|
+
* @deprecated Configuration now happens through the observability system
|
|
109
|
+
*/
|
|
110
|
+
export const configureLogger = (): void => {
|
|
111
|
+
// No-op: Configuration now happens through the observability system
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Re-export type - removed for Rollup compatibility
|