@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,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Sentry Micro Frontend Plugin
|
|
3
|
+
* Sentry Micro Frontend Plugin
|
|
4
|
+
*
|
|
5
|
+
* Extends the base Sentry plugin with micro frontend-specific functionality
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { SentryPlugin } from '@integrations/sentry/observability-plugin/plugin';
|
|
9
|
+
import { logDebug, logError, logWarn } from '@repo/shared/logs';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
createBackstageBeforeSend,
|
|
13
|
+
createMultiplexedTransport,
|
|
14
|
+
enhanceEventWithBackstageApp,
|
|
15
|
+
} from './multiplexed-transport';
|
|
16
|
+
import { mapLogLevelToSentrySeverity } from './sentry-types';
|
|
17
|
+
import {
|
|
18
|
+
createBackstageScope,
|
|
19
|
+
detectCurrentBackstageApp,
|
|
20
|
+
ensureSingleInit,
|
|
21
|
+
getParentSentry,
|
|
22
|
+
hasParentSentry,
|
|
23
|
+
markAsHost,
|
|
24
|
+
} from './utils';
|
|
25
|
+
|
|
26
|
+
import type { SentryScope, SentrySDK } from './sentry-types';
|
|
27
|
+
import type { MicroFrontendMode, SentryMicroFrontendConfig } from './types';
|
|
28
|
+
import type { LogLevel, ObservabilityContext } from '../../core/types';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sentry plugin optimized for micro frontend architectures
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Sentry plugin optimized for micro frontend architectures.
|
|
35
|
+
*
|
|
36
|
+
* Extends SentryPlugin with micro frontend-specific features:
|
|
37
|
+
* - Host/child/standalone mode detection
|
|
38
|
+
* - Multiplexed transport for routing events to different Sentry projects
|
|
39
|
+
* - Backstage app context management
|
|
40
|
+
* - Parent Sentry instance detection and reuse
|
|
41
|
+
*/
|
|
42
|
+
export class SentryMicroFrontendPlugin extends SentryPlugin {
|
|
43
|
+
private mode: MicroFrontendMode;
|
|
44
|
+
private backstageApp: string | undefined;
|
|
45
|
+
private parentSentry: SentrySDK | undefined;
|
|
46
|
+
private backstageScope: SentryScope | undefined;
|
|
47
|
+
private microFrontendConfig: SentryMicroFrontendConfig;
|
|
48
|
+
private eventProcessorCleanup?: (() => void) | undefined;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a new SentryMicroFrontendPlugin instance.
|
|
52
|
+
*
|
|
53
|
+
* @param config - Sentry micro frontend plugin configuration
|
|
54
|
+
*/
|
|
55
|
+
constructor(config: SentryMicroFrontendConfig = {}) {
|
|
56
|
+
// Determine operation mode
|
|
57
|
+
const mode = SentryMicroFrontendPlugin.determineMode(config);
|
|
58
|
+
|
|
59
|
+
// Prepare base config based on mode
|
|
60
|
+
const baseConfig = SentryMicroFrontendPlugin.prepareConfig(config, mode);
|
|
61
|
+
|
|
62
|
+
// Initialize parent class
|
|
63
|
+
super(baseConfig);
|
|
64
|
+
|
|
65
|
+
this.mode = mode;
|
|
66
|
+
this.microFrontendConfig = config;
|
|
67
|
+
this.backstageApp = config.backstageApp ?? detectCurrentBackstageApp(config.backstageApps);
|
|
68
|
+
|
|
69
|
+
// Handle parent Sentry if in child mode
|
|
70
|
+
if (this.mode === 'child') {
|
|
71
|
+
const parent = getParentSentry();
|
|
72
|
+
if (parent) {
|
|
73
|
+
this.parentSentry = parent as SentrySDK;
|
|
74
|
+
this.enabled = true; // Re-enable since we'll use parent
|
|
75
|
+
|
|
76
|
+
// Create backstageApp-specific scope
|
|
77
|
+
if (this.backstageApp && this.parentSentry.Scope) {
|
|
78
|
+
this.backstageScope = createBackstageScope(
|
|
79
|
+
this.backstageApp,
|
|
80
|
+
config.globalTags,
|
|
81
|
+
) as SentryScope;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Mark as host if applicable
|
|
87
|
+
if (this.mode === 'host') {
|
|
88
|
+
markAsHost(this.backstageApp);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Determine the operation mode based on configuration and environment.
|
|
94
|
+
*
|
|
95
|
+
* @param config - Plugin configuration
|
|
96
|
+
* @returns Operation mode ('host', 'child', or 'standalone')
|
|
97
|
+
*/
|
|
98
|
+
private static determineMode(config: SentryMicroFrontendConfig): MicroFrontendMode {
|
|
99
|
+
// Explicit host mode
|
|
100
|
+
if (config.isHost) {
|
|
101
|
+
return 'host';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check for parent Sentry
|
|
105
|
+
if (config.detectParent !== false && hasParentSentry()) {
|
|
106
|
+
return 'child';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Default to standalone
|
|
110
|
+
return 'standalone';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Prepare configuration based on operation mode.
|
|
115
|
+
*
|
|
116
|
+
* Adjusts configuration settings based on whether running as host, child, or standalone.
|
|
117
|
+
*
|
|
118
|
+
* @param config - Original plugin configuration
|
|
119
|
+
* @param mode - Determined operation mode
|
|
120
|
+
* @returns Prepared configuration object
|
|
121
|
+
*/
|
|
122
|
+
private static prepareConfig(
|
|
123
|
+
config: SentryMicroFrontendConfig,
|
|
124
|
+
mode: MicroFrontendMode,
|
|
125
|
+
): SentryMicroFrontendConfig {
|
|
126
|
+
const preparedConfig = { ...config };
|
|
127
|
+
|
|
128
|
+
if (mode === 'child') {
|
|
129
|
+
// Disable initialization in child mode
|
|
130
|
+
preparedConfig.enabled = false;
|
|
131
|
+
} else if (
|
|
132
|
+
mode === 'host' &&
|
|
133
|
+
config.backstageApps &&
|
|
134
|
+
config.useMultiplexedTransport !== false
|
|
135
|
+
) {
|
|
136
|
+
// Host mode with multiplexed transport is handled in initialize()
|
|
137
|
+
// Don't set transport here as it needs Sentry to be loaded first
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return preparedConfig;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Initialize the plugin
|
|
145
|
+
*/
|
|
146
|
+
async initialize(config?: SentryMicroFrontendConfig): Promise<void> {
|
|
147
|
+
const mergedConfig = { ...this.microFrontendConfig, ...config };
|
|
148
|
+
|
|
149
|
+
if (this.mode === 'child') {
|
|
150
|
+
// In child mode, we don't initialize Sentry but set up our client reference
|
|
151
|
+
if (this.parentSentry) {
|
|
152
|
+
this.client = this.parentSentry;
|
|
153
|
+
this.initialized = true;
|
|
154
|
+
|
|
155
|
+
// Configure parent Sentry to include backstageApp information
|
|
156
|
+
if (this.backstageApp && mergedConfig.addBackstageContext !== false) {
|
|
157
|
+
this.configureParentSentry();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// For host and standalone modes
|
|
164
|
+
if (mergedConfig.preventDuplicateInit !== false) {
|
|
165
|
+
try {
|
|
166
|
+
ensureSingleInit();
|
|
167
|
+
} catch (error) {
|
|
168
|
+
logError('Sentry initialization prevented', { error });
|
|
169
|
+
this.enabled = false;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Initialize parent class
|
|
175
|
+
await super.initialize(mergedConfig);
|
|
176
|
+
|
|
177
|
+
// Additional setup for host mode
|
|
178
|
+
if (this.mode === 'host' && this.client && mergedConfig.backstageApps) {
|
|
179
|
+
this.setupHostMode(mergedConfig);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Configure parent Sentry instance for child mode
|
|
185
|
+
*/
|
|
186
|
+
private configureParentSentry(): void {
|
|
187
|
+
if (!this.parentSentry || !this.backstageApp) {
|
|
188
|
+
logWarn(
|
|
189
|
+
'[SentryMicroFrontendPlugin] Cannot configure parent Sentry: missing parentSentry or backstageApp',
|
|
190
|
+
);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// Create event processor
|
|
196
|
+
|
|
197
|
+
const processor = (event: any) => {
|
|
198
|
+
try {
|
|
199
|
+
if (this.shouldProcessEvent(event)) {
|
|
200
|
+
enhanceEventWithBackstageApp(event, this.backstageApp ?? '', {
|
|
201
|
+
mode: this.mode,
|
|
202
|
+
plugin: 'SentryMicroFrontendPlugin',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
} catch (error) {
|
|
206
|
+
logError('[SentryMicroFrontendPlugin] Error processing event', { error, event });
|
|
207
|
+
}
|
|
208
|
+
return event;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Add global event processor to include backstageApp information
|
|
212
|
+
if (typeof this.parentSentry.addEventProcessor === 'function') {
|
|
213
|
+
this.parentSentry.addEventProcessor(processor);
|
|
214
|
+
|
|
215
|
+
// Store cleanup function
|
|
216
|
+
this.eventProcessorCleanup = () => {
|
|
217
|
+
// Note: Sentry SDK doesn't provide a way to remove event processors
|
|
218
|
+
// We'll mark events as processed to avoid double processing
|
|
219
|
+
};
|
|
220
|
+
} else {
|
|
221
|
+
logWarn('[SentryMicroFrontendPlugin] Parent Sentry does not support addEventProcessor');
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
logError('[SentryMicroFrontendPlugin] Failed to configure parent Sentry', { error });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Set up host mode with multiplexed transport
|
|
230
|
+
*/
|
|
231
|
+
private setupHostMode(config: SentryMicroFrontendConfig): void {
|
|
232
|
+
if (!config.backstageApps || !this.client) {
|
|
233
|
+
logWarn(
|
|
234
|
+
'[SentryMicroFrontendPlugin] Cannot setup host mode: missing backstageApps or client',
|
|
235
|
+
);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
// Create multiplexed transport
|
|
241
|
+
const transport = createMultiplexedTransport(
|
|
242
|
+
config.backstageApps,
|
|
243
|
+
config.fallbackDsn ?? config.dsn,
|
|
244
|
+
this.client,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (transport && typeof (this.client as any).init === 'function') {
|
|
248
|
+
// Re-initialize with multiplexed transport
|
|
249
|
+
const currentOptions =
|
|
250
|
+
typeof (this.client as any).getOptions === 'function'
|
|
251
|
+
? (this.client as any).getOptions()
|
|
252
|
+
: {};
|
|
253
|
+
|
|
254
|
+
(this.client as any).init({
|
|
255
|
+
...currentOptions,
|
|
256
|
+
transport,
|
|
257
|
+
beforeSend: createBackstageBeforeSend(this.backstageApp ?? 'main', config.beforeSend),
|
|
258
|
+
});
|
|
259
|
+
} else {
|
|
260
|
+
logWarn(
|
|
261
|
+
'[SentryMicroFrontendPlugin] Client does not support init method or transport creation failed',
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
logError('[SentryMicroFrontendPlugin] Failed to setup host mode', { error });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Check if we should process this event
|
|
271
|
+
*/
|
|
272
|
+
|
|
273
|
+
private shouldProcessEvent(event: any): boolean {
|
|
274
|
+
// Don't double-process events
|
|
275
|
+
if (event.tags?.microFrontendProcessed) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Check if event is from our backstageApp
|
|
280
|
+
if (
|
|
281
|
+
this.backstageApp &&
|
|
282
|
+
event.tags?.backstageApp &&
|
|
283
|
+
event.tags.backstageApp !== this.backstageApp
|
|
284
|
+
) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Capture an exception with backstageApp context
|
|
293
|
+
*/
|
|
294
|
+
captureException(error: Error | unknown, context?: ObservabilityContext): void {
|
|
295
|
+
if (!this.enabled) return;
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
if (this.mode === 'child' && this.parentSentry && this.backstageScope) {
|
|
299
|
+
// Use parent Sentry with backstageApp scope
|
|
300
|
+
if (typeof this.parentSentry.withScope === 'function') {
|
|
301
|
+
this.parentSentry.withScope((scope: any) => {
|
|
302
|
+
try {
|
|
303
|
+
// Copy backstageApp scope properties
|
|
304
|
+
if (this.backstageScope && typeof scope.setTag === 'function') {
|
|
305
|
+
scope.setTag('backstageApp', this.backstageApp ?? 'unknown');
|
|
306
|
+
scope.setTag('microFrontend', true);
|
|
307
|
+
scope.setContext('microFrontend', {
|
|
308
|
+
backstageApp: this.backstageApp,
|
|
309
|
+
mode: this.mode,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Add additional context
|
|
314
|
+
if (context && typeof scope.setContext === 'function') {
|
|
315
|
+
scope.setContext('additional', context);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (this.parentSentry && typeof this.parentSentry.captureException === 'function') {
|
|
319
|
+
this.parentSentry.captureException(error);
|
|
320
|
+
}
|
|
321
|
+
} catch (scopeError) {
|
|
322
|
+
logError('[SentryMicroFrontendPlugin] Error in scope callback', {
|
|
323
|
+
error: scopeError,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
} else {
|
|
328
|
+
logWarn('[SentryMicroFrontendPlugin] Parent Sentry does not support withScope');
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
// Use standard capture with backstageApp enhancement
|
|
332
|
+
const enhancedContext = {
|
|
333
|
+
...context,
|
|
334
|
+
microFrontend: {
|
|
335
|
+
backstageApp: this.backstageApp,
|
|
336
|
+
mode: this.mode,
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
super.captureException(error, enhancedContext);
|
|
340
|
+
}
|
|
341
|
+
} catch (captureError) {
|
|
342
|
+
logError('[SentryMicroFrontendPlugin] Failed to capture exception', { error: captureError });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Capture a message with backstageApp context
|
|
348
|
+
*/
|
|
349
|
+
captureMessage(message: string, level: LogLevel = 'info', context?: ObservabilityContext): void {
|
|
350
|
+
if (!this.enabled) return;
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
const sentryLevel = mapLogLevelToSentrySeverity(level);
|
|
354
|
+
|
|
355
|
+
if (this.mode === 'child' && this.parentSentry && this.backstageScope) {
|
|
356
|
+
// Use parent Sentry with backstageApp scope
|
|
357
|
+
if (typeof this.parentSentry.withScope === 'function') {
|
|
358
|
+
this.parentSentry.withScope((scope: SentryScope) => {
|
|
359
|
+
try {
|
|
360
|
+
// Copy backstageApp scope properties
|
|
361
|
+
if (this.backstageScope && typeof scope.setTag === 'function') {
|
|
362
|
+
scope.setTag('backstageApp', this.backstageApp ?? 'unknown');
|
|
363
|
+
scope.setTag('microFrontend', true);
|
|
364
|
+
if (typeof scope.setContext === 'function') {
|
|
365
|
+
scope.setContext('microFrontend', {
|
|
366
|
+
backstageApp: this.backstageApp,
|
|
367
|
+
mode: this.mode,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Add additional context
|
|
373
|
+
if (context && typeof scope.setContext === 'function') {
|
|
374
|
+
scope.setContext('additional', context);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (this.parentSentry && typeof this.parentSentry.captureMessage === 'function') {
|
|
378
|
+
this.parentSentry.captureMessage(message, sentryLevel);
|
|
379
|
+
}
|
|
380
|
+
} catch (scopeError) {
|
|
381
|
+
logError('[SentryMicroFrontendPlugin] Error in scope callback', {
|
|
382
|
+
error: scopeError,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
} else {
|
|
387
|
+
logWarn('[SentryMicroFrontendPlugin] Parent Sentry does not support withScope');
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
// Use standard capture
|
|
391
|
+
super.captureMessage(message, level, context);
|
|
392
|
+
}
|
|
393
|
+
} catch (captureError) {
|
|
394
|
+
logError('[SentryMicroFrontendPlugin] Failed to capture message', { error: captureError });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Get the current operation mode
|
|
400
|
+
*/
|
|
401
|
+
getMode(): MicroFrontendMode {
|
|
402
|
+
return this.mode;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Get the current Backstage app identifier
|
|
407
|
+
*/
|
|
408
|
+
getBackstageApp(): string | undefined {
|
|
409
|
+
return this.backstageApp;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* @deprecated Use getBackstageApp instead.
|
|
414
|
+
*/
|
|
415
|
+
getZone(): string | undefined {
|
|
416
|
+
return this.getBackstageApp();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Get debug information about the plugin state
|
|
421
|
+
*/
|
|
422
|
+
|
|
423
|
+
getDebugInfo(): Record<string, any> {
|
|
424
|
+
return {
|
|
425
|
+
mode: this.mode,
|
|
426
|
+
backstageApp: this.backstageApp,
|
|
427
|
+
enabled: this.enabled,
|
|
428
|
+
initialized: this.initialized,
|
|
429
|
+
hasParentSentry: this.mode === 'child' && Boolean(this.parentSentry),
|
|
430
|
+
clientType: this.client ? this.client.constructor.name : 'none',
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Clean up the plugin and release resources
|
|
436
|
+
*/
|
|
437
|
+
async cleanup(): Promise<void> {
|
|
438
|
+
try {
|
|
439
|
+
// Clean up event processor if it exists
|
|
440
|
+
if (this.eventProcessorCleanup) {
|
|
441
|
+
this.eventProcessorCleanup();
|
|
442
|
+
this.eventProcessorCleanup = undefined;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Clear backstageApp scope
|
|
446
|
+
|
|
447
|
+
if (this.backstageScope && typeof (this.backstageScope as any).clear === 'function') {
|
|
448
|
+
(this.backstageScope as any).clear();
|
|
449
|
+
}
|
|
450
|
+
this.backstageScope = undefined;
|
|
451
|
+
|
|
452
|
+
// Clear parent Sentry reference
|
|
453
|
+
this.parentSentry = undefined;
|
|
454
|
+
|
|
455
|
+
// Call parent cleanup
|
|
456
|
+
await super.cleanup();
|
|
457
|
+
|
|
458
|
+
// Mark as not initialized
|
|
459
|
+
this.initialized = false;
|
|
460
|
+
this.enabled = false;
|
|
461
|
+
|
|
462
|
+
logDebug('[SentryMicroFrontendPlugin] Cleanup completed');
|
|
463
|
+
} catch (error) {
|
|
464
|
+
logError('[SentryMicroFrontendPlugin] Error during cleanup', { error });
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Destroy the plugin (alias for cleanup)
|
|
470
|
+
*/
|
|
471
|
+
async destroy(): Promise<void> {
|
|
472
|
+
await this.cleanup();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Factory function to create a Sentry Micro Frontend plugin.
|
|
478
|
+
*
|
|
479
|
+
* Creates a configured SentryMicroFrontendPlugin instance with micro frontend-specific
|
|
480
|
+
* features like multiplexed transport and backstage app routing.
|
|
481
|
+
*
|
|
482
|
+
* @param config - Optional Sentry micro frontend plugin configuration
|
|
483
|
+
* @returns SentryMicroFrontendPlugin instance
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* ```typescript
|
|
487
|
+
* const sentry = createSentryMicroFrontendPlugin({
|
|
488
|
+
* dsn: 'https://...',
|
|
489
|
+
* backstage: [
|
|
490
|
+
* { name: 'admin', dsn: 'https://admin-dsn...' },
|
|
491
|
+
* { name: 'dashboard', dsn: 'https://dashboard-dsn...' },
|
|
492
|
+
* ],
|
|
493
|
+
* });
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
export const createSentryMicroFrontendPlugin = (
|
|
497
|
+
config?: SentryMicroFrontendConfig,
|
|
498
|
+
): SentryMicroFrontendPlugin => {
|
|
499
|
+
return new SentryMicroFrontendPlugin(config);
|
|
500
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Type definitions for Sentry SDK
|
|
3
|
+
* Type definitions for Sentry SDK
|
|
4
|
+
* These are minimal types to avoid using 'any' throughout the plugin
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { LogLevel } from '../../core/types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sentry Scope interface
|
|
11
|
+
*/
|
|
12
|
+
export interface SentryScope {
|
|
13
|
+
setTag(key: string, value: string | number | boolean | null): void;
|
|
14
|
+
setContext(key: string, context: Record<string, any> | null): void;
|
|
15
|
+
setUser(user: Record<string, any> | null): void;
|
|
16
|
+
setLevel(level: SentrySeverityLevel): void;
|
|
17
|
+
setFingerprint(fingerprint: string[]): void;
|
|
18
|
+
clear(): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sentry Hub interface
|
|
23
|
+
*/
|
|
24
|
+
export interface SentryHub {
|
|
25
|
+
getScope(): SentryScope;
|
|
26
|
+
pushScope(): SentryScope;
|
|
27
|
+
popScope(): boolean;
|
|
28
|
+
withScope(callback: (scope: SentryScope) => void): void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Sentry Client interface
|
|
33
|
+
*/
|
|
34
|
+
export interface SentryClient {
|
|
35
|
+
captureException(exception: any, hint?: any): string;
|
|
36
|
+
captureMessage(message: string, level?: SentrySeverityLevel): string;
|
|
37
|
+
captureEvent(event: any): string;
|
|
38
|
+
flush(timeout?: number): Promise<boolean>;
|
|
39
|
+
close(timeout?: number): Promise<boolean>;
|
|
40
|
+
getOptions(): SentryOptions;
|
|
41
|
+
getCurrentHub?(): SentryHub;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sentry SDK interface
|
|
46
|
+
*/
|
|
47
|
+
export interface SentrySDK {
|
|
48
|
+
init(options: SentryOptions): void;
|
|
49
|
+
captureException(exception: any, captureContext?: any): string;
|
|
50
|
+
captureMessage(message: string, captureContext?: any): string;
|
|
51
|
+
captureEvent(event: any, hint?: any): string;
|
|
52
|
+
withScope(callback: (scope: SentryScope) => void): void;
|
|
53
|
+
addBreadcrumb(breadcrumb: any): void;
|
|
54
|
+
setUser(user: any): void;
|
|
55
|
+
setTag(key: string, value: string): void;
|
|
56
|
+
setContext(key: string, context: any): void;
|
|
57
|
+
setExtra(key: string, extra: any): void;
|
|
58
|
+
getCurrentHub(): SentryHub;
|
|
59
|
+
getClient<T extends SentryClient>(): T | undefined;
|
|
60
|
+
flush(timeout?: number): Promise<boolean>;
|
|
61
|
+
close(timeout?: number): Promise<boolean>;
|
|
62
|
+
lastEventId(): string | undefined;
|
|
63
|
+
|
|
64
|
+
// Integration methods
|
|
65
|
+
addEventProcessor(processor: (event: any) => any | null): void;
|
|
66
|
+
|
|
67
|
+
// Browser specific
|
|
68
|
+
Scope?: new () => SentryScope;
|
|
69
|
+
|
|
70
|
+
// Transport creation
|
|
71
|
+
makeMultiplexedTransport?(
|
|
72
|
+
createTransport: (options: any) => any,
|
|
73
|
+
matcher: (options: any) => any[],
|
|
74
|
+
): any;
|
|
75
|
+
makeFetchTransport?(options: any): any;
|
|
76
|
+
|
|
77
|
+
// Integrations
|
|
78
|
+
browserTracingIntegration?(): any;
|
|
79
|
+
browserProfilingIntegration?(): any;
|
|
80
|
+
replayIntegration?(options?: any): any;
|
|
81
|
+
captureConsoleIntegration?(options?: any): any;
|
|
82
|
+
feedbackIntegration?(options?: any): any;
|
|
83
|
+
vercelAIIntegration?(): any;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Sentry Options interface
|
|
88
|
+
*/
|
|
89
|
+
export interface SentryOptions {
|
|
90
|
+
dsn?: string;
|
|
91
|
+
release?: string;
|
|
92
|
+
environment?: string;
|
|
93
|
+
debug?: boolean;
|
|
94
|
+
sampleRate?: number;
|
|
95
|
+
tracesSampleRate?: number;
|
|
96
|
+
profilesSampleRate?: number;
|
|
97
|
+
replaysSessionSampleRate?: number;
|
|
98
|
+
replaysOnErrorSampleRate?: number;
|
|
99
|
+
transport?: any;
|
|
100
|
+
integrations?: any[];
|
|
101
|
+
beforeSend?: (event: any, hint: any) => any | null;
|
|
102
|
+
beforeSendTransaction?: (event: any, hint: any) => any | null;
|
|
103
|
+
beforeBreadcrumb?: (breadcrumb: any, hint?: any) => any | null;
|
|
104
|
+
tracePropagationTargets?: (string | RegExp)[];
|
|
105
|
+
initialScope?: any;
|
|
106
|
+
maxBreadcrumbs?: number;
|
|
107
|
+
attachStacktrace?: boolean;
|
|
108
|
+
autoSessionTracking?: boolean;
|
|
109
|
+
sendDefaultPii?: boolean;
|
|
110
|
+
|
|
111
|
+
_experiments?: Record<string, any>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Sentry Severity Level
|
|
116
|
+
*/
|
|
117
|
+
export type SentrySeverityLevel = 'fatal' | 'error' | 'warning' | 'log' | 'info' | 'debug';
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Map LogLevel to Sentry Severity.
|
|
121
|
+
*
|
|
122
|
+
* Converts the observability package's LogLevel to Sentry's SeverityLevel format.
|
|
123
|
+
*
|
|
124
|
+
* @param level - Log level from observability package
|
|
125
|
+
* @returns Corresponding Sentry severity level
|
|
126
|
+
*/
|
|
127
|
+
export function mapLogLevelToSentrySeverity(level: LogLevel): SentrySeverityLevel {
|
|
128
|
+
switch (level) {
|
|
129
|
+
case 'error':
|
|
130
|
+
return 'error';
|
|
131
|
+
case 'warning':
|
|
132
|
+
return 'warning';
|
|
133
|
+
case 'info':
|
|
134
|
+
return 'info';
|
|
135
|
+
case 'debug':
|
|
136
|
+
return 'debug';
|
|
137
|
+
default:
|
|
138
|
+
return 'info';
|
|
139
|
+
}
|
|
140
|
+
}
|