@module-federation/observability-plugin 0.0.0-alpha.0
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 +498 -0
- package/README.md +295 -0
- package/dist/AI_TROUBLESHOOTING.md +498 -0
- package/dist/README.md +295 -0
- package/dist/build.d.ts +194 -0
- package/dist/build.js +710 -0
- package/dist/esm/build.js +679 -0
- package/dist/esm/index.js +1442 -0
- package/dist/esm/node.js +156 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.js +1444 -0
- package/dist/node.d.ts +13 -0
- package/dist/node.js +158 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# @module-federation/observability-plugin
|
|
2
|
+
|
|
3
|
+
Runtime observability plugin for Module Federation loading flows.
|
|
4
|
+
|
|
5
|
+
This package is designed for Module Federation `2.5.0` and later. Older
|
|
6
|
+
projects can still use runtime error codes, but the full observability workflow
|
|
7
|
+
requires upgrading the MF runtime and installing this plugin.
|
|
8
|
+
|
|
9
|
+
This package is currently the minimal observability foundation. It records
|
|
10
|
+
structured in-memory loading events when the plugin is installed and not
|
|
11
|
+
disabled. Optional outputs are conservative by default: no browser global, no
|
|
12
|
+
Node file output, and no raw error stack in console output.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { createInstance } from '@module-federation/runtime';
|
|
16
|
+
import { ObservabilityPlugin } from '@module-federation/observability-plugin';
|
|
17
|
+
|
|
18
|
+
const observability = ObservabilityPlugin({
|
|
19
|
+
level: 'verbose',
|
|
20
|
+
browser: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
scope: 'host',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
createInstance({
|
|
27
|
+
name: 'host',
|
|
28
|
+
plugins: [observability.plugin],
|
|
29
|
+
remotes: [],
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const report = observability.getLatestReport();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The plugin does not upload data or expose a browser global by default. Reports
|
|
36
|
+
are kept in memory. Runtime request URLs, error messages, and stored error
|
|
37
|
+
stacks keep their original query/hash and error details because those values are
|
|
38
|
+
often needed for debugging. Large deployment locator fields such as
|
|
39
|
+
`publicPath` and `remoteEntry` are only length-limited.
|
|
40
|
+
|
|
41
|
+
## Safe Observability
|
|
42
|
+
|
|
43
|
+
Enable only the output channels that match the environment:
|
|
44
|
+
|
|
45
|
+
- Browser dev: use `browser.enabled: true` with a scoped reader such as
|
|
46
|
+
`window.__FEDERATION__.__OBSERVABILITY__.host`.
|
|
47
|
+
- Browser prod: set `browser.mode: "production"` so console output stays limited
|
|
48
|
+
to `traceId` and known `errorCode`; export full reports only through explicit
|
|
49
|
+
app-owned flows such as `exportReport()` or `onReport`.
|
|
50
|
+
- Node / SSR: use the Node entry
|
|
51
|
+
`@module-federation/observability-plugin/node` and set `fileOutput: true` when
|
|
52
|
+
local files are needed.
|
|
53
|
+
- Build: use `ObservabilityBuildPlugin` when build-side files are needed for
|
|
54
|
+
later comparison.
|
|
55
|
+
|
|
56
|
+
Reports include the loading timeline, selected host/remote/shared facts,
|
|
57
|
+
original runtime URLs, original error message/stack, clipped deployment
|
|
58
|
+
`moduleInfo` when it is relevant, and user-provided observability
|
|
59
|
+
metadata with count and length limits. Reports do not collect request headers,
|
|
60
|
+
cookies, authorization values, remote response bodies, remote source, module
|
|
61
|
+
source, React props, full `moduleInfo.modules`, full `moduleInfo.shared`, or
|
|
62
|
+
asset lists from deployment `moduleInfo`.
|
|
63
|
+
Fields whose value is `undefined` are omitted from returned reports and events,
|
|
64
|
+
so missing fields should be read as "not observed or not relevant" instead of a
|
|
65
|
+
literal value.
|
|
66
|
+
|
|
67
|
+
Failure hints are printed with `console.error` so browser DevTools, CDP-based
|
|
68
|
+
agents, Node logs, and log collection systems can detect that a Module
|
|
69
|
+
Federation load failed and then use the printed `traceId` to fetch the full
|
|
70
|
+
report. Successful or recovered reports are still available through the reader
|
|
71
|
+
APIs and callbacks, but they are not promoted to console errors.
|
|
72
|
+
|
|
73
|
+
The runtime only exposes the loading lifecycle hooks needed to know whether the
|
|
74
|
+
main flow started, succeeded, or failed. This plugin listens to those hooks,
|
|
75
|
+
derives detailed reasons like shared version mismatch or eager boundary issues,
|
|
76
|
+
and exposes the final loading state through a small `summary` object:
|
|
77
|
+
|
|
78
|
+
- `runtime-loaded`: Module Federation finished loading the remote module.
|
|
79
|
+
- `component-loaded`: business code called `markComponentLoaded`.
|
|
80
|
+
- `failed`: the load failed and `failedPhase` points to the first specific
|
|
81
|
+
failing phase.
|
|
82
|
+
- `recovered`: loading hit an error but a fallback/recovery path returned a
|
|
83
|
+
result.
|
|
84
|
+
|
|
85
|
+
For remote loading, the plugin listens to runtime lifecycle hooks such as
|
|
86
|
+
`beforeRequest`, `afterMatchRemote`, `onLoad`, `afterLoadRemote`,
|
|
87
|
+
`errorLoadRemote`, `loadEntry`, `afterLoadEntry`, `beforeInitRemote`,
|
|
88
|
+
`afterInitRemote`, `beforeGetExpose`, `afterGetExpose`,
|
|
89
|
+
`beforeExecuteFactory`, `afterExecuteFactory`, and snapshot resolve hooks. It
|
|
90
|
+
does not return a value from observer hooks, so it does not change fallback or
|
|
91
|
+
retry results from plugins such as `@module-federation/retry-plugin`.
|
|
92
|
+
|
|
93
|
+
Successful verbose reports include the key runtime stages: remote match,
|
|
94
|
+
manifest, remoteEntry load, remoteEntry init, expose resolution, module factory
|
|
95
|
+
execution, and final load completion. When a stage has matching start and end
|
|
96
|
+
events, the end event includes a bounded `duration` value.
|
|
97
|
+
|
|
98
|
+
The report also keeps compact loading state under `summary`. It contains the
|
|
99
|
+
final outcome, per-phase status and duration under `summary.phases`, safe
|
|
100
|
+
cache/recovery markers under `summary.flags`, and the last resolved shared
|
|
101
|
+
provider/version under `summary.shared` when a shared dependency was observed.
|
|
102
|
+
`level: "summary"` omits start events from the stored timeline but still keeps
|
|
103
|
+
the derived durations on the matching success/error events. `level: "verbose"`
|
|
104
|
+
keeps the full timeline.
|
|
105
|
+
|
|
106
|
+
Each report also includes a deterministic `diagnosis` object. It is generated
|
|
107
|
+
by engineering rules, not by an AI model. It keeps the final outcome, likely
|
|
108
|
+
owner, completed and pending phases, observability facts, documentation link when
|
|
109
|
+
a known runtime error code is present, and a short list of next checks. This is
|
|
110
|
+
the field a person or AI coding agent should read first before falling back to
|
|
111
|
+
the raw `events` timeline.
|
|
112
|
+
|
|
113
|
+
For a stable report-reading and fixing workflow, see
|
|
114
|
+
[`AI_TROUBLESHOOTING.md`](./AI_TROUBLESHOOTING.md). Codex-style agents can also
|
|
115
|
+
use the repository's single `mf` skill entry with the `observability`
|
|
116
|
+
sub-command.
|
|
117
|
+
|
|
118
|
+
`errorLoadShare` is used only for observation. Shared dependency miss, version
|
|
119
|
+
mismatch, and eager boundary errors are not retried by the retry plugin by
|
|
120
|
+
default because they are usually configuration or availability problems instead
|
|
121
|
+
of transient network failures.
|
|
122
|
+
|
|
123
|
+
Business code can mark its own success condition with a fixed event:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
observability.markComponentLoaded({
|
|
127
|
+
requestId: 'remote/Button',
|
|
128
|
+
componentName: 'Button',
|
|
129
|
+
metadata: {
|
|
130
|
+
route: '/settings',
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This records `component:business-loaded` on the same trace when possible.
|
|
136
|
+
Business metadata is optional. String values keep their original details and are
|
|
137
|
+
only length-limited, so user-provided metadata remains trustworthy.
|
|
138
|
+
|
|
139
|
+
Browser output is available only when the plugin option explicitly enables it.
|
|
140
|
+
When browser output is enabled, the report can be read from:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
window.__FEDERATION__.__OBSERVABILITY__.host.getLatestReport();
|
|
144
|
+
window.__FEDERATION__.__OBSERVABILITY__.host.getReport('mf-trace-id');
|
|
145
|
+
window.__FEDERATION__.__OBSERVABILITY__.host.getReports({ limit: 5 });
|
|
146
|
+
window.__FEDERATION__.__OBSERVABILITY__.host.findReports({ remote: 'remote1' });
|
|
147
|
+
window.__FEDERATION__.__OBSERVABILITY__.host.exportReport('mf-trace-id');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The same methods are available on the observability controller returned by
|
|
151
|
+
`ObservabilityPlugin()`. `getReports({ limit })` returns recent reports newest
|
|
152
|
+
first. `findReports()` can filter by `traceId`, `remote`, `expose`, `shared`,
|
|
153
|
+
`status`, or `outcome`. `exportReport()` returns a copied report object, using
|
|
154
|
+
the latest report when no `traceId` is provided.
|
|
155
|
+
|
|
156
|
+
For browser production use, set `browser.mode: "production"` when the runtime
|
|
157
|
+
console must stay minimal:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
ObservabilityPlugin({
|
|
161
|
+
browser: {
|
|
162
|
+
enabled: true,
|
|
163
|
+
scope: 'host',
|
|
164
|
+
mode: 'production',
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
In production browser mode, the `console.error` hint only includes the `traceId`
|
|
170
|
+
and known `errorCode`. It does not print the report body, raw stack, request
|
|
171
|
+
URL, or `read:` command. Full reports are still available only through explicit
|
|
172
|
+
user choices such as the observability controller, `exportReport()`, or an
|
|
173
|
+
application-owned `onReport` upload. Production applications that want richer
|
|
174
|
+
observability should prefer `onReport` / `onEvent` to forward reports to their
|
|
175
|
+
own telemetry system instead of exposing a public browser global.
|
|
176
|
+
|
|
177
|
+
Node file output is provided by the Node-specific entry:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { createInstance } from '@module-federation/runtime';
|
|
181
|
+
import { ObservabilityPlugin } from '@module-federation/observability-plugin/node';
|
|
182
|
+
|
|
183
|
+
const observability = ObservabilityPlugin({
|
|
184
|
+
level: 'verbose',
|
|
185
|
+
fileOutput: true,
|
|
186
|
+
directory: '.mf/observability',
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
createInstance({
|
|
190
|
+
name: 'host',
|
|
191
|
+
plugins: [observability.plugin],
|
|
192
|
+
remotes: [],
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
When Node file output is explicitly enabled, the Node entry writes:
|
|
197
|
+
|
|
198
|
+
- `.mf/observability/latest.json`: a formatted copy of the latest complete
|
|
199
|
+
report, including `traceId`, top-level status/error fields, `diagnosis`,
|
|
200
|
+
`summary`, clipped `moduleInfo` when relevant, and the report's own `events`.
|
|
201
|
+
- `.mf/observability/events.jsonl`: append-only event stream. Each line is one
|
|
202
|
+
JSON object for one runtime event and includes fields such as `traceId`,
|
|
203
|
+
`timestamp`, `phase`, `status`, remote/shared/expose context, and error
|
|
204
|
+
fields when present.
|
|
205
|
+
|
|
206
|
+
Read `latest.json` first. Use `events.jsonl` only when multiple traces must be
|
|
207
|
+
compared or when the full event ordering for a `traceId` is needed.
|
|
208
|
+
|
|
209
|
+
On errors, the plugin prints a small console hint with the `traceId` and the
|
|
210
|
+
available read path. The console hint is intentionally small and does not carry
|
|
211
|
+
the full report or the raw stack by default. If a user explicitly needs the
|
|
212
|
+
full stack, they can opt in with `printRawStack: true` or capture the original
|
|
213
|
+
error through `onRawError`. The default browser/runtime entry does not include
|
|
214
|
+
Node file output code.
|
|
215
|
+
|
|
216
|
+
Build-time observability is provided by the build-specific entry:
|
|
217
|
+
|
|
218
|
+
```js
|
|
219
|
+
const { ModuleFederationPlugin } = require('@module-federation/enhanced/webpack');
|
|
220
|
+
const { ObservabilityBuildPlugin } = require('@module-federation/observability-plugin/build');
|
|
221
|
+
|
|
222
|
+
const moduleFederationOptions = {
|
|
223
|
+
name: 'host',
|
|
224
|
+
remotes: {
|
|
225
|
+
remote1: 'remote1@http://localhost:3001/mf-manifest.json',
|
|
226
|
+
},
|
|
227
|
+
exposes: {
|
|
228
|
+
'./Button': './src/Button',
|
|
229
|
+
},
|
|
230
|
+
shared: {
|
|
231
|
+
react: { singleton: true, requiredVersion: '^18.0.0' },
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
module.exports = {
|
|
236
|
+
plugins: [
|
|
237
|
+
new ModuleFederationPlugin(moduleFederationOptions),
|
|
238
|
+
new ObservabilityBuildPlugin({
|
|
239
|
+
moduleFederation: moduleFederationOptions,
|
|
240
|
+
}),
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
When this optional build plugin is installed, it writes
|
|
246
|
+
`.mf/observability/build-info.json`. The file is a summary of the Module
|
|
247
|
+
Federation build configuration and generated manifest/stats facts:
|
|
248
|
+
bundler name/version, Module Federation plugin version when available, build
|
|
249
|
+
version when available, remoteEntry file/type/publicPath mode, remotes,
|
|
250
|
+
exposes, and shared dependencies. It intentionally omits local expose source
|
|
251
|
+
paths, asset lists, source code, and environment variables. Remote URLs and the
|
|
252
|
+
`remoteEntry.publicPath` deployment locator keep query/hash data. If build
|
|
253
|
+
observability output fails, the build continues and a bundler warning is emitted.
|
|
254
|
+
|
|
255
|
+
When the build has compilation errors, or when the observability plugin cannot
|
|
256
|
+
write its own build output, the build plugin writes
|
|
257
|
+
`.mf/observability/build-report.json`. The report has the same high-level shape
|
|
258
|
+
as runtime reports: a `traceId`, `status`, `failedPhase`, `events`,
|
|
259
|
+
`summary.error`, `diagnosis`, and the top-level `build` object. Clean builds
|
|
260
|
+
remove the stale report file so readers do not mistake an old build error for
|
|
261
|
+
the current state.
|
|
262
|
+
|
|
263
|
+
Runtime reports do not include build facts. `summary` is only the loading-state
|
|
264
|
+
view. If debugging needs build-side evidence, read
|
|
265
|
+
`.mf/observability/build-info.json` or `.mf/observability/build-report.json`
|
|
266
|
+
separately and compare it with the runtime report.
|
|
267
|
+
|
|
268
|
+
For snapshot-dependent failures, such as `RUNTIME-007`, reports can also include
|
|
269
|
+
top-level `moduleInfo`. This field is collected only for failures that depend on
|
|
270
|
+
`__FEDERATION__.moduleInfo`. It is clipped by default: the plugin keeps matching
|
|
271
|
+
entries with only `name`, `publicPath`, `getPublicPath`, `remoteEntry`, and
|
|
272
|
+
`globalName`, keeps the deployment locator fields only length-limited, and
|
|
273
|
+
removes large fields such as `modules` and `shared`.
|
|
274
|
+
|
|
275
|
+
Runtime errors are normalized into stable fields on both events and reports:
|
|
276
|
+
|
|
277
|
+
- `errorCode`: for example `RUNTIME-003` or `RUNTIME-008` when the original
|
|
278
|
+
error includes one.
|
|
279
|
+
- `failedPhase` and `lifecycle`: where the failure happened.
|
|
280
|
+
- `ownerHint`: a deterministic hint such as `host`, `remote`, `shared`, or
|
|
281
|
+
`network`.
|
|
282
|
+
- `retryable`: whether the observed failure looks transient.
|
|
283
|
+
- `errorContext`: a context object with values such as manifest URL,
|
|
284
|
+
remote name, `entryGlobalName`, request id, expose, or shared package.
|
|
285
|
+
|
|
286
|
+
The first batch includes specific diagnosis facts and actions for
|
|
287
|
+
`RUNTIME-001`, `RUNTIME-003`, `RUNTIME-004`, `RUNTIME-005`, `RUNTIME-006`, and
|
|
288
|
+
`RUNTIME-008`. `RUNTIME-008` is further classified as `network`, `timeout`,
|
|
289
|
+
`script-execution`, or `unknown` in `errorContext.resourceErrorType` and
|
|
290
|
+
`diagnosis.facts.resourceErrorType`.
|
|
291
|
+
|
|
292
|
+
Shared dependency reports include only evidence fields such as package name,
|
|
293
|
+
share scope, requested version, available versions, selected provider, and a
|
|
294
|
+
reason like `missing-provider`, `version-mismatch`, or `sync-async-boundary`.
|
|
295
|
+
They do not include shared factories, module values, source, or business data.
|