@module-federation/observability-plugin 0.0.0-alpha.0 → 0.0.0-feat-federationdiagnosticerror-20260511123257
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/AI_TROUBLESHOOTING.md +39 -5
- package/LICENSE +21 -0
- package/README.md +123 -33
- package/dist/AI_TROUBLESHOOTING.md +39 -5
- package/dist/CHANGELOG.md +12 -0
- package/dist/README.md +123 -33
- package/dist/esm/index.js +248 -28
- package/dist/esm/node.js +8 -5
- package/dist/index.d.ts +31 -3
- package/dist/index.js +251 -29
- package/dist/node.d.ts +4 -3
- package/dist/node.js +9 -4
- package/package.json +4 -4
package/AI_TROUBLESHOOTING.md
CHANGED
|
@@ -46,12 +46,24 @@ read: window.__FEDERATION__.__OBSERVABILITY__["runtime_host"].getReport("mf-..."
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Run the `read:` command exactly as printed. If the browser global reader is not
|
|
49
|
-
enabled,
|
|
49
|
+
enabled, ask for the application-owned `onReport` output or the uploaded
|
|
50
|
+
observability record.
|
|
51
|
+
|
|
52
|
+
If the user wants to inspect loading chains or says the page is stuck loading
|
|
53
|
+
without an error, do not wait for a failure hint. Read recent reports directly:
|
|
50
54
|
|
|
51
55
|
```ts
|
|
52
|
-
|
|
56
|
+
window.__FEDERATION__.__OBSERVABILITY__['runtime_host'].getReports({ limit: 10 });
|
|
53
57
|
```
|
|
54
58
|
|
|
59
|
+
Then look for `status: "pending"` or `summary.outcome: "pending"`. Use
|
|
60
|
+
`startedAt`, `updatedAt`, `duration`, `summary.phases`, and
|
|
61
|
+
`diagnosis.pendingPhases` to explain where the trace is currently stuck. When
|
|
62
|
+
the browser reader is enabled in development mode, the console prints
|
|
63
|
+
`Observability trace started` for `loadRemote` and `loadShare` by default; use
|
|
64
|
+
that `traceId` to read the exact report. In production browser mode, this start
|
|
65
|
+
log is disabled unless the app explicitly sets `trace.printStart: true`.
|
|
66
|
+
|
|
55
67
|
For the latest report:
|
|
56
68
|
|
|
57
69
|
```ts
|
|
@@ -90,8 +102,7 @@ errorCode: RUNTIME-...
|
|
|
90
102
|
|
|
91
103
|
Do not assume the full browser report is publicly readable when the `read:` line
|
|
92
104
|
is absent. Ask the application owner to export it with `exportReport(traceId)`,
|
|
93
|
-
|
|
94
|
-
application's own uploaded observability record.
|
|
105
|
+
or check the application's own uploaded observability record.
|
|
95
106
|
|
|
96
107
|
If the goal is ongoing observability instead of one-off debugging, ask whether
|
|
97
108
|
the application has enabled `onReport` or `onEvent`. In production, those
|
|
@@ -170,12 +181,27 @@ not by an AI model.
|
|
|
170
181
|
|
|
171
182
|
- `summary.outcome`: final outcome, such as `runtime-loaded`,
|
|
172
183
|
`component-loaded`, or `failed`.
|
|
184
|
+
- `summary.runtimeLoaded`: the remote module loaded successfully in the runtime.
|
|
185
|
+
- `summary.loadCompleted`: the `loadRemote` flow ended; this is not by itself a
|
|
186
|
+
success signal.
|
|
187
|
+
- `summary.componentLoaded`: a component-level success signal was observed. It
|
|
188
|
+
can come from business `markComponentLoaded` or from the opt-in React lifecycle
|
|
189
|
+
observer reporting `component:react-mounted`.
|
|
173
190
|
- `summary.phases`: per-phase status, duration, cache, retry, and recovery
|
|
174
191
|
markers.
|
|
175
192
|
- `summary.flags`: cross-phase cache, retry, fallback, and recovery markers.
|
|
176
|
-
- `summary.shared`: last observed shared provider/version result.
|
|
193
|
+
- `summary.shared`: last observed shared provider/version result. If multiple
|
|
194
|
+
shared dependencies are involved, inspect every `phase: "shared"` event.
|
|
177
195
|
- `summary.error`: compact error summary.
|
|
178
196
|
|
|
197
|
+
If React lifecycle observation is enabled, reports may include
|
|
198
|
+
`component:react-render-started`, `component:react-mounted`, or
|
|
199
|
+
`component:react-render-timeout`. Treat `component:react-mounted` as React mount
|
|
200
|
+
success only. It does not prove that business data, charts, or SDK
|
|
201
|
+
initialization completed. The wrapper also injects an `onMFRemoteLoaded` prop
|
|
202
|
+
into the remote React component; if the producer calls it, the report includes
|
|
203
|
+
`component:business-loaded`, which is the producer's explicit ready signal.
|
|
204
|
+
|
|
179
205
|
### Build files
|
|
180
206
|
|
|
181
207
|
Runtime reports do not embed build evidence. If you need build-side facts, read
|
|
@@ -354,6 +380,7 @@ Shared dependency or async boundary configuration failed.
|
|
|
354
380
|
Start with:
|
|
355
381
|
|
|
356
382
|
- `summary.shared`
|
|
383
|
+
- all `events` entries where `phase === "shared"`
|
|
357
384
|
- shared config in `.mf/observability/build-info.json` when available
|
|
358
385
|
- `summary.error.lifecycle`
|
|
359
386
|
- `diagnosis.actions`
|
|
@@ -365,6 +392,13 @@ Likely fixes:
|
|
|
365
392
|
- Fix `eager` for sync shared usage.
|
|
366
393
|
- Add an async startup or async boundary when needed.
|
|
367
394
|
|
|
395
|
+
Shared evidence is scoped to the MF instance that resolved the shared
|
|
396
|
+
dependency. It can identify the instance, shared package, selected
|
|
397
|
+
provider/version, and related config. Do not claim that a shared dependency was
|
|
398
|
+
caused by a specific remote/expose unless the report contains explicit evidence.
|
|
399
|
+
Bundler runtime chunk and module execution can trigger shared resolution after
|
|
400
|
+
remote loading, so remote/expose causality is not guaranteed.
|
|
401
|
+
|
|
368
402
|
### `RUNTIME-008`
|
|
369
403
|
|
|
370
404
|
RemoteEntry loading or execution failed.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 ScriptedAlchemy LLC (Zack Jackson) Zhou Shaw (zhouxiao)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -15,21 +15,19 @@ Node file output, and no raw error stack in console output.
|
|
|
15
15
|
import { createInstance } from '@module-federation/runtime';
|
|
16
16
|
import { ObservabilityPlugin } from '@module-federation/observability-plugin';
|
|
17
17
|
|
|
18
|
-
const observability = ObservabilityPlugin({
|
|
19
|
-
level: 'verbose',
|
|
20
|
-
browser: {
|
|
21
|
-
enabled: true,
|
|
22
|
-
scope: 'host',
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
|
|
26
18
|
createInstance({
|
|
27
19
|
name: 'host',
|
|
28
|
-
plugins: [
|
|
20
|
+
plugins: [
|
|
21
|
+
ObservabilityPlugin({
|
|
22
|
+
level: 'verbose',
|
|
23
|
+
browser: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
scope: 'host',
|
|
26
|
+
},
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
29
|
remotes: [],
|
|
30
30
|
});
|
|
31
|
-
|
|
32
|
-
const report = observability.getLatestReport();
|
|
33
31
|
```
|
|
34
32
|
|
|
35
33
|
The plugin does not upload data or expose a browser global by default. Reports
|
|
@@ -68,7 +66,13 @@ Failure hints are printed with `console.error` so browser DevTools, CDP-based
|
|
|
68
66
|
agents, Node logs, and log collection systems can detect that a Module
|
|
69
67
|
Federation load failed and then use the printed `traceId` to fetch the full
|
|
70
68
|
report. Successful or recovered reports are still available through the reader
|
|
71
|
-
APIs and callbacks, but they are not promoted to console errors.
|
|
69
|
+
APIs and callbacks, but they are not promoted to console errors. When the
|
|
70
|
+
browser reader is enabled in development mode, the plugin prints a small
|
|
71
|
+
`console.info` line by default when a `loadRemote` or `loadShare` trace starts.
|
|
72
|
+
The line includes the `traceId` and read command so an agent can inspect pending
|
|
73
|
+
loading state before a timeout or error happens. In production browser mode,
|
|
74
|
+
start logs are disabled by default; set `trace.printStart: true` only when you
|
|
75
|
+
explicitly want them.
|
|
72
76
|
|
|
73
77
|
The runtime only exposes the loading lifecycle hooks needed to know whether the
|
|
74
78
|
main flow started, succeeded, or failed. This plugin listens to those hooks,
|
|
@@ -76,7 +80,8 @@ derives detailed reasons like shared version mismatch or eager boundary issues,
|
|
|
76
80
|
and exposes the final loading state through a small `summary` object:
|
|
77
81
|
|
|
78
82
|
- `runtime-loaded`: Module Federation finished loading the remote module.
|
|
79
|
-
- `component-loaded`: business code called `markComponentLoaded
|
|
83
|
+
- `component-loaded`: business code called `markComponentLoaded`, or the
|
|
84
|
+
opt-in React lifecycle observer confirmed that the remote component mounted.
|
|
80
85
|
- `failed`: the load failed and `failedPhase` points to the first specific
|
|
81
86
|
failing phase.
|
|
82
87
|
- `recovered`: loading hit an error but a fallback/recovery path returned a
|
|
@@ -120,10 +125,36 @@ mismatch, and eager boundary errors are not retried by the retry plugin by
|
|
|
120
125
|
default because they are usually configuration or availability problems instead
|
|
121
126
|
of transient network failures.
|
|
122
127
|
|
|
123
|
-
Business code can mark its own success condition with a fixed event
|
|
128
|
+
Business code can mark its own success condition with a fixed event. When React
|
|
129
|
+
component lifecycle observation is enabled, the wrapper injects an
|
|
130
|
+
`onMFRemoteLoaded` prop into the remote component. The producer can call it when
|
|
131
|
+
the component's own ready condition is met:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { useEffect } from 'react';
|
|
135
|
+
import type { OnMFRemoteLoaded } from '@module-federation/observability-plugin';
|
|
136
|
+
|
|
137
|
+
export default function RemotePanel({ onMFRemoteLoaded }: { onMFRemoteLoaded?: OnMFRemoteLoaded }) {
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
onMFRemoteLoaded?.({
|
|
140
|
+
metadata: {
|
|
141
|
+
dataReady: true,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}, [onMFRemoteLoaded]);
|
|
145
|
+
|
|
146
|
+
return <section>Remote panel</section>;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If the app wants to mark readiness from the consumer side, it can still call the
|
|
151
|
+
instance method directly:
|
|
124
152
|
|
|
125
153
|
```ts
|
|
126
|
-
|
|
154
|
+
import { getInstance } from '@module-federation/runtime';
|
|
155
|
+
import '@module-federation/observability-plugin';
|
|
156
|
+
|
|
157
|
+
getInstance()?.markComponentLoaded({
|
|
127
158
|
requestId: 'remote/Button',
|
|
128
159
|
componentName: 'Button',
|
|
129
160
|
metadata: {
|
|
@@ -132,9 +163,40 @@ observability.markComponentLoaded({
|
|
|
132
163
|
});
|
|
133
164
|
```
|
|
134
165
|
|
|
135
|
-
|
|
166
|
+
Both paths record `component:business-loaded` on the same trace when possible.
|
|
136
167
|
Business metadata is optional. String values keep their original details and are
|
|
137
|
-
only length-limited, so user-provided metadata remains trustworthy.
|
|
168
|
+
only length-limited, so user-provided metadata remains trustworthy. The instance
|
|
169
|
+
method is attached when the observability plugin is registered. If an
|
|
170
|
+
application uses multiple runtime instances, call it on the instance that
|
|
171
|
+
registered this plugin.
|
|
172
|
+
|
|
173
|
+
React component lifecycle observation is available only when explicitly enabled:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
ObservabilityPlugin({
|
|
177
|
+
level: 'verbose',
|
|
178
|
+
react: {
|
|
179
|
+
enabled: true,
|
|
180
|
+
timeout: 5000,
|
|
181
|
+
consumerNames: ['runtime_host'],
|
|
182
|
+
remoteIds: ['remote/Button'],
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
When this option is enabled, the plugin tries to wrap remote function components
|
|
188
|
+
returned by `loadRemote`. The wrapper does not add DOM nodes. It injects the
|
|
189
|
+
`onMFRemoteLoaded` prop and records
|
|
190
|
+
`component:react-render-started`, `component:react-mounted`, or
|
|
191
|
+
`component:react-render-timeout`. This is useful for dev and AI debugging, but
|
|
192
|
+
it only means React mounted the component. Keep using `markComponentLoaded` when
|
|
193
|
+
the business definition of success depends on data, charts, SDKs, or other async
|
|
194
|
+
work inside the remote component.
|
|
195
|
+
|
|
196
|
+
Use `react.consumerNames` or `react.remoteIds` to limit this behavior to the
|
|
197
|
+
consumers or remote requests you are actively debugging. If both are empty, the
|
|
198
|
+
plugin observes detected React function components for every runtime instance
|
|
199
|
+
that registered it.
|
|
138
200
|
|
|
139
201
|
Browser output is available only when the plugin option explicitly enables it.
|
|
140
202
|
When browser output is enabled, the report can be read from:
|
|
@@ -147,11 +209,29 @@ window.__FEDERATION__.__OBSERVABILITY__.host.findReports({ remote: 'remote1' });
|
|
|
147
209
|
window.__FEDERATION__.__OBSERVABILITY__.host.exportReport('mf-trace-id');
|
|
148
210
|
```
|
|
149
211
|
|
|
150
|
-
|
|
151
|
-
`
|
|
152
|
-
|
|
153
|
-
`
|
|
154
|
-
|
|
212
|
+
`getReports({ limit })` returns recent reports newest first. `findReports()` can
|
|
213
|
+
filter by `traceId`, `remote`, `expose`, `shared`, `status`, or `outcome`.
|
|
214
|
+
`exportReport()` returns a copied report object, using the latest report when no
|
|
215
|
+
`traceId` is provided.
|
|
216
|
+
|
|
217
|
+
For agent-led development debugging where a page may stay in a loading state,
|
|
218
|
+
enable the browser reader. Development browser mode prints start traces by
|
|
219
|
+
default:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
ObservabilityPlugin({
|
|
223
|
+
level: 'verbose',
|
|
224
|
+
browser: {
|
|
225
|
+
enabled: true,
|
|
226
|
+
scope: 'host',
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
This prints only `loadRemote` and `loadShare` start lines. It does not print a
|
|
232
|
+
line for every internal phase. Set `trace.printStart: false` to disable it in
|
|
233
|
+
development browser mode. In production browser mode, set
|
|
234
|
+
`trace.printStart: true` to opt in.
|
|
155
235
|
|
|
156
236
|
For browser production use, set `browser.mode: "production"` when the runtime
|
|
157
237
|
console must stay minimal:
|
|
@@ -169,10 +249,10 @@ ObservabilityPlugin({
|
|
|
169
249
|
In production browser mode, the `console.error` hint only includes the `traceId`
|
|
170
250
|
and known `errorCode`. It does not print the report body, raw stack, request
|
|
171
251
|
URL, or `read:` command. Full reports are still available only through explicit
|
|
172
|
-
user choices such as
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
252
|
+
user choices such as `exportReport()` or an application-owned `onReport` upload.
|
|
253
|
+
Production applications that want richer observability should prefer
|
|
254
|
+
`onReport` / `onEvent` to forward reports to their own telemetry system instead
|
|
255
|
+
of exposing a public browser global.
|
|
176
256
|
|
|
177
257
|
Node file output is provided by the Node-specific entry:
|
|
178
258
|
|
|
@@ -180,15 +260,15 @@ Node file output is provided by the Node-specific entry:
|
|
|
180
260
|
import { createInstance } from '@module-federation/runtime';
|
|
181
261
|
import { ObservabilityPlugin } from '@module-federation/observability-plugin/node';
|
|
182
262
|
|
|
183
|
-
const observability = ObservabilityPlugin({
|
|
184
|
-
level: 'verbose',
|
|
185
|
-
fileOutput: true,
|
|
186
|
-
directory: '.mf/observability',
|
|
187
|
-
});
|
|
188
|
-
|
|
189
263
|
createInstance({
|
|
190
264
|
name: 'host',
|
|
191
|
-
plugins: [
|
|
265
|
+
plugins: [
|
|
266
|
+
ObservabilityPlugin({
|
|
267
|
+
level: 'verbose',
|
|
268
|
+
fileOutput: true,
|
|
269
|
+
directory: '.mf/observability',
|
|
270
|
+
}),
|
|
271
|
+
],
|
|
192
272
|
remotes: [],
|
|
193
273
|
});
|
|
194
274
|
```
|
|
@@ -293,3 +373,13 @@ Shared dependency reports include only evidence fields such as package name,
|
|
|
293
373
|
share scope, requested version, available versions, selected provider, and a
|
|
294
374
|
reason like `missing-provider`, `version-mismatch`, or `sync-async-boundary`.
|
|
295
375
|
They do not include shared factories, module values, source, or business data.
|
|
376
|
+
|
|
377
|
+
Shared observability is intentionally scoped to the Module Federation instance
|
|
378
|
+
that resolved the shared dependency. It can answer which MF instance loaded a
|
|
379
|
+
shared package, which registered provider/version was selected, and the related
|
|
380
|
+
scope/version/eager configuration. It does not guarantee a causal link from that
|
|
381
|
+
shared dependency back to a specific remote or expose, because shared resolution
|
|
382
|
+
can be triggered later by the bundler runtime while chunks and module factories
|
|
383
|
+
execute. When multiple shared dependencies are involved, read all
|
|
384
|
+
`phase: "shared"` events. `summary.shared` is only a compact last-observed
|
|
385
|
+
summary.
|
|
@@ -46,12 +46,24 @@ read: window.__FEDERATION__.__OBSERVABILITY__["runtime_host"].getReport("mf-..."
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Run the `read:` command exactly as printed. If the browser global reader is not
|
|
49
|
-
enabled,
|
|
49
|
+
enabled, ask for the application-owned `onReport` output or the uploaded
|
|
50
|
+
observability record.
|
|
51
|
+
|
|
52
|
+
If the user wants to inspect loading chains or says the page is stuck loading
|
|
53
|
+
without an error, do not wait for a failure hint. Read recent reports directly:
|
|
50
54
|
|
|
51
55
|
```ts
|
|
52
|
-
|
|
56
|
+
window.__FEDERATION__.__OBSERVABILITY__['runtime_host'].getReports({ limit: 10 });
|
|
53
57
|
```
|
|
54
58
|
|
|
59
|
+
Then look for `status: "pending"` or `summary.outcome: "pending"`. Use
|
|
60
|
+
`startedAt`, `updatedAt`, `duration`, `summary.phases`, and
|
|
61
|
+
`diagnosis.pendingPhases` to explain where the trace is currently stuck. When
|
|
62
|
+
the browser reader is enabled in development mode, the console prints
|
|
63
|
+
`Observability trace started` for `loadRemote` and `loadShare` by default; use
|
|
64
|
+
that `traceId` to read the exact report. In production browser mode, this start
|
|
65
|
+
log is disabled unless the app explicitly sets `trace.printStart: true`.
|
|
66
|
+
|
|
55
67
|
For the latest report:
|
|
56
68
|
|
|
57
69
|
```ts
|
|
@@ -90,8 +102,7 @@ errorCode: RUNTIME-...
|
|
|
90
102
|
|
|
91
103
|
Do not assume the full browser report is publicly readable when the `read:` line
|
|
92
104
|
is absent. Ask the application owner to export it with `exportReport(traceId)`,
|
|
93
|
-
|
|
94
|
-
application's own uploaded observability record.
|
|
105
|
+
or check the application's own uploaded observability record.
|
|
95
106
|
|
|
96
107
|
If the goal is ongoing observability instead of one-off debugging, ask whether
|
|
97
108
|
the application has enabled `onReport` or `onEvent`. In production, those
|
|
@@ -170,12 +181,27 @@ not by an AI model.
|
|
|
170
181
|
|
|
171
182
|
- `summary.outcome`: final outcome, such as `runtime-loaded`,
|
|
172
183
|
`component-loaded`, or `failed`.
|
|
184
|
+
- `summary.runtimeLoaded`: the remote module loaded successfully in the runtime.
|
|
185
|
+
- `summary.loadCompleted`: the `loadRemote` flow ended; this is not by itself a
|
|
186
|
+
success signal.
|
|
187
|
+
- `summary.componentLoaded`: a component-level success signal was observed. It
|
|
188
|
+
can come from business `markComponentLoaded` or from the opt-in React lifecycle
|
|
189
|
+
observer reporting `component:react-mounted`.
|
|
173
190
|
- `summary.phases`: per-phase status, duration, cache, retry, and recovery
|
|
174
191
|
markers.
|
|
175
192
|
- `summary.flags`: cross-phase cache, retry, fallback, and recovery markers.
|
|
176
|
-
- `summary.shared`: last observed shared provider/version result.
|
|
193
|
+
- `summary.shared`: last observed shared provider/version result. If multiple
|
|
194
|
+
shared dependencies are involved, inspect every `phase: "shared"` event.
|
|
177
195
|
- `summary.error`: compact error summary.
|
|
178
196
|
|
|
197
|
+
If React lifecycle observation is enabled, reports may include
|
|
198
|
+
`component:react-render-started`, `component:react-mounted`, or
|
|
199
|
+
`component:react-render-timeout`. Treat `component:react-mounted` as React mount
|
|
200
|
+
success only. It does not prove that business data, charts, or SDK
|
|
201
|
+
initialization completed. The wrapper also injects an `onMFRemoteLoaded` prop
|
|
202
|
+
into the remote React component; if the producer calls it, the report includes
|
|
203
|
+
`component:business-loaded`, which is the producer's explicit ready signal.
|
|
204
|
+
|
|
179
205
|
### Build files
|
|
180
206
|
|
|
181
207
|
Runtime reports do not embed build evidence. If you need build-side facts, read
|
|
@@ -354,6 +380,7 @@ Shared dependency or async boundary configuration failed.
|
|
|
354
380
|
Start with:
|
|
355
381
|
|
|
356
382
|
- `summary.shared`
|
|
383
|
+
- all `events` entries where `phase === "shared"`
|
|
357
384
|
- shared config in `.mf/observability/build-info.json` when available
|
|
358
385
|
- `summary.error.lifecycle`
|
|
359
386
|
- `diagnosis.actions`
|
|
@@ -365,6 +392,13 @@ Likely fixes:
|
|
|
365
392
|
- Fix `eager` for sync shared usage.
|
|
366
393
|
- Add an async startup or async boundary when needed.
|
|
367
394
|
|
|
395
|
+
Shared evidence is scoped to the MF instance that resolved the shared
|
|
396
|
+
dependency. It can identify the instance, shared package, selected
|
|
397
|
+
provider/version, and related config. Do not claim that a shared dependency was
|
|
398
|
+
caused by a specific remote/expose unless the report contains explicit evidence.
|
|
399
|
+
Bundler runtime chunk and module execution can trigger shared resolution after
|
|
400
|
+
remote loading, so remote/expose causality is not guaranteed.
|
|
401
|
+
|
|
368
402
|
### `RUNTIME-008`
|
|
369
403
|
|
|
370
404
|
RemoteEntry loading or execution failed.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @module-federation/observability-plugin
|
|
2
|
+
|
|
3
|
+
## 0.0.0-feat-federationdiagnosticerror-20260511123257
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9b21865: Add an opt-in observability plugin, a direct runtime plugin API with instance-bound component loaded marks, opt-in React component lifecycle observation with automatic `onMFRemoteLoaded` prop injection, opt-in start console traces for `loadRemote` and `loadShare`, a Node-specific export for file reports, a build-specific export for build summaries and build error reports, remote and shared lifecycle hooks, console trace hints, safe browser/Node report outputs, configurable error stack capture with explicit console raw-stack opt-ins, shared/eager loading evidence, final loading outcome summaries for Module Federation loading reports, deterministic fact reports for runtime and build failures, no-op return handling for observer hooks, detailed remote match/init/expose/factory phase events with phase durations, compact phase summaries, cache/retry/fallback markers, length-limited business component metadata, clipped moduleInfo evidence with preserved deployment locator fields for snapshot-dependent failures, normalized runtime error summaries with error codes, owner hints, retryability, and safe context, dedicated runtime error codes for invalid manifests, missing exposes, and remote container init failures, plus an AI troubleshooting guide for reading and fixing observability reports.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [5d4095d]
|
|
12
|
+
- @module-federation/sdk@0.0.0-feat-federationdiagnosticerror-20260511123257
|
package/dist/README.md
CHANGED
|
@@ -15,21 +15,19 @@ Node file output, and no raw error stack in console output.
|
|
|
15
15
|
import { createInstance } from '@module-federation/runtime';
|
|
16
16
|
import { ObservabilityPlugin } from '@module-federation/observability-plugin';
|
|
17
17
|
|
|
18
|
-
const observability = ObservabilityPlugin({
|
|
19
|
-
level: 'verbose',
|
|
20
|
-
browser: {
|
|
21
|
-
enabled: true,
|
|
22
|
-
scope: 'host',
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
|
|
26
18
|
createInstance({
|
|
27
19
|
name: 'host',
|
|
28
|
-
plugins: [
|
|
20
|
+
plugins: [
|
|
21
|
+
ObservabilityPlugin({
|
|
22
|
+
level: 'verbose',
|
|
23
|
+
browser: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
scope: 'host',
|
|
26
|
+
},
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
29
|
remotes: [],
|
|
30
30
|
});
|
|
31
|
-
|
|
32
|
-
const report = observability.getLatestReport();
|
|
33
31
|
```
|
|
34
32
|
|
|
35
33
|
The plugin does not upload data or expose a browser global by default. Reports
|
|
@@ -68,7 +66,13 @@ Failure hints are printed with `console.error` so browser DevTools, CDP-based
|
|
|
68
66
|
agents, Node logs, and log collection systems can detect that a Module
|
|
69
67
|
Federation load failed and then use the printed `traceId` to fetch the full
|
|
70
68
|
report. Successful or recovered reports are still available through the reader
|
|
71
|
-
APIs and callbacks, but they are not promoted to console errors.
|
|
69
|
+
APIs and callbacks, but they are not promoted to console errors. When the
|
|
70
|
+
browser reader is enabled in development mode, the plugin prints a small
|
|
71
|
+
`console.info` line by default when a `loadRemote` or `loadShare` trace starts.
|
|
72
|
+
The line includes the `traceId` and read command so an agent can inspect pending
|
|
73
|
+
loading state before a timeout or error happens. In production browser mode,
|
|
74
|
+
start logs are disabled by default; set `trace.printStart: true` only when you
|
|
75
|
+
explicitly want them.
|
|
72
76
|
|
|
73
77
|
The runtime only exposes the loading lifecycle hooks needed to know whether the
|
|
74
78
|
main flow started, succeeded, or failed. This plugin listens to those hooks,
|
|
@@ -76,7 +80,8 @@ derives detailed reasons like shared version mismatch or eager boundary issues,
|
|
|
76
80
|
and exposes the final loading state through a small `summary` object:
|
|
77
81
|
|
|
78
82
|
- `runtime-loaded`: Module Federation finished loading the remote module.
|
|
79
|
-
- `component-loaded`: business code called `markComponentLoaded
|
|
83
|
+
- `component-loaded`: business code called `markComponentLoaded`, or the
|
|
84
|
+
opt-in React lifecycle observer confirmed that the remote component mounted.
|
|
80
85
|
- `failed`: the load failed and `failedPhase` points to the first specific
|
|
81
86
|
failing phase.
|
|
82
87
|
- `recovered`: loading hit an error but a fallback/recovery path returned a
|
|
@@ -120,10 +125,36 @@ mismatch, and eager boundary errors are not retried by the retry plugin by
|
|
|
120
125
|
default because they are usually configuration or availability problems instead
|
|
121
126
|
of transient network failures.
|
|
122
127
|
|
|
123
|
-
Business code can mark its own success condition with a fixed event
|
|
128
|
+
Business code can mark its own success condition with a fixed event. When React
|
|
129
|
+
component lifecycle observation is enabled, the wrapper injects an
|
|
130
|
+
`onMFRemoteLoaded` prop into the remote component. The producer can call it when
|
|
131
|
+
the component's own ready condition is met:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { useEffect } from 'react';
|
|
135
|
+
import type { OnMFRemoteLoaded } from '@module-federation/observability-plugin';
|
|
136
|
+
|
|
137
|
+
export default function RemotePanel({ onMFRemoteLoaded }: { onMFRemoteLoaded?: OnMFRemoteLoaded }) {
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
onMFRemoteLoaded?.({
|
|
140
|
+
metadata: {
|
|
141
|
+
dataReady: true,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}, [onMFRemoteLoaded]);
|
|
145
|
+
|
|
146
|
+
return <section>Remote panel</section>;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If the app wants to mark readiness from the consumer side, it can still call the
|
|
151
|
+
instance method directly:
|
|
124
152
|
|
|
125
153
|
```ts
|
|
126
|
-
|
|
154
|
+
import { getInstance } from '@module-federation/runtime';
|
|
155
|
+
import '@module-federation/observability-plugin';
|
|
156
|
+
|
|
157
|
+
getInstance()?.markComponentLoaded({
|
|
127
158
|
requestId: 'remote/Button',
|
|
128
159
|
componentName: 'Button',
|
|
129
160
|
metadata: {
|
|
@@ -132,9 +163,40 @@ observability.markComponentLoaded({
|
|
|
132
163
|
});
|
|
133
164
|
```
|
|
134
165
|
|
|
135
|
-
|
|
166
|
+
Both paths record `component:business-loaded` on the same trace when possible.
|
|
136
167
|
Business metadata is optional. String values keep their original details and are
|
|
137
|
-
only length-limited, so user-provided metadata remains trustworthy.
|
|
168
|
+
only length-limited, so user-provided metadata remains trustworthy. The instance
|
|
169
|
+
method is attached when the observability plugin is registered. If an
|
|
170
|
+
application uses multiple runtime instances, call it on the instance that
|
|
171
|
+
registered this plugin.
|
|
172
|
+
|
|
173
|
+
React component lifecycle observation is available only when explicitly enabled:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
ObservabilityPlugin({
|
|
177
|
+
level: 'verbose',
|
|
178
|
+
react: {
|
|
179
|
+
enabled: true,
|
|
180
|
+
timeout: 5000,
|
|
181
|
+
consumerNames: ['runtime_host'],
|
|
182
|
+
remoteIds: ['remote/Button'],
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
When this option is enabled, the plugin tries to wrap remote function components
|
|
188
|
+
returned by `loadRemote`. The wrapper does not add DOM nodes. It injects the
|
|
189
|
+
`onMFRemoteLoaded` prop and records
|
|
190
|
+
`component:react-render-started`, `component:react-mounted`, or
|
|
191
|
+
`component:react-render-timeout`. This is useful for dev and AI debugging, but
|
|
192
|
+
it only means React mounted the component. Keep using `markComponentLoaded` when
|
|
193
|
+
the business definition of success depends on data, charts, SDKs, or other async
|
|
194
|
+
work inside the remote component.
|
|
195
|
+
|
|
196
|
+
Use `react.consumerNames` or `react.remoteIds` to limit this behavior to the
|
|
197
|
+
consumers or remote requests you are actively debugging. If both are empty, the
|
|
198
|
+
plugin observes detected React function components for every runtime instance
|
|
199
|
+
that registered it.
|
|
138
200
|
|
|
139
201
|
Browser output is available only when the plugin option explicitly enables it.
|
|
140
202
|
When browser output is enabled, the report can be read from:
|
|
@@ -147,11 +209,29 @@ window.__FEDERATION__.__OBSERVABILITY__.host.findReports({ remote: 'remote1' });
|
|
|
147
209
|
window.__FEDERATION__.__OBSERVABILITY__.host.exportReport('mf-trace-id');
|
|
148
210
|
```
|
|
149
211
|
|
|
150
|
-
|
|
151
|
-
`
|
|
152
|
-
|
|
153
|
-
`
|
|
154
|
-
|
|
212
|
+
`getReports({ limit })` returns recent reports newest first. `findReports()` can
|
|
213
|
+
filter by `traceId`, `remote`, `expose`, `shared`, `status`, or `outcome`.
|
|
214
|
+
`exportReport()` returns a copied report object, using the latest report when no
|
|
215
|
+
`traceId` is provided.
|
|
216
|
+
|
|
217
|
+
For agent-led development debugging where a page may stay in a loading state,
|
|
218
|
+
enable the browser reader. Development browser mode prints start traces by
|
|
219
|
+
default:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
ObservabilityPlugin({
|
|
223
|
+
level: 'verbose',
|
|
224
|
+
browser: {
|
|
225
|
+
enabled: true,
|
|
226
|
+
scope: 'host',
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
This prints only `loadRemote` and `loadShare` start lines. It does not print a
|
|
232
|
+
line for every internal phase. Set `trace.printStart: false` to disable it in
|
|
233
|
+
development browser mode. In production browser mode, set
|
|
234
|
+
`trace.printStart: true` to opt in.
|
|
155
235
|
|
|
156
236
|
For browser production use, set `browser.mode: "production"` when the runtime
|
|
157
237
|
console must stay minimal:
|
|
@@ -169,10 +249,10 @@ ObservabilityPlugin({
|
|
|
169
249
|
In production browser mode, the `console.error` hint only includes the `traceId`
|
|
170
250
|
and known `errorCode`. It does not print the report body, raw stack, request
|
|
171
251
|
URL, or `read:` command. Full reports are still available only through explicit
|
|
172
|
-
user choices such as
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
252
|
+
user choices such as `exportReport()` or an application-owned `onReport` upload.
|
|
253
|
+
Production applications that want richer observability should prefer
|
|
254
|
+
`onReport` / `onEvent` to forward reports to their own telemetry system instead
|
|
255
|
+
of exposing a public browser global.
|
|
176
256
|
|
|
177
257
|
Node file output is provided by the Node-specific entry:
|
|
178
258
|
|
|
@@ -180,15 +260,15 @@ Node file output is provided by the Node-specific entry:
|
|
|
180
260
|
import { createInstance } from '@module-federation/runtime';
|
|
181
261
|
import { ObservabilityPlugin } from '@module-federation/observability-plugin/node';
|
|
182
262
|
|
|
183
|
-
const observability = ObservabilityPlugin({
|
|
184
|
-
level: 'verbose',
|
|
185
|
-
fileOutput: true,
|
|
186
|
-
directory: '.mf/observability',
|
|
187
|
-
});
|
|
188
|
-
|
|
189
263
|
createInstance({
|
|
190
264
|
name: 'host',
|
|
191
|
-
plugins: [
|
|
265
|
+
plugins: [
|
|
266
|
+
ObservabilityPlugin({
|
|
267
|
+
level: 'verbose',
|
|
268
|
+
fileOutput: true,
|
|
269
|
+
directory: '.mf/observability',
|
|
270
|
+
}),
|
|
271
|
+
],
|
|
192
272
|
remotes: [],
|
|
193
273
|
});
|
|
194
274
|
```
|
|
@@ -293,3 +373,13 @@ Shared dependency reports include only evidence fields such as package name,
|
|
|
293
373
|
share scope, requested version, available versions, selected provider, and a
|
|
294
374
|
reason like `missing-provider`, `version-mismatch`, or `sync-async-boundary`.
|
|
295
375
|
They do not include shared factories, module values, source, or business data.
|
|
376
|
+
|
|
377
|
+
Shared observability is intentionally scoped to the Module Federation instance
|
|
378
|
+
that resolved the shared dependency. It can answer which MF instance loaded a
|
|
379
|
+
shared package, which registered provider/version was selected, and the related
|
|
380
|
+
scope/version/eager configuration. It does not guarantee a causal link from that
|
|
381
|
+
shared dependency back to a specific remote or expose, because shared resolution
|
|
382
|
+
can be triggered later by the bundler runtime while chunks and module factories
|
|
383
|
+
execute. When multiple shared dependencies are involved, read all
|
|
384
|
+
`phase: "shared"` events. `summary.shared` is only a compact last-observed
|
|
385
|
+
summary.
|