@ohos-ports/dxos-tracing 0.11.1-beta.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/LICENSE +105 -0
- package/README.md +3 -0
- package/dist/lib/index.mjs +509 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/types/src/api.d.ts +47 -0
- package/dist/types/src/api.d.ts.map +1 -0
- package/dist/types/src/buffering-backend.d.ts +24 -0
- package/dist/types/src/buffering-backend.d.ts.map +1 -0
- package/dist/types/src/diagnostic.d.ts +38 -0
- package/dist/types/src/diagnostic.d.ts.map +1 -0
- package/dist/types/src/diagnostics-channel.d.ts +34 -0
- package/dist/types/src/diagnostics-channel.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +7 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/remote/index.d.ts +2 -0
- package/dist/types/src/remote/index.d.ts.map +1 -0
- package/dist/types/src/remote/metrics.d.ts +37 -0
- package/dist/types/src/remote/metrics.d.ts.map +1 -0
- package/dist/types/src/trace-processor.d.ts +29 -0
- package/dist/types/src/trace-processor.d.ts.map +1 -0
- package/dist/types/src/tracing-types.d.ts +67 -0
- package/dist/types/src/tracing-types.d.ts.map +1 -0
- package/dist/types/src/tracing.test.d.ts +2 -0
- package/dist/types/src/tracing.test.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +2 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +42 -0
- package/src/api.ts +204 -0
- package/src/buffering-backend.ts +112 -0
- package/src/diagnostic.ts +103 -0
- package/src/diagnostics-channel.ts +174 -0
- package/src/index.ts +25 -0
- package/src/remote/index.ts +5 -0
- package/src/remote/metrics.ts +57 -0
- package/src/trace-processor.ts +82 -0
- package/src/tracing-types.ts +77 -0
- package/src/tracing.test.ts +296 -0
- package/src/util.ts +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, ALv2 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-Apache-2.0
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 DXOS
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
91
|
+
the date we make the Software available. On or after that date, you may use the
|
|
92
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
93
|
+
will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
96
|
+
this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
103
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
|
+
specific language governing permissions and limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import { Context, TRACE_SPAN_ATTRIBUTE } from "@dxos/context";
|
|
2
|
+
import { Trigger, asyncTimeout, sleep } from "@dxos/async";
|
|
3
|
+
import { invariant } from "@dxos/invariant";
|
|
4
|
+
//#region src/buffering-backend.ts
|
|
5
|
+
var BUFFERED_PREFIX = "buffered-";
|
|
6
|
+
/**
|
|
7
|
+
* Span handle that records operations while no real OTEL backend is available.
|
|
8
|
+
* Once a real backend is registered, the buffered span is replayed and a
|
|
9
|
+
* {@link delegate} is set so future calls forward to the real span.
|
|
10
|
+
*/
|
|
11
|
+
var BufferedSpan = class {
|
|
12
|
+
options;
|
|
13
|
+
spanContext;
|
|
14
|
+
startTime;
|
|
15
|
+
delegate;
|
|
16
|
+
#ended = false;
|
|
17
|
+
#endTime;
|
|
18
|
+
#error;
|
|
19
|
+
#hasError = false;
|
|
20
|
+
constructor(options, id) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
this.spanContext = { traceparent: `${BUFFERED_PREFIX}${id}` };
|
|
23
|
+
this.startTime = Date.now();
|
|
24
|
+
}
|
|
25
|
+
end(endTime) {
|
|
26
|
+
if (this.delegate) {
|
|
27
|
+
this.delegate.end(endTime);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.#endTime = endTime ?? Date.now();
|
|
31
|
+
this.#ended = true;
|
|
32
|
+
}
|
|
33
|
+
setError(err) {
|
|
34
|
+
if (this.delegate) {
|
|
35
|
+
this.delegate.setError?.(err);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.#error = err;
|
|
39
|
+
this.#hasError = true;
|
|
40
|
+
}
|
|
41
|
+
replay(real) {
|
|
42
|
+
if (this.#hasError) real.setError?.(this.#error);
|
|
43
|
+
if (this.#ended) real.end(this.#endTime);
|
|
44
|
+
else this.delegate = real;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* A {@link TracingBackend} that buffers span operations until a real backend
|
|
49
|
+
* registers. On {@link drain}, buffered spans are replayed in FIFO order with
|
|
50
|
+
* parent IDs translated from synthetic `buffered-*` traceparents to real OTEL
|
|
51
|
+
* IDs, preserving the trace hierarchy.
|
|
52
|
+
*/
|
|
53
|
+
var BufferingTracingBackend = class {
|
|
54
|
+
#pending = [];
|
|
55
|
+
#counter = 0;
|
|
56
|
+
startSpan(options) {
|
|
57
|
+
const span = new BufferedSpan(options, ++this.#counter);
|
|
58
|
+
this.#pending.push(span);
|
|
59
|
+
return span;
|
|
60
|
+
}
|
|
61
|
+
/** Discard all buffered spans without replaying them. */
|
|
62
|
+
clear() {
|
|
63
|
+
this.#pending.length = 0;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Replay all buffered spans into {@link backend}.
|
|
67
|
+
*
|
|
68
|
+
* @returns Map from synthetic buffered traceparent to real {@link TraceContextData},
|
|
69
|
+
* used by the post-drain translating wrapper to resolve stale buffered IDs
|
|
70
|
+
* still present on in-flight {@link Context} objects.
|
|
71
|
+
*/
|
|
72
|
+
drain(backend) {
|
|
73
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
74
|
+
for (const buffered of this.#pending) {
|
|
75
|
+
let parentContext = buffered.options.parentContext;
|
|
76
|
+
if (parentContext && parentContext.traceparent.startsWith("buffered-")) parentContext = idMap.get(parentContext.traceparent) ?? parentContext;
|
|
77
|
+
const real = backend.startSpan({
|
|
78
|
+
...buffered.options,
|
|
79
|
+
parentContext,
|
|
80
|
+
startTime: buffered.startTime
|
|
81
|
+
});
|
|
82
|
+
if (real.spanContext) idMap.set(buffered.spanContext.traceparent, real.spanContext);
|
|
83
|
+
buffered.replay(real);
|
|
84
|
+
}
|
|
85
|
+
this.#pending.length = 0;
|
|
86
|
+
return idMap;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/util.ts
|
|
91
|
+
var createId = () => Math.random().toString(36).slice(2);
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/diagnostic.ts
|
|
94
|
+
var __dxlog_file$1 = "/__w/dxos/dxos/packages/common/tracing/src/diagnostic.ts";
|
|
95
|
+
var DIAGNOSTICS_TIMEOUT = 1e4;
|
|
96
|
+
var TraceDiagnosticImpl = class {
|
|
97
|
+
id;
|
|
98
|
+
fetch;
|
|
99
|
+
name;
|
|
100
|
+
_onUnregister;
|
|
101
|
+
constructor(id, fetch, name, _onUnregister) {
|
|
102
|
+
this.id = id;
|
|
103
|
+
this.fetch = fetch;
|
|
104
|
+
this.name = name;
|
|
105
|
+
this._onUnregister = _onUnregister;
|
|
106
|
+
}
|
|
107
|
+
unregister() {
|
|
108
|
+
this._onUnregister();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
var DiagnosticsManager = class {
|
|
112
|
+
instanceId = createId();
|
|
113
|
+
registry = /* @__PURE__ */ new Map();
|
|
114
|
+
_instanceTag = null;
|
|
115
|
+
get instanceTag() {
|
|
116
|
+
return this._instanceTag;
|
|
117
|
+
}
|
|
118
|
+
setInstanceTag(tag) {
|
|
119
|
+
this._instanceTag = tag;
|
|
120
|
+
}
|
|
121
|
+
registerDiagnostic(params) {
|
|
122
|
+
const impl = new TraceDiagnosticImpl(params.id, params.fetch, params.name ?? params.id, () => {
|
|
123
|
+
if (this.registry.get(params.id) === impl) this.registry.delete(params.id);
|
|
124
|
+
});
|
|
125
|
+
this.registry.set(params.id, impl);
|
|
126
|
+
return impl;
|
|
127
|
+
}
|
|
128
|
+
list() {
|
|
129
|
+
return Array.from(this.registry.values()).map((diagnostic) => ({
|
|
130
|
+
id: diagnostic.id,
|
|
131
|
+
instanceId: this.instanceId,
|
|
132
|
+
instanceTag: this._instanceTag,
|
|
133
|
+
name: diagnostic.name
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
async fetch(request) {
|
|
137
|
+
if (request.instanceId != null) invariant(request.instanceId === this.instanceId, "Invalid instance id", {
|
|
138
|
+
"~LogMeta": "~LogMeta",
|
|
139
|
+
F: __dxlog_file$1,
|
|
140
|
+
L: 82,
|
|
141
|
+
S: this,
|
|
142
|
+
A: ["request.instanceId === this.instanceId", "'Invalid instance id'"]
|
|
143
|
+
});
|
|
144
|
+
const { id } = request;
|
|
145
|
+
const diagnostic = this.registry.get(id);
|
|
146
|
+
invariant(diagnostic, "Diagnostic not found", {
|
|
147
|
+
"~LogMeta": "~LogMeta",
|
|
148
|
+
F: __dxlog_file$1,
|
|
149
|
+
L: 86,
|
|
150
|
+
S: this,
|
|
151
|
+
A: ["diagnostic", "'Diagnostic not found'"]
|
|
152
|
+
});
|
|
153
|
+
try {
|
|
154
|
+
const data = await asyncTimeout(diagnostic.fetch(), DIAGNOSTICS_TIMEOUT);
|
|
155
|
+
return {
|
|
156
|
+
id,
|
|
157
|
+
instanceId: this.instanceId,
|
|
158
|
+
data
|
|
159
|
+
};
|
|
160
|
+
} catch (err) {
|
|
161
|
+
return {
|
|
162
|
+
id,
|
|
163
|
+
instanceId: this.instanceId,
|
|
164
|
+
data: null,
|
|
165
|
+
error: err.stack
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/diagnostics-channel.ts
|
|
172
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/common/tracing/src/diagnostics-channel.ts";
|
|
173
|
+
var DEFAULT_CHANNEL_NAME = "dxos-diagnostics";
|
|
174
|
+
var DISCOVER_TIME = 500;
|
|
175
|
+
var DiagnosticsChannel = class DiagnosticsChannel {
|
|
176
|
+
_channelName;
|
|
177
|
+
static get supported() {
|
|
178
|
+
return globalThis.BroadcastChannel != null;
|
|
179
|
+
}
|
|
180
|
+
_ctx = new Context(void 0, {
|
|
181
|
+
"~LogMeta": "~LogMeta",
|
|
182
|
+
F: __dxlog_file,
|
|
183
|
+
L: 46
|
|
184
|
+
});
|
|
185
|
+
_serveChannel = void 0;
|
|
186
|
+
_clientChannel = void 0;
|
|
187
|
+
constructor(_channelName = DEFAULT_CHANNEL_NAME) {
|
|
188
|
+
this._channelName = _channelName;
|
|
189
|
+
if (DiagnosticsChannel.supported) {
|
|
190
|
+
this._serveChannel = new BroadcastChannel(_channelName);
|
|
191
|
+
this._clientChannel = new BroadcastChannel(_channelName);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
destroy() {
|
|
195
|
+
this._ctx.dispose();
|
|
196
|
+
this._serveChannel?.close();
|
|
197
|
+
this._clientChannel?.close();
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* In node.js, the channel will keep the process alive.
|
|
201
|
+
* This method allows the process to exit.
|
|
202
|
+
* Noop in the browser.
|
|
203
|
+
*/
|
|
204
|
+
unref() {
|
|
205
|
+
if (this._serveChannel && typeof this._serveChannel.unref === "function") {
|
|
206
|
+
this._serveChannel.unref();
|
|
207
|
+
this._clientChannel.unref();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
serve(manager) {
|
|
211
|
+
invariant(this._serveChannel, void 0, {
|
|
212
|
+
"~LogMeta": "~LogMeta",
|
|
213
|
+
F: __dxlog_file,
|
|
214
|
+
L: 78,
|
|
215
|
+
S: this,
|
|
216
|
+
A: ["this._serveChannel", ""]
|
|
217
|
+
});
|
|
218
|
+
const listener = async (event) => {
|
|
219
|
+
switch (event.data.type) {
|
|
220
|
+
case "DIAGNOSTICS_DISCOVER": {
|
|
221
|
+
const diagnostics = manager.list();
|
|
222
|
+
this._serveChannel.postMessage({
|
|
223
|
+
type: "DIAGNOSTICS_ANNOUNCE",
|
|
224
|
+
diagnostics
|
|
225
|
+
});
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case "DIAGNOSTICS_FETCH": {
|
|
229
|
+
const { requestId, request } = event.data;
|
|
230
|
+
if (request.instanceId != null && request.instanceId !== manager.instanceId) break;
|
|
231
|
+
else if (request.instanceTag != null && request.instanceTag !== manager.instanceTag) break;
|
|
232
|
+
const data = await manager.fetch(request);
|
|
233
|
+
this._serveChannel.postMessage({
|
|
234
|
+
type: "DIAGNOSTICS_RESPONSE",
|
|
235
|
+
requestId,
|
|
236
|
+
data
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
this._serveChannel.addEventListener("message", listener);
|
|
243
|
+
this._ctx.onDispose(() => this._serveChannel.removeEventListener("message", listener));
|
|
244
|
+
}
|
|
245
|
+
async discover() {
|
|
246
|
+
invariant(this._clientChannel, void 0, {
|
|
247
|
+
"~LogMeta": "~LogMeta",
|
|
248
|
+
F: __dxlog_file,
|
|
249
|
+
L: 114,
|
|
250
|
+
S: this,
|
|
251
|
+
A: ["this._clientChannel", ""]
|
|
252
|
+
});
|
|
253
|
+
const diagnostics = [];
|
|
254
|
+
const collector = (event) => {
|
|
255
|
+
const data = event.data;
|
|
256
|
+
switch (data.type) {
|
|
257
|
+
case "DIAGNOSTICS_ANNOUNCE":
|
|
258
|
+
diagnostics.push(...data.diagnostics);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
try {
|
|
263
|
+
this._clientChannel.addEventListener("message", collector);
|
|
264
|
+
this._clientChannel.postMessage({ type: "DIAGNOSTICS_DISCOVER" });
|
|
265
|
+
await sleep(DISCOVER_TIME);
|
|
266
|
+
const result = [];
|
|
267
|
+
for (const diagnostic of diagnostics) if (!result.some((d) => d.id === diagnostic.id && d.instanceId === diagnostic.instanceId)) result.push(diagnostic);
|
|
268
|
+
return diagnostics;
|
|
269
|
+
} finally {
|
|
270
|
+
this._clientChannel.removeEventListener("message", collector);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async fetch(request) {
|
|
274
|
+
invariant(this._clientChannel, void 0, {
|
|
275
|
+
"~LogMeta": "~LogMeta",
|
|
276
|
+
F: __dxlog_file,
|
|
277
|
+
L: 147,
|
|
278
|
+
S: this,
|
|
279
|
+
A: ["this._clientChannel", ""]
|
|
280
|
+
});
|
|
281
|
+
const requestId = createId();
|
|
282
|
+
const trigger = new Trigger();
|
|
283
|
+
const listener = (event) => {
|
|
284
|
+
const data = event.data;
|
|
285
|
+
if (data.type === "DIAGNOSTICS_RESPONSE" && data.requestId === requestId) trigger.wake(data.data);
|
|
286
|
+
};
|
|
287
|
+
try {
|
|
288
|
+
this._clientChannel.addEventListener("message", listener);
|
|
289
|
+
this._clientChannel.postMessage({
|
|
290
|
+
type: "DIAGNOSTICS_FETCH",
|
|
291
|
+
requestId,
|
|
292
|
+
request
|
|
293
|
+
});
|
|
294
|
+
return await trigger.wait({ timeout: DIAGNOSTICS_TIMEOUT });
|
|
295
|
+
} finally {
|
|
296
|
+
this._clientChannel.removeEventListener("message", listener);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/remote/metrics.ts
|
|
302
|
+
/**
|
|
303
|
+
* Allows metrics to be recorded within SDK code without requiring specific consumers.
|
|
304
|
+
*/
|
|
305
|
+
var RemoteMetrics = class {
|
|
306
|
+
_metrics = /* @__PURE__ */ new Set();
|
|
307
|
+
registerProcessor(processor) {
|
|
308
|
+
this._metrics.add(processor);
|
|
309
|
+
}
|
|
310
|
+
increment(name, value, data) {
|
|
311
|
+
return Array.from(this._metrics.values()).map((processor) => processor.increment(name, value, data));
|
|
312
|
+
}
|
|
313
|
+
distribution(name, value, data) {
|
|
314
|
+
return Array.from(this._metrics.values()).map((processor) => processor.distribution(name, value, data));
|
|
315
|
+
}
|
|
316
|
+
set(name, value, data) {
|
|
317
|
+
return Array.from(this._metrics.values()).map((processor) => processor.set(name, value, data));
|
|
318
|
+
}
|
|
319
|
+
gauge(name, value, data) {
|
|
320
|
+
return Array.from(this._metrics.values()).map((processor) => processor.gauge(name, value, data));
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/trace-processor.ts
|
|
325
|
+
var TraceProcessor = class {
|
|
326
|
+
diagnostics = new DiagnosticsManager();
|
|
327
|
+
diagnosticsChannel = new DiagnosticsChannel();
|
|
328
|
+
remoteMetrics = new RemoteMetrics();
|
|
329
|
+
#bufferingBackend = new BufferingTracingBackend();
|
|
330
|
+
#activeBackend = this.#bufferingBackend;
|
|
331
|
+
/**
|
|
332
|
+
* Tracing backend. Initially a buffering backend that records spans;
|
|
333
|
+
* once the observability package sets a real backend, the buffer is drained
|
|
334
|
+
* and a thin translating wrapper is installed that resolves stale buffered
|
|
335
|
+
* parent IDs still held by in-flight {@link Context} objects.
|
|
336
|
+
*
|
|
337
|
+
* The wrapper only allocates when a `buffered-*` parent is actually encountered;
|
|
338
|
+
* the common path is a single `startsWith` check and direct passthrough.
|
|
339
|
+
*/
|
|
340
|
+
get tracingBackend() {
|
|
341
|
+
return this.#activeBackend;
|
|
342
|
+
}
|
|
343
|
+
set tracingBackend(backend) {
|
|
344
|
+
if (!backend || backend === this.#bufferingBackend) {
|
|
345
|
+
this.#bufferingBackend.clear();
|
|
346
|
+
this.#activeBackend = this.#bufferingBackend;
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const idMap = this.#bufferingBackend.drain(backend);
|
|
350
|
+
this.#activeBackend = { startSpan: (options) => {
|
|
351
|
+
const parent = options.parentContext;
|
|
352
|
+
if (parent?.traceparent.startsWith("buffered-")) {
|
|
353
|
+
const translated = idMap.get(parent.traceparent);
|
|
354
|
+
if (translated) return backend.startSpan({
|
|
355
|
+
...options,
|
|
356
|
+
parentContext: translated
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return backend.startSpan(options);
|
|
360
|
+
} };
|
|
361
|
+
}
|
|
362
|
+
_instanceTag = null;
|
|
363
|
+
constructor() {
|
|
364
|
+
if (DiagnosticsChannel.supported) this.diagnosticsChannel.serve(this.diagnostics);
|
|
365
|
+
this.diagnosticsChannel.unref();
|
|
366
|
+
}
|
|
367
|
+
setInstanceTag(tag) {
|
|
368
|
+
this._instanceTag = tag;
|
|
369
|
+
this.diagnostics.setInstanceTag(tag);
|
|
370
|
+
}
|
|
371
|
+
addLink(parent, child, opts) {}
|
|
372
|
+
};
|
|
373
|
+
var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
|
|
374
|
+
var sanitizeClassName = (className) => {
|
|
375
|
+
let name = className.replace(/^_+/, "");
|
|
376
|
+
const m = name.match(/[^_](\d+)$/);
|
|
377
|
+
if (m) name = name.slice(0, -m[1].length);
|
|
378
|
+
return name;
|
|
379
|
+
};
|
|
380
|
+
//#endregion
|
|
381
|
+
//#region src/api.ts
|
|
382
|
+
/** localStorage key that switches the browser OTEL sampler from 30% to 100%. */
|
|
383
|
+
var TRACE_ALL_KEY = "dxos.debug.traceAll";
|
|
384
|
+
var mark = (name) => {
|
|
385
|
+
performance.mark(name);
|
|
386
|
+
};
|
|
387
|
+
/**
|
|
388
|
+
* Decorator that creates a span for the execution duration of the decorated method.
|
|
389
|
+
* Calls the TracingBackend directly; no custom TracingSpan objects.
|
|
390
|
+
*/
|
|
391
|
+
var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
|
|
392
|
+
const method = descriptor.value;
|
|
393
|
+
descriptor.value = async function(...args) {
|
|
394
|
+
const parentCtx = args[0] instanceof Context ? args[0] : null;
|
|
395
|
+
const startTs = performance.now();
|
|
396
|
+
const parentSpanContext = parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
|
|
397
|
+
const spanName = `${sanitizeClassName(target.constructor?.name ?? "unknown")}.${propertyKey}`;
|
|
398
|
+
const spanAttributes = {};
|
|
399
|
+
if (attributes) for (const [key, value] of Object.entries(attributes)) spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
|
|
400
|
+
const remoteSpan = showInRemoteTracing ? TRACE_PROCESSOR.tracingBackend?.startSpan({
|
|
401
|
+
name: spanName,
|
|
402
|
+
op: op ?? "function",
|
|
403
|
+
attributes: spanAttributes,
|
|
404
|
+
parentContext: parentSpanContext
|
|
405
|
+
}) : void 0;
|
|
406
|
+
let callArgs = args;
|
|
407
|
+
if (parentCtx) callArgs = [remoteSpan?.spanContext != null ? parentCtx.derive({ attributes: { [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext } }) : parentCtx.derive(), ...args.slice(1)];
|
|
408
|
+
try {
|
|
409
|
+
return await method.apply(this, callArgs);
|
|
410
|
+
} catch (err) {
|
|
411
|
+
remoteSpan?.setError?.(err);
|
|
412
|
+
throw err;
|
|
413
|
+
} finally {
|
|
414
|
+
remoteSpan?.end();
|
|
415
|
+
if (showInBrowserTimeline && typeof globalThis?.performance?.measure === "function") performance.measure(spanName, {
|
|
416
|
+
start: startTs,
|
|
417
|
+
end: performance.now()
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
var manualSpans = /* @__PURE__ */ new Map();
|
|
423
|
+
var manualSpanTimestamps = /* @__PURE__ */ new Map();
|
|
424
|
+
/**
|
|
425
|
+
* Creates a span that must be ended manually.
|
|
426
|
+
*
|
|
427
|
+
* Returns a child Context that carries the new span's `TRACE_SPAN_ATTRIBUTE`.
|
|
428
|
+
* Callers should use the returned ctx for downstream work so that nested
|
|
429
|
+
* `@trace.span` methods and RPC calls inherit this span as their parent
|
|
430
|
+
* and land in the same trace (rather than starting a new root).
|
|
431
|
+
*
|
|
432
|
+
* When the new span cannot be created (duplicate id, no backend, or
|
|
433
|
+
* `showInRemoteTracing: false`), the parentCtx is returned unchanged.
|
|
434
|
+
*/
|
|
435
|
+
var spanStart = (params) => {
|
|
436
|
+
if (manualSpans.has(params.id) || manualSpanTimestamps.has(params.id)) return params.parentCtx;
|
|
437
|
+
const spanName = `${sanitizeClassName(params.instance?.constructor?.name ?? "unknown")}.${params.methodName}`;
|
|
438
|
+
if (params.showInBrowserTimeline) manualSpanTimestamps.set(params.id, {
|
|
439
|
+
name: spanName,
|
|
440
|
+
startTs: performance.now()
|
|
441
|
+
});
|
|
442
|
+
if (params.showInRemoteTracing === false || !TRACE_PROCESSOR.tracingBackend) return params.parentCtx;
|
|
443
|
+
const parentSpanContext = params.parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
|
|
444
|
+
const spanAttributes = {};
|
|
445
|
+
if (params.attributes) for (const [key, value] of Object.entries(params.attributes)) spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
|
|
446
|
+
const remoteSpan = TRACE_PROCESSOR.tracingBackend.startSpan({
|
|
447
|
+
name: spanName,
|
|
448
|
+
op: params.op ?? "function",
|
|
449
|
+
attributes: spanAttributes,
|
|
450
|
+
parentContext: parentSpanContext
|
|
451
|
+
});
|
|
452
|
+
manualSpans.set(params.id, remoteSpan);
|
|
453
|
+
if (params.parentCtx && remoteSpan.spanContext != null) return params.parentCtx.derive({ attributes: { [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext } });
|
|
454
|
+
return params.parentCtx;
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Ends a span that was started manually.
|
|
458
|
+
*/
|
|
459
|
+
var spanEnd = (id) => {
|
|
460
|
+
const remoteSpan = manualSpans.get(id);
|
|
461
|
+
if (remoteSpan) {
|
|
462
|
+
remoteSpan.end();
|
|
463
|
+
manualSpans.delete(id);
|
|
464
|
+
}
|
|
465
|
+
const timestamps = manualSpanTimestamps.get(id);
|
|
466
|
+
if (timestamps && typeof globalThis?.performance?.measure === "function") {
|
|
467
|
+
performance.measure(timestamps.name, {
|
|
468
|
+
start: timestamps.startTs,
|
|
469
|
+
end: performance.now()
|
|
470
|
+
});
|
|
471
|
+
manualSpanTimestamps.delete(id);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
var addLink = (parent, child, opts = {}) => {
|
|
475
|
+
TRACE_PROCESSOR.addLink(parent, child, opts);
|
|
476
|
+
};
|
|
477
|
+
/**
|
|
478
|
+
* Register a diagnostic that could be queried.
|
|
479
|
+
*/
|
|
480
|
+
var diagnostic = (params) => {
|
|
481
|
+
return TRACE_PROCESSOR.diagnostics.registerDiagnostic(params);
|
|
482
|
+
};
|
|
483
|
+
var trace = {
|
|
484
|
+
addLink,
|
|
485
|
+
diagnostic,
|
|
486
|
+
mark,
|
|
487
|
+
span,
|
|
488
|
+
spanStart,
|
|
489
|
+
spanEnd,
|
|
490
|
+
metrics: TRACE_PROCESSOR.remoteMetrics
|
|
491
|
+
};
|
|
492
|
+
//#endregion
|
|
493
|
+
//#region src/index.ts
|
|
494
|
+
trace.diagnostic({
|
|
495
|
+
id: "process-info",
|
|
496
|
+
name: "Process Info",
|
|
497
|
+
fetch: async () => {
|
|
498
|
+
return {
|
|
499
|
+
platform: globalThis.process?.platform,
|
|
500
|
+
arch: globalThis.process?.arch,
|
|
501
|
+
versions: globalThis.process?.versions,
|
|
502
|
+
href: globalThis.location?.href
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
//#endregion
|
|
507
|
+
export { DIAGNOSTICS_TIMEOUT, DiagnosticsChannel, DiagnosticsManager, RemoteMetrics, TRACE_ALL_KEY, TRACE_PROCESSOR, TraceDiagnosticImpl, TraceProcessor, sanitizeClassName, trace };
|
|
508
|
+
|
|
509
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/buffering-backend.ts","../../src/util.ts","../../src/diagnostic.ts","../../src/diagnostics-channel.ts","../../src/remote/metrics.ts","../../src/trace-processor.ts","../../src/api.ts","../../src/index.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type TraceContextData } from '@dxos/context';\n\nimport type { RemoteSpan, StartSpanOptions, TracingBackend } from './tracing-types';\n\nexport const BUFFERED_PREFIX = 'buffered-';\n\n/**\n * Span handle that records operations while no real OTEL backend is available.\n * Once a real backend is registered, the buffered span is replayed and a\n * {@link delegate} is set so future calls forward to the real span.\n */\nclass BufferedSpan implements RemoteSpan {\n readonly spanContext: TraceContextData;\n readonly startTime: number;\n delegate?: RemoteSpan;\n\n #ended = false;\n #endTime?: number;\n #error?: unknown;\n #hasError = false;\n\n constructor(\n readonly options: StartSpanOptions,\n id: number,\n ) {\n this.spanContext = { traceparent: `${BUFFERED_PREFIX}${id}` };\n this.startTime = Date.now();\n }\n\n end(endTime?: number): void {\n if (this.delegate) {\n this.delegate.end(endTime);\n return;\n }\n this.#endTime = endTime ?? Date.now();\n this.#ended = true;\n }\n\n setError(err: unknown): void {\n if (this.delegate) {\n this.delegate.setError?.(err);\n return;\n }\n this.#error = err;\n this.#hasError = true;\n }\n\n replay(real: RemoteSpan): void {\n if (this.#hasError) {\n real.setError?.(this.#error);\n }\n if (this.#ended) {\n real.end(this.#endTime);\n } else {\n this.delegate = real;\n }\n }\n}\n\n/**\n * A {@link TracingBackend} that buffers span operations until a real backend\n * registers. On {@link drain}, buffered spans are replayed in FIFO order with\n * parent IDs translated from synthetic `buffered-*` traceparents to real OTEL\n * IDs, preserving the trace hierarchy.\n */\nexport class BufferingTracingBackend implements TracingBackend {\n readonly #pending: BufferedSpan[] = [];\n #counter = 0;\n\n startSpan(options: StartSpanOptions): RemoteSpan {\n const span = new BufferedSpan(options, ++this.#counter);\n this.#pending.push(span);\n return span;\n }\n\n /** Discard all buffered spans without replaying them. */\n clear(): void {\n this.#pending.length = 0;\n }\n\n /**\n * Replay all buffered spans into {@link backend}.\n *\n * @returns Map from synthetic buffered traceparent to real {@link TraceContextData},\n * used by the post-drain translating wrapper to resolve stale buffered IDs\n * still present on in-flight {@link Context} objects.\n */\n drain(backend: TracingBackend): Map<string, TraceContextData> {\n const idMap = new Map<string, TraceContextData>();\n\n for (const buffered of this.#pending) {\n let parentContext = buffered.options.parentContext;\n if (parentContext && parentContext.traceparent.startsWith(BUFFERED_PREFIX)) {\n parentContext = idMap.get(parentContext.traceparent) ?? parentContext;\n }\n\n const real = backend.startSpan({ ...buffered.options, parentContext, startTime: buffered.startTime });\n\n if (real.spanContext) {\n idMap.set(buffered.spanContext.traceparent, real.spanContext);\n }\n\n buffered.replay(real);\n }\n this.#pending.length = 0;\n return idMap;\n }\n}\n","// TODO(dmaretskyi): Use UUID.\n//\n// Copyright 2024 DXOS.org\n//\n\nexport const createId = () => Math.random().toString(36).slice(2);\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { asyncTimeout } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\n\nimport { type TraceDiagnostic, type TraceDiagnosticProps } from './api';\nimport { createId } from './util';\n\nexport const DIAGNOSTICS_TIMEOUT = 10_000;\n\nexport type DiagnosticMetadata = {\n id: string;\n instanceId: string;\n instanceTag: string | null;\n name: string;\n};\n\nexport type DiagnosticsRequest = {\n id: string;\n instanceId?: string;\n instanceTag?: string | null;\n};\n\nexport type DiagnosticsData = {\n id: string;\n instanceId: string;\n data: any;\n error?: string;\n};\n\nexport class TraceDiagnosticImpl implements TraceDiagnostic {\n constructor(\n public id: string,\n public fetch: () => any,\n public name: string,\n private readonly _onUnregister: () => void,\n ) {}\n\n unregister(): void {\n this._onUnregister();\n }\n}\n\nexport class DiagnosticsManager {\n readonly instanceId = createId();\n\n readonly registry = new Map<string, TraceDiagnosticImpl>();\n\n private _instanceTag: string | null = null;\n\n get instanceTag(): string | null {\n return this._instanceTag;\n }\n\n setInstanceTag(tag: string): void {\n this._instanceTag = tag;\n }\n\n registerDiagnostic(params: TraceDiagnosticProps<any>): TraceDiagnostic {\n const impl = new TraceDiagnosticImpl(params.id, params.fetch, params.name ?? params.id, () => {\n if (this.registry.get(params.id) === impl) {\n this.registry.delete(params.id);\n }\n });\n this.registry.set(params.id, impl);\n return impl;\n }\n\n list(): DiagnosticMetadata[] {\n return Array.from(this.registry.values()).map((diagnostic) => ({\n id: diagnostic.id,\n instanceId: this.instanceId,\n instanceTag: this._instanceTag,\n name: diagnostic.name,\n }));\n }\n\n async fetch(request: DiagnosticsRequest): Promise<DiagnosticsData> {\n if (request.instanceId != null) {\n invariant(request.instanceId === this.instanceId, 'Invalid instance id');\n }\n const { id } = request;\n const diagnostic = this.registry.get(id);\n invariant(diagnostic, 'Diagnostic not found');\n try {\n const data = await asyncTimeout(diagnostic.fetch(), DIAGNOSTICS_TIMEOUT);\n return {\n id,\n instanceId: this.instanceId,\n data,\n };\n } catch (err: any) {\n return {\n id,\n instanceId: this.instanceId,\n data: null,\n error: err.stack,\n };\n }\n }\n}\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { Trigger, sleep } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { invariant } from '@dxos/invariant';\n\nimport {\n type DiagnosticMetadata,\n DIAGNOSTICS_TIMEOUT,\n type DiagnosticsData,\n type DiagnosticsManager,\n type DiagnosticsRequest,\n} from './diagnostic';\nimport { createId } from './util';\n\nconst DEFAULT_CHANNEL_NAME = 'dxos-diagnostics';\n\nconst DISCOVER_TIME = 500;\n\nexport type DiagnosticChannelMessage =\n | {\n type: 'DIAGNOSTICS_DISCOVER';\n }\n | {\n type: 'DIAGNOSTICS_ANNOUNCE';\n diagnostics: DiagnosticMetadata[];\n }\n | {\n type: 'DIAGNOSTICS_FETCH';\n requestId: string;\n request: DiagnosticsRequest;\n }\n | {\n type: 'DIAGNOSTICS_RESPONSE';\n requestId: string;\n data: DiagnosticsData;\n };\n\nexport class DiagnosticsChannel {\n static get supported() {\n return globalThis.BroadcastChannel != null;\n }\n\n private _ctx = new Context();\n\n // Separate channels becauase the client and server may be in the same process.\n private readonly _serveChannel?: BroadcastChannel = undefined;\n private readonly _clientChannel?: BroadcastChannel = undefined;\n\n constructor(private readonly _channelName: string = DEFAULT_CHANNEL_NAME) {\n if (DiagnosticsChannel.supported) {\n this._serveChannel = new BroadcastChannel(_channelName);\n this._clientChannel = new BroadcastChannel(_channelName);\n }\n }\n\n destroy(): void {\n void this._ctx.dispose();\n this._serveChannel?.close();\n this._clientChannel?.close();\n }\n\n /**\n * In node.js, the channel will keep the process alive.\n * This method allows the process to exit.\n * Noop in the browser.\n */\n unref(): void {\n if (this._serveChannel && typeof (this._serveChannel as any).unref === 'function') {\n (this._serveChannel as any).unref();\n (this._clientChannel as any).unref();\n }\n }\n\n serve(manager: DiagnosticsManager): void {\n invariant(this._serveChannel);\n const listener = async (event: MessageEvent) => {\n switch (event.data.type) {\n case 'DIAGNOSTICS_DISCOVER': {\n const diagnostics = manager.list();\n this._serveChannel!.postMessage({\n type: 'DIAGNOSTICS_ANNOUNCE',\n diagnostics,\n } satisfies DiagnosticChannelMessage);\n break;\n }\n case 'DIAGNOSTICS_FETCH': {\n const { requestId, request } = event.data;\n\n if (request.instanceId != null && request.instanceId !== manager.instanceId) {\n break;\n } else if (request.instanceTag != null && request.instanceTag !== manager.instanceTag) {\n break;\n }\n\n const data = await manager.fetch(request);\n this._serveChannel!.postMessage({\n type: 'DIAGNOSTICS_RESPONSE',\n requestId,\n data,\n } satisfies DiagnosticChannelMessage);\n break;\n }\n }\n };\n\n this._serveChannel.addEventListener('message', listener);\n this._ctx.onDispose(() => this._serveChannel!.removeEventListener('message', listener));\n }\n\n async discover(): Promise<DiagnosticMetadata[]> {\n invariant(this._clientChannel);\n const diagnostics: DiagnosticMetadata[] = [];\n\n const collector = (event: MessageEvent) => {\n const data = event.data as DiagnosticChannelMessage;\n switch (data.type) {\n case 'DIAGNOSTICS_ANNOUNCE':\n diagnostics.push(...data.diagnostics);\n break;\n }\n };\n\n try {\n this._clientChannel.addEventListener('message', collector);\n this._clientChannel.postMessage({ type: 'DIAGNOSTICS_DISCOVER' } satisfies DiagnosticChannelMessage);\n\n await sleep(DISCOVER_TIME);\n\n // Dedup.\n const result: DiagnosticMetadata[] = [];\n for (const diagnostic of diagnostics) {\n if (!result.some((d) => d.id === diagnostic.id && d.instanceId === diagnostic.instanceId)) {\n result.push(diagnostic);\n }\n }\n\n return diagnostics;\n } finally {\n this._clientChannel.removeEventListener('message', collector);\n }\n }\n\n async fetch(request: DiagnosticsRequest): Promise<DiagnosticsData> {\n invariant(this._clientChannel);\n const requestId = createId();\n\n const trigger = new Trigger<DiagnosticsData>();\n const listener = (event: MessageEvent) => {\n const data = event.data as DiagnosticChannelMessage;\n if (data.type === 'DIAGNOSTICS_RESPONSE' && data.requestId === requestId) {\n trigger.wake(data.data);\n }\n };\n\n try {\n this._clientChannel.addEventListener('message', listener);\n this._clientChannel.postMessage({\n type: 'DIAGNOSTICS_FETCH',\n requestId,\n request,\n } satisfies DiagnosticChannelMessage);\n\n // NOTE: Must have await keyword in this block.\n const result = await trigger.wait({ timeout: DIAGNOSTICS_TIMEOUT });\n\n return result;\n } finally {\n this._clientChannel.removeEventListener('message', listener);\n }\n }\n}\n","//\n// Copyright 2024 DXOS.org\n//\n\ntype TagType = number | string | boolean | null | undefined;\n\nexport interface MetricData {\n unit?: string;\n tags?: Record<string, TagType>;\n timestamp?: number;\n}\n\ninterface MetricsMethods {\n /**\n * Adds a value to a counter metric\n */\n increment(name: string, value?: number, data?: MetricData): void;\n /**\n * Adds a value to a distribution metric\n */\n distribution(name: string, value: number, data?: MetricData): void;\n /**\n * Adds a value to a set metric. Value must be a string or integer.\n */\n set(name: string, value: number | string, data?: MetricData): void;\n /**\n * Adds a value to a gauge metric\n */\n gauge(name: string, value: number, data?: MetricData): void;\n}\n\n/**\n * Allows metrics to be recorded within SDK code without requiring specific consumers.\n */\nexport class RemoteMetrics implements MetricsMethods {\n private _metrics = new Set<MetricsMethods>();\n\n registerProcessor(processor: MetricsMethods): void {\n this._metrics.add(processor);\n }\n\n increment(name: string, value?: number, data?: MetricData): void[] {\n return Array.from(this._metrics.values()).map((processor) => processor.increment(name, value, data));\n }\n\n distribution(name: string, value: number, data?: MetricData): void[] {\n return Array.from(this._metrics.values()).map((processor) => processor.distribution(name, value, data));\n }\n\n set(name: string, value: number | string, data?: MetricData): void[] {\n return Array.from(this._metrics.values()).map((processor) => processor.set(name, value, data));\n }\n\n gauge(name: string, value: number, data?: MetricData): void[] {\n return Array.from(this._metrics.values()).map((processor) => processor.gauge(name, value, data));\n }\n}\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport type { AddLinkOptions } from './api';\nimport { BUFFERED_PREFIX, BufferingTracingBackend } from './buffering-backend';\nimport { DiagnosticsManager } from './diagnostic';\nimport { DiagnosticsChannel } from './diagnostics-channel';\nimport { RemoteMetrics } from './remote/metrics';\nimport type { RemoteSpan, StartSpanOptions, TracingBackend } from './tracing-types';\n\nexport class TraceProcessor {\n public readonly diagnostics = new DiagnosticsManager();\n public readonly diagnosticsChannel = new DiagnosticsChannel();\n public readonly remoteMetrics = new RemoteMetrics();\n\n readonly #bufferingBackend = new BufferingTracingBackend();\n #activeBackend: TracingBackend = this.#bufferingBackend;\n\n /**\n * Tracing backend. Initially a buffering backend that records spans;\n * once the observability package sets a real backend, the buffer is drained\n * and a thin translating wrapper is installed that resolves stale buffered\n * parent IDs still held by in-flight {@link Context} objects.\n *\n * The wrapper only allocates when a `buffered-*` parent is actually encountered;\n * the common path is a single `startsWith` check and direct passthrough.\n */\n get tracingBackend(): TracingBackend {\n return this.#activeBackend;\n }\n\n set tracingBackend(backend: TracingBackend | undefined) {\n if (!backend || backend === this.#bufferingBackend) {\n this.#bufferingBackend.clear();\n this.#activeBackend = this.#bufferingBackend;\n return;\n }\n const idMap = this.#bufferingBackend.drain(backend);\n this.#activeBackend = {\n startSpan: (options: StartSpanOptions): RemoteSpan => {\n const parent = options.parentContext;\n if (parent?.traceparent.startsWith(BUFFERED_PREFIX)) {\n const translated = idMap.get(parent.traceparent);\n if (translated) {\n return backend.startSpan({ ...options, parentContext: translated });\n }\n }\n return backend.startSpan(options);\n },\n };\n }\n\n private _instanceTag: string | null = null;\n\n constructor() {\n if (DiagnosticsChannel.supported) {\n this.diagnosticsChannel.serve(this.diagnostics);\n }\n this.diagnosticsChannel.unref();\n }\n\n setInstanceTag(tag: string): void {\n this._instanceTag = tag;\n this.diagnostics.setInstanceTag(tag);\n }\n\n // TODO(burdon): Not implemented.\n addLink(parent: any, child: any, opts: AddLinkOptions): void {}\n}\n\nexport const TRACE_PROCESSOR: TraceProcessor = ((globalThis as any).TRACE_PROCESSOR ??= new TraceProcessor());\n\nexport const sanitizeClassName = (className: string) => {\n let name = className.replace(/^_+/, '');\n const SANITIZE_REGEX = /[^_](\\d+)$/;\n const m = name.match(SANITIZE_REGEX);\n if (m) {\n name = name.slice(0, -m[1].length);\n }\n return name;\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { Context, TRACE_SPAN_ATTRIBUTE } from '@dxos/context';\nimport { type MaybePromise } from '@dxos/util';\n\nimport { TRACE_PROCESSOR, sanitizeClassName } from './trace-processor';\nimport type { RemoteSpan } from './tracing-types';\n\n/** localStorage key that switches the browser OTEL sampler from 30% to 100%. */\nexport const TRACE_ALL_KEY = 'dxos.debug.traceAll';\n\nconst mark = (name: string) => {\n performance.mark(name);\n};\n\nexport type SpanOptions = {\n showInBrowserTimeline?: boolean;\n /** When false the span is not exported to remote OTLP collectors. Defaults to true. */\n showInRemoteTracing?: boolean;\n op?: string;\n attributes?: Record<string, any>;\n};\n\n/**\n * Decorator that creates a span for the execution duration of the decorated method.\n * Calls the TracingBackend directly; no custom TracingSpan objects.\n */\nconst span =\n ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes }: SpanOptions = {}) =>\n (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<(...args: any) => any>) => {\n const method = descriptor.value!;\n\n descriptor.value = async function (this: any, ...args: any) {\n const parentCtx = args[0] instanceof Context ? args[0] : null;\n const startTs = performance.now();\n\n const parentSpanContext = parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);\n\n const className = sanitizeClassName(target.constructor?.name ?? 'unknown');\n const spanName = `${className}.${propertyKey}`;\n\n const spanAttributes: Record<string, any> = {};\n if (attributes) {\n for (const [key, value] of Object.entries(attributes)) {\n spanAttributes[key.startsWith('ctx.') ? key : `ctx.${key}`] = value;\n }\n }\n\n const remoteSpan = showInRemoteTracing\n ? TRACE_PROCESSOR.tracingBackend?.startSpan({\n name: spanName,\n op: op ?? 'function',\n attributes: spanAttributes,\n parentContext: parentSpanContext,\n })\n : undefined;\n\n let callArgs = args;\n if (parentCtx) {\n const childCtx =\n remoteSpan?.spanContext != null\n ? parentCtx.derive({ attributes: { [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext } })\n : parentCtx.derive();\n callArgs = [childCtx, ...args.slice(1)];\n }\n\n try {\n return await method.apply(this, callArgs);\n } catch (err) {\n remoteSpan?.setError?.(err);\n throw err;\n } finally {\n remoteSpan?.end();\n if (showInBrowserTimeline && typeof globalThis?.performance?.measure === 'function') {\n performance.measure(spanName, { start: startTs, end: performance.now() });\n }\n }\n };\n };\n\nconst manualSpans = new Map<string, RemoteSpan>();\nconst manualSpanTimestamps = new Map<string, { name: string; startTs: number }>();\n\nexport type ManualSpanParams = {\n id: string;\n instance: any;\n methodName: string;\n parentCtx: Context | null;\n showInBrowserTimeline?: boolean;\n showInRemoteTracing?: boolean;\n op?: string;\n attributes?: Record<string, any>;\n};\n\n/**\n * Creates a span that must be ended manually.\n *\n * Returns a child Context that carries the new span's `TRACE_SPAN_ATTRIBUTE`.\n * Callers should use the returned ctx for downstream work so that nested\n * `@trace.span` methods and RPC calls inherit this span as their parent\n * and land in the same trace (rather than starting a new root).\n *\n * When the new span cannot be created (duplicate id, no backend, or\n * `showInRemoteTracing: false`), the parentCtx is returned unchanged.\n */\nconst spanStart = (params: ManualSpanParams): Context | null => {\n if (manualSpans.has(params.id) || manualSpanTimestamps.has(params.id)) {\n return params.parentCtx;\n }\n\n const className = sanitizeClassName(params.instance?.constructor?.name ?? 'unknown');\n const spanName = `${className}.${params.methodName}`;\n\n if (params.showInBrowserTimeline) {\n manualSpanTimestamps.set(params.id, { name: spanName, startTs: performance.now() });\n }\n\n if (params.showInRemoteTracing === false || !TRACE_PROCESSOR.tracingBackend) {\n return params.parentCtx;\n }\n\n const parentSpanContext = params.parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);\n\n const spanAttributes: Record<string, any> = {};\n if (params.attributes) {\n for (const [key, value] of Object.entries(params.attributes)) {\n spanAttributes[key.startsWith('ctx.') ? key : `ctx.${key}`] = value;\n }\n }\n\n const remoteSpan = TRACE_PROCESSOR.tracingBackend.startSpan({\n name: spanName,\n op: params.op ?? 'function',\n attributes: spanAttributes,\n parentContext: parentSpanContext,\n });\n manualSpans.set(params.id, remoteSpan);\n\n if (params.parentCtx && remoteSpan.spanContext != null) {\n return params.parentCtx.derive({ attributes: { [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext } });\n }\n return params.parentCtx;\n};\n\n/**\n * Ends a span that was started manually.\n */\nconst spanEnd = (id: string) => {\n const remoteSpan = manualSpans.get(id);\n if (remoteSpan) {\n remoteSpan.end();\n manualSpans.delete(id);\n }\n\n const timestamps = manualSpanTimestamps.get(id);\n if (timestamps && typeof globalThis?.performance?.measure === 'function') {\n performance.measure(timestamps.name, { start: timestamps.startTs, end: performance.now() });\n manualSpanTimestamps.delete(id);\n }\n};\n\nexport type AddLinkOptions = {};\n\nconst addLink = (parent: any, child: any, opts: AddLinkOptions = {}) => {\n TRACE_PROCESSOR.addLink(parent, child, opts);\n};\n\nexport type TraceDiagnosticProps<T> = {\n /** Unique ID. */\n id: string;\n\n /**\n * Human-readable name.\n * @defaults Defaults to `id`\n */\n name?: string;\n\n /** Function that will be called to fetch the diagnostic data. */\n fetch: () => MaybePromise<T>;\n};\n\nexport interface TraceDiagnostic {\n id: string;\n unregister(): void;\n}\n\n/**\n * Register a diagnostic that could be queried.\n */\nconst diagnostic = <T>(params: TraceDiagnosticProps<T>): TraceDiagnostic => {\n return TRACE_PROCESSOR.diagnostics.registerDiagnostic(params);\n};\n\nexport const trace = {\n addLink,\n diagnostic,\n mark,\n span,\n spanStart,\n spanEnd,\n metrics: TRACE_PROCESSOR.remoteMetrics,\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { trace } from './api';\n\nexport * from './api';\nexport * from './trace-processor';\nexport * from './tracing-types';\nexport * from './diagnostic';\nexport * from './diagnostics-channel';\nexport * from './remote/metrics';\n\ntrace.diagnostic({\n id: 'process-info',\n name: 'Process Info',\n fetch: async () => {\n return {\n platform: globalThis.process?.platform,\n arch: globalThis.process?.arch,\n versions: globalThis.process?.versions,\n href: globalThis.location?.href,\n };\n },\n});\n"],"mappings":"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"}
|