@dxos/tracing 0.8.4-main.f9ba587 → 0.8.4-main.fcc0d83b33

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.
Files changed (47) hide show
  1. package/dist/lib/browser/index.mjs +395 -435
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +395 -435
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/api.d.ts +36 -17
  8. package/dist/types/src/api.d.ts.map +1 -1
  9. package/dist/types/src/buffering-backend.d.ts +24 -0
  10. package/dist/types/src/buffering-backend.d.ts.map +1 -0
  11. package/dist/types/src/diagnostic.d.ts +2 -2
  12. package/dist/types/src/diagnostic.d.ts.map +1 -1
  13. package/dist/types/src/diagnostics-channel.d.ts.map +1 -1
  14. package/dist/types/src/index.d.ts +1 -2
  15. package/dist/types/src/index.d.ts.map +1 -1
  16. package/dist/types/src/metrics/base.d.ts.map +1 -1
  17. package/dist/types/src/metrics/custom-counter.d.ts.map +1 -1
  18. package/dist/types/src/metrics/map-counter.d.ts.map +1 -1
  19. package/dist/types/src/metrics/time-series-counter.d.ts.map +1 -1
  20. package/dist/types/src/metrics/time-usage-counter.d.ts.map +1 -1
  21. package/dist/types/src/metrics/unary-counter.d.ts.map +1 -1
  22. package/dist/types/src/remote/index.d.ts +0 -1
  23. package/dist/types/src/remote/index.d.ts.map +1 -1
  24. package/dist/types/src/remote/metrics.d.ts.map +1 -1
  25. package/dist/types/src/symbols.d.ts +0 -1
  26. package/dist/types/src/symbols.d.ts.map +1 -1
  27. package/dist/types/src/trace-processor.d.ts +16 -52
  28. package/dist/types/src/trace-processor.d.ts.map +1 -1
  29. package/dist/types/src/tracing-types.d.ts +67 -0
  30. package/dist/types/src/tracing-types.d.ts.map +1 -0
  31. package/dist/types/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +15 -14
  33. package/src/api.ts +237 -35
  34. package/src/buffering-backend.ts +112 -0
  35. package/src/diagnostic.ts +2 -2
  36. package/src/index.ts +1 -2
  37. package/src/remote/index.ts +0 -1
  38. package/src/symbols.ts +0 -2
  39. package/src/trace-processor.ts +59 -259
  40. package/src/tracing-types.ts +77 -0
  41. package/src/tracing.test.ts +513 -4
  42. package/dist/types/src/remote/tracing.d.ts +0 -23
  43. package/dist/types/src/remote/tracing.d.ts.map +0 -1
  44. package/dist/types/src/trace-sender.d.ts +0 -9
  45. package/dist/types/src/trace-sender.d.ts.map +0 -1
  46. package/src/remote/tracing.ts +0 -53
  47. package/src/trace-sender.ts +0 -88
@@ -1,21 +1,105 @@
1
1
  // src/api.ts
2
- import { Context as Context2 } from "@dxos/context";
2
+ import { Context as Context2, LifecycleState, Resource, TRACE_SPAN_ATTRIBUTE } from "@dxos/context";
3
3
 
4
4
  // src/symbols.ts
5
- var symbolTracingContext = Symbol("dxos.tracing.context");
5
+ var symbolTracingContext = /* @__PURE__ */ Symbol("dxos.tracing.context");
6
6
  var getTracingContext = (target) => {
7
7
  return target[symbolTracingContext] ??= {
8
8
  infoProperties: {},
9
9
  metricsProperties: {}
10
10
  };
11
11
  };
12
- var TRACE_SPAN_ATTRIBUTE = "dxos.trace-span";
13
12
 
14
13
  // src/trace-processor.ts
15
- import { unrefTimeout } from "@dxos/async";
16
- import { LogLevel, getContextFromEntry, log } from "@dxos/log";
14
+ import { LogLevel, log } from "@dxos/log";
17
15
  import { getPrototypeSpecificInstanceId } from "@dxos/util";
18
16
 
17
+ // src/buffering-backend.ts
18
+ var BUFFERED_PREFIX = "buffered-";
19
+ var BufferedSpan = class {
20
+ options;
21
+ spanContext;
22
+ startTime;
23
+ delegate;
24
+ #ended = false;
25
+ #endTime;
26
+ #error;
27
+ #hasError = false;
28
+ constructor(options, id) {
29
+ this.options = options;
30
+ this.spanContext = {
31
+ traceparent: `${BUFFERED_PREFIX}${id}`
32
+ };
33
+ this.startTime = Date.now();
34
+ }
35
+ end(endTime) {
36
+ if (this.delegate) {
37
+ this.delegate.end(endTime);
38
+ return;
39
+ }
40
+ this.#endTime = endTime ?? Date.now();
41
+ this.#ended = true;
42
+ }
43
+ setError(err) {
44
+ if (this.delegate) {
45
+ this.delegate.setError?.(err);
46
+ return;
47
+ }
48
+ this.#error = err;
49
+ this.#hasError = true;
50
+ }
51
+ replay(real) {
52
+ if (this.#hasError) {
53
+ real.setError?.(this.#error);
54
+ }
55
+ if (this.#ended) {
56
+ real.end(this.#endTime);
57
+ } else {
58
+ this.delegate = real;
59
+ }
60
+ }
61
+ };
62
+ var BufferingTracingBackend = class {
63
+ #pending = [];
64
+ #counter = 0;
65
+ startSpan(options) {
66
+ const span2 = new BufferedSpan(options, ++this.#counter);
67
+ this.#pending.push(span2);
68
+ return span2;
69
+ }
70
+ /** Discard all buffered spans without replaying them. */
71
+ clear() {
72
+ this.#pending.length = 0;
73
+ }
74
+ /**
75
+ * Replay all buffered spans into {@link backend}.
76
+ *
77
+ * @returns Map from synthetic buffered traceparent to real {@link TraceContextData},
78
+ * used by the post-drain translating wrapper to resolve stale buffered IDs
79
+ * still present on in-flight {@link Context} objects.
80
+ */
81
+ drain(backend) {
82
+ const idMap = /* @__PURE__ */ new Map();
83
+ for (const buffered of this.#pending) {
84
+ let parentContext = buffered.options.parentContext;
85
+ if (parentContext && parentContext.traceparent.startsWith(BUFFERED_PREFIX)) {
86
+ parentContext = idMap.get(parentContext.traceparent) ?? parentContext;
87
+ }
88
+ const real = backend.startSpan({
89
+ ...buffered.options,
90
+ parentContext,
91
+ startTime: buffered.startTime
92
+ });
93
+ if (real.spanContext) {
94
+ idMap.set(buffered.spanContext.traceparent, real.spanContext);
95
+ }
96
+ buffered.replay(real);
97
+ }
98
+ this.#pending.length = 0;
99
+ return idMap;
100
+ }
101
+ };
102
+
19
103
  // src/diagnostic.ts
20
104
  import { asyncTimeout } from "@dxos/async";
21
105
  import { invariant } from "@dxos/invariant";
@@ -27,6 +111,10 @@ var createId = () => Math.random().toString(36).slice(2);
27
111
  var __dxlog_file = "/__w/dxos/dxos/packages/common/tracing/src/diagnostic.ts";
28
112
  var DIAGNOSTICS_TIMEOUT = 1e4;
29
113
  var TraceDiagnosticImpl = class {
114
+ id;
115
+ fetch;
116
+ name;
117
+ _onUnregister;
30
118
  constructor(id, fetch, name, _onUnregister) {
31
119
  this.id = id;
32
120
  this.fetch = fetch;
@@ -38,11 +126,9 @@ var TraceDiagnosticImpl = class {
38
126
  }
39
127
  };
40
128
  var DiagnosticsManager = class {
41
- constructor() {
42
- this.instanceId = createId();
43
- this.registry = /* @__PURE__ */ new Map();
44
- this._instanceTag = null;
45
- }
129
+ instanceId = createId();
130
+ registry = /* @__PURE__ */ new Map();
131
+ _instanceTag = null;
46
132
  get instanceTag() {
47
133
  return this._instanceTag;
48
134
  }
@@ -68,27 +154,11 @@ var DiagnosticsManager = class {
68
154
  }
69
155
  async fetch(request) {
70
156
  if (request.instanceId != null) {
71
- invariant(request.instanceId === this.instanceId, "Invalid instance id", {
72
- F: __dxlog_file,
73
- L: 82,
74
- S: this,
75
- A: [
76
- "request.instanceId === this.instanceId",
77
- "'Invalid instance id'"
78
- ]
79
- });
157
+ invariant(request.instanceId === this.instanceId, "Invalid instance id", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 52, S: this, A: ["request.instanceId === this.instanceId", "'Invalid instance id'"] });
80
158
  }
81
159
  const { id } = request;
82
160
  const diagnostic2 = this.registry.get(id);
83
- invariant(diagnostic2, "Diagnostic not found", {
84
- F: __dxlog_file,
85
- L: 86,
86
- S: this,
87
- A: [
88
- "diagnostic",
89
- "'Diagnostic not found'"
90
- ]
91
- });
161
+ invariant(diagnostic2, "Diagnostic not found", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 56, S: this, A: ["diagnostic", "'Diagnostic not found'"] });
92
162
  try {
93
163
  const data = await asyncTimeout(diagnostic2.fetch(), DIAGNOSTICS_TIMEOUT);
94
164
  return {
@@ -115,17 +185,16 @@ var __dxlog_file2 = "/__w/dxos/dxos/packages/common/tracing/src/diagnostics-chan
115
185
  var DEFAULT_CHANNEL_NAME = "dxos-diagnostics";
116
186
  var DISCOVER_TIME = 500;
117
187
  var DiagnosticsChannel = class _DiagnosticsChannel {
188
+ _channelName;
118
189
  static get supported() {
119
190
  return globalThis.BroadcastChannel != null;
120
191
  }
192
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 16 });
193
+ // Separate channels becauase the client and server may be in the same process.
194
+ _serveChannel = void 0;
195
+ _clientChannel = void 0;
121
196
  constructor(_channelName = DEFAULT_CHANNEL_NAME) {
122
197
  this._channelName = _channelName;
123
- this._ctx = new Context(void 0, {
124
- F: __dxlog_file2,
125
- L: 46
126
- });
127
- this._serveChannel = void 0;
128
- this._clientChannel = void 0;
129
198
  if (_DiagnosticsChannel.supported) {
130
199
  this._serveChannel = new BroadcastChannel(_channelName);
131
200
  this._clientChannel = new BroadcastChannel(_channelName);
@@ -148,15 +217,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
148
217
  }
149
218
  }
150
219
  serve(manager) {
151
- invariant2(this._serveChannel, void 0, {
152
- F: __dxlog_file2,
153
- L: 78,
154
- S: this,
155
- A: [
156
- "this._serveChannel",
157
- ""
158
- ]
159
- });
220
+ invariant2(this._serveChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 43, S: this, A: ["this._serveChannel", ""] });
160
221
  const listener = async (event) => {
161
222
  switch (event.data.type) {
162
223
  case "DIAGNOSTICS_DISCOVER": {
@@ -188,15 +249,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
188
249
  this._ctx.onDispose(() => this._serveChannel.removeEventListener("message", listener));
189
250
  }
190
251
  async discover() {
191
- invariant2(this._clientChannel, void 0, {
192
- F: __dxlog_file2,
193
- L: 114,
194
- S: this,
195
- A: [
196
- "this._clientChannel",
197
- ""
198
- ]
199
- });
252
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 77, S: this, A: ["this._clientChannel", ""] });
200
253
  const diagnostics = [];
201
254
  const collector = (event) => {
202
255
  const data = event.data;
@@ -224,15 +277,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
224
277
  }
225
278
  }
226
279
  async fetch(request) {
227
- invariant2(this._clientChannel, void 0, {
228
- F: __dxlog_file2,
229
- L: 147,
230
- S: this,
231
- A: [
232
- "this._clientChannel",
233
- ""
234
- ]
235
- });
280
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 106, S: this, A: ["this._clientChannel", ""] });
236
281
  const requestId = createId();
237
282
  const trigger = new Trigger();
238
283
  const listener = (event) => {
@@ -260,9 +305,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
260
305
 
261
306
  // src/remote/metrics.ts
262
307
  var RemoteMetrics = class {
263
- constructor() {
264
- this._metrics = /* @__PURE__ */ new Set();
265
- }
308
+ _metrics = /* @__PURE__ */ new Set();
266
309
  registerProcessor(processor) {
267
310
  this._metrics.add(processor);
268
311
  }
@@ -280,124 +323,6 @@ var RemoteMetrics = class {
280
323
  }
281
324
  };
282
325
 
283
- // src/remote/tracing.ts
284
- var RemoteTracing = class {
285
- constructor() {
286
- this._spanMap = /* @__PURE__ */ new Map();
287
- }
288
- registerProcessor(processor) {
289
- this._tracing = processor;
290
- }
291
- flushSpan(span2) {
292
- if (!this._tracing) {
293
- return;
294
- }
295
- if (!span2.endTs) {
296
- const remoteSpan = this._tracing.startSpan({
297
- name: span2.methodName,
298
- op: span2.op ?? "function",
299
- attributes: span2.attributes
300
- });
301
- this._spanMap.set(span2, remoteSpan);
302
- } else {
303
- const remoteSpan = this._spanMap.get(span2);
304
- if (remoteSpan) {
305
- remoteSpan.end();
306
- this._spanMap.delete(span2);
307
- }
308
- }
309
- }
310
- };
311
-
312
- // src/trace-sender.ts
313
- import { Stream } from "@dxos/codec-protobuf/stream";
314
- var TraceSender = class {
315
- constructor(_traceProcessor) {
316
- this._traceProcessor = _traceProcessor;
317
- }
318
- streamTrace(request) {
319
- return new Stream(({ ctx, next }) => {
320
- const flushEvents = (resources, spans2, logs) => {
321
- const event = {
322
- resourceAdded: [],
323
- resourceRemoved: [],
324
- spanAdded: [],
325
- logAdded: []
326
- };
327
- if (resources) {
328
- for (const id of resources) {
329
- const entry = this._traceProcessor.resources.get(id);
330
- if (entry) {
331
- event.resourceAdded.push({
332
- resource: entry.data
333
- });
334
- } else {
335
- event.resourceRemoved.push({
336
- id
337
- });
338
- }
339
- }
340
- } else {
341
- for (const entry of this._traceProcessor.resources.values()) {
342
- event.resourceAdded.push({
343
- resource: entry.data
344
- });
345
- }
346
- }
347
- if (spans2) {
348
- for (const id of spans2) {
349
- const span2 = this._traceProcessor.spans.get(id);
350
- if (span2) {
351
- event.spanAdded.push({
352
- span: span2
353
- });
354
- }
355
- }
356
- } else {
357
- for (const span2 of this._traceProcessor.spans.values()) {
358
- event.spanAdded.push({
359
- span: span2
360
- });
361
- }
362
- }
363
- if (logs) {
364
- for (const log2 of logs) {
365
- event.logAdded.push({
366
- log: log2
367
- });
368
- }
369
- } else {
370
- for (const log2 of this._traceProcessor.logs) {
371
- event.logAdded.push({
372
- log: log2
373
- });
374
- }
375
- }
376
- if (event.resourceAdded.length > 0 || event.resourceRemoved.length > 0 || event.spanAdded.length > 0) {
377
- next(event);
378
- }
379
- };
380
- const flush = () => {
381
- flushEvents(subscription.dirtyResources, subscription.dirtySpans, subscription.newLogs);
382
- subscription.dirtyResources.clear();
383
- subscription.dirtySpans.clear();
384
- subscription.newLogs.length = 0;
385
- };
386
- const subscription = {
387
- flush,
388
- dirtyResources: /* @__PURE__ */ new Set(),
389
- dirtySpans: /* @__PURE__ */ new Set(),
390
- newLogs: []
391
- };
392
- this._traceProcessor.subscriptions.add(subscription);
393
- ctx.onDispose(() => {
394
- this._traceProcessor.subscriptions.delete(subscription);
395
- });
396
- flushEvents(null, null, null);
397
- });
398
- }
399
- };
400
-
401
326
  // src/weak-ref.ts
402
327
  var WeakRefMock = class {
403
328
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -412,6 +337,15 @@ var WeakRef = globalThis.WeakRef ?? WeakRefMock;
412
337
  // src/trace-processor.ts
413
338
  var __dxlog_file3 = "/__w/dxos/dxos/packages/common/tracing/src/trace-processor.ts";
414
339
  var ResourceEntry = class {
340
+ data;
341
+ instance;
342
+ annotation;
343
+ /**
344
+ * Sometimes bundlers mangle class names: WebFile -> WebFile2.
345
+ *
346
+ * We use a heuristic to remove the suffix.
347
+ */
348
+ sanitizedClassName;
415
349
  constructor(data, instance, annotation) {
416
350
  this.data = data;
417
351
  this.instance = instance;
@@ -423,66 +357,56 @@ var ResourceEntry = class {
423
357
  }
424
358
  };
425
359
  var MAX_RESOURCE_RECORDS = 2e3;
426
- var MAX_SPAN_RECORDS = 1e3;
427
360
  var MAX_LOG_RECORDS = 1e3;
428
- var REFRESH_INTERVAL = 1e3;
429
361
  var MAX_INFO_OBJECT_DEPTH = 8;
430
- var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
431
362
  var TraceProcessor = class {
432
- constructor() {
433
- this.diagnostics = new DiagnosticsManager();
434
- this.diagnosticsChannel = new DiagnosticsChannel();
435
- this.remoteMetrics = new RemoteMetrics();
436
- this.remoteTracing = new RemoteTracing();
437
- this.subscriptions = /* @__PURE__ */ new Set();
438
- this.resources = /* @__PURE__ */ new Map();
439
- this.resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
440
- this.resourceIdList = [];
441
- this.spans = /* @__PURE__ */ new Map();
442
- this.spanIdList = [];
443
- this.logs = [];
444
- this._instanceTag = null;
445
- this._logProcessor = (config, entry) => {
446
- switch (entry.level) {
447
- case LogLevel.ERROR:
448
- case LogLevel.WARN:
449
- case LogLevel.TRACE: {
450
- const scope = entry.meta?.S;
451
- const resource2 = this.resourceInstanceIndex.get(scope);
452
- if (!resource2) {
453
- return;
454
- }
455
- const context = getContextFromEntry(entry) ?? {};
456
- for (const key of Object.keys(context)) {
457
- context[key] = sanitizeValue(context[key], 0, this);
363
+ diagnostics = new DiagnosticsManager();
364
+ diagnosticsChannel = new DiagnosticsChannel();
365
+ remoteMetrics = new RemoteMetrics();
366
+ #bufferingBackend = new BufferingTracingBackend();
367
+ #activeBackend = this.#bufferingBackend;
368
+ /**
369
+ * Tracing backend. Initially a buffering backend that records spans;
370
+ * once the observability package sets a real backend, the buffer is drained
371
+ * and a thin translating wrapper is installed that resolves stale buffered
372
+ * parent IDs still held by in-flight {@link Context} objects.
373
+ *
374
+ * The wrapper only allocates when a `buffered-*` parent is actually encountered;
375
+ * the common path is a single `startsWith` check and direct passthrough.
376
+ */
377
+ get tracingBackend() {
378
+ return this.#activeBackend;
379
+ }
380
+ set tracingBackend(backend) {
381
+ if (!backend || backend === this.#bufferingBackend) {
382
+ this.#bufferingBackend.clear();
383
+ this.#activeBackend = this.#bufferingBackend;
384
+ return;
385
+ }
386
+ const idMap = this.#bufferingBackend.drain(backend);
387
+ this.#activeBackend = {
388
+ startSpan: (options) => {
389
+ const parent = options.parentContext;
390
+ if (parent?.traceparent.startsWith(BUFFERED_PREFIX)) {
391
+ const translated = idMap.get(parent.traceparent);
392
+ if (translated) {
393
+ return backend.startSpan({
394
+ ...options,
395
+ parentContext: translated
396
+ });
458
397
  }
459
- const entryToPush = {
460
- level: entry.level,
461
- message: entry.message,
462
- context,
463
- timestamp: /* @__PURE__ */ new Date(),
464
- meta: {
465
- file: entry.meta?.F ?? "",
466
- line: entry.meta?.L ?? 0,
467
- resourceId: resource2.data.id
468
- }
469
- };
470
- this._pushLog(entryToPush);
471
- break;
472
398
  }
473
- default:
399
+ return backend.startSpan(options);
474
400
  }
475
401
  };
476
- log.addProcessor(this._logProcessor.bind(this), void 0, {
477
- F: __dxlog_file3,
478
- L: 103,
479
- S: this,
480
- C: (f, a) => f(...a)
481
- });
482
- if (!IS_CLOUDFLARE_WORKERS) {
483
- const refreshInterval = setInterval(this.refresh.bind(this), REFRESH_INTERVAL);
484
- unrefTimeout(refreshInterval);
485
- }
402
+ }
403
+ resources = /* @__PURE__ */ new Map();
404
+ resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
405
+ resourceIdList = [];
406
+ logs = [];
407
+ _instanceTag = null;
408
+ constructor() {
409
+ log.addProcessor(this._logProcessor.bind(this), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 80, S: this });
486
410
  if (DiagnosticsChannel.supported) {
487
411
  this.diagnosticsChannel.serve(this.diagnostics);
488
412
  }
@@ -492,10 +416,7 @@ var TraceProcessor = class {
492
416
  this._instanceTag = tag;
493
417
  this.diagnostics.setInstanceTag(tag);
494
418
  }
495
- /**
496
- * @internal
497
- */
498
- // TODO(burdon): Comment.
419
+ /** @internal */
499
420
  createTraceResource(params) {
500
421
  const id = this.resources.size;
501
422
  const tracingContext = getTracingContext(Object.getPrototypeOf(params.instance));
@@ -516,24 +437,10 @@ var TraceProcessor = class {
516
437
  if (this.resourceIdList.length > MAX_RESOURCE_RECORDS) {
517
438
  this._clearResources();
518
439
  }
519
- this._markResourceDirty(id);
520
- }
521
- createTraceSender() {
522
- return new TraceSender(this);
523
- }
524
- traceSpan(params) {
525
- const span2 = new TracingSpan(this, params);
526
- this._flushSpan(span2);
527
- return span2;
528
440
  }
529
441
  // TODO(burdon): Not implemented.
530
442
  addLink(parent, child, opts) {
531
443
  }
532
- //
533
- // Getters
534
- //
535
- // TODO(burdon): Define type.
536
- // TODO(burdon): Reconcile with system service.
537
444
  getDiagnostics() {
538
445
  this.refresh();
539
446
  return {
@@ -541,7 +448,6 @@ var TraceProcessor = class {
541
448
  `${entry.sanitizedClassName}#${entry.data.instanceId}`,
542
449
  entry.data
543
450
  ])),
544
- spans: Array.from(this.spans.values()),
545
451
  logs: this.logs.filter((log2) => log2.level >= LogLevel.INFO)
546
452
  };
547
453
  }
@@ -596,43 +502,8 @@ var TraceProcessor = class {
596
502
  for (const key of Object.keys(tracingContext.metricsProperties)) {
597
503
  instance[key]._tick?.(time);
598
504
  }
599
- let _changed = false;
600
- const oldInfo = resource2.data.info;
601
505
  resource2.data.info = this.getResourceInfo(instance);
602
- _changed ||= !areEqualShallow(oldInfo, resource2.data.info);
603
- const oldMetrics = resource2.data.metrics;
604
506
  resource2.data.metrics = this.getResourceMetrics(instance);
605
- _changed ||= !areEqualShallow(oldMetrics, resource2.data.metrics);
606
- this._markResourceDirty(resource2.data.id);
607
- }
608
- for (const subscription of this.subscriptions) {
609
- subscription.flush();
610
- }
611
- }
612
- //
613
- // Implementation
614
- //
615
- /**
616
- * @internal
617
- */
618
- _flushSpan(runtimeSpan) {
619
- const span2 = runtimeSpan.serialize();
620
- this.spans.set(span2.id, span2);
621
- this.spanIdList.push(span2.id);
622
- if (this.spanIdList.length > MAX_SPAN_RECORDS) {
623
- this._clearSpans();
624
- }
625
- this._markSpanDirty(span2.id);
626
- this.remoteTracing.flushSpan(runtimeSpan);
627
- }
628
- _markResourceDirty(id) {
629
- for (const subscription of this.subscriptions) {
630
- subscription.dirtyResources.add(id);
631
- }
632
- }
633
- _markSpanDirty(id) {
634
- for (const subscription of this.subscriptions) {
635
- subscription.dirtySpans.add(id);
636
507
  }
637
508
  }
638
509
  _clearResources() {
@@ -641,103 +512,45 @@ var TraceProcessor = class {
641
512
  this.resources.delete(id);
642
513
  }
643
514
  }
644
- _clearSpans() {
645
- while (this.spanIdList.length > MAX_SPAN_RECORDS) {
646
- const id = this.spanIdList.shift();
647
- this.spans.delete(id);
648
- }
649
- }
650
515
  _pushLog(log2) {
651
516
  this.logs.push(log2);
652
517
  if (this.logs.length > MAX_LOG_RECORDS) {
653
518
  this.logs.shift();
654
519
  }
655
- for (const subscription of this.subscriptions) {
656
- subscription.newLogs.push(log2);
657
- }
658
520
  }
659
- };
660
- var TracingSpan = class _TracingSpan {
661
- static {
662
- this.nextId = 0;
663
- }
664
- constructor(_traceProcessor, params) {
665
- this._traceProcessor = _traceProcessor;
666
- this.parentId = null;
667
- this.resourceId = null;
668
- this.endTs = null;
669
- this.error = null;
670
- this._ctx = null;
671
- this.id = _TracingSpan.nextId++;
672
- this.methodName = params.methodName;
673
- this.resourceId = _traceProcessor.getResourceId(params.instance);
674
- this.startTs = performance.now();
675
- this._showInBrowserTimeline = params.showInBrowserTimeline;
676
- this.op = params.op;
677
- this.attributes = params.attributes ?? {};
678
- if (params.parentCtx) {
679
- this._ctx = params.parentCtx.derive({
680
- attributes: {
681
- [TRACE_SPAN_ATTRIBUTE]: this.id
521
+ _logProcessor = (config, entry) => {
522
+ switch (entry.level) {
523
+ case LogLevel.ERROR:
524
+ case LogLevel.WARN:
525
+ case LogLevel.TRACE: {
526
+ const scope = entry.meta?.S;
527
+ const resource2 = this.resourceInstanceIndex.get(scope);
528
+ if (!resource2) {
529
+ return;
682
530
  }
683
- });
684
- const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
685
- if (typeof parentId === "number") {
686
- this.parentId = parentId;
531
+ const context = {
532
+ ...entry.computedContext
533
+ };
534
+ if (entry.computedError !== void 0) {
535
+ context.error = entry.computedError;
536
+ }
537
+ const { filename, line } = entry.computedMeta;
538
+ const entryToPush = {
539
+ level: entry.level,
540
+ message: entry.message ?? entry.computedError ?? "",
541
+ context,
542
+ timestamp: new Date(entry.timestamp),
543
+ meta: {
544
+ file: filename ?? "",
545
+ line: line ?? 0,
546
+ resourceId: resource2.data.id
547
+ }
548
+ };
549
+ this._pushLog(entryToPush);
550
+ break;
687
551
  }
552
+ default:
688
553
  }
689
- }
690
- get name() {
691
- const resource2 = this._traceProcessor.resources.get(this.resourceId);
692
- return resource2 ? `${resource2.sanitizedClassName}#${resource2.data.instanceId}.${this.methodName}` : this.methodName;
693
- }
694
- get ctx() {
695
- return this._ctx;
696
- }
697
- markSuccess() {
698
- this.endTs = performance.now();
699
- this._traceProcessor._flushSpan(this);
700
- if (this._showInBrowserTimeline) {
701
- this._markInBrowserTimeline();
702
- }
703
- }
704
- markError(err) {
705
- this.endTs = performance.now();
706
- this.error = serializeError(err);
707
- this._traceProcessor._flushSpan(this);
708
- if (this._showInBrowserTimeline) {
709
- this._markInBrowserTimeline();
710
- }
711
- }
712
- serialize() {
713
- return {
714
- id: this.id,
715
- resourceId: this.resourceId ?? void 0,
716
- methodName: this.methodName,
717
- parentId: this.parentId ?? void 0,
718
- startTs: this.startTs.toFixed(3),
719
- endTs: this.endTs?.toFixed(3) ?? void 0,
720
- error: this.error ?? void 0
721
- };
722
- }
723
- _markInBrowserTimeline() {
724
- if (typeof globalThis?.performance?.measure === "function") {
725
- performance.measure(this.name, {
726
- start: this.startTs,
727
- end: this.endTs
728
- });
729
- }
730
- }
731
- };
732
- var serializeError = (err) => {
733
- if (err instanceof Error) {
734
- return {
735
- name: err.name,
736
- message: err.message
737
- };
738
- }
739
- return {
740
- message: String(err)
741
554
  };
742
555
  };
743
556
  var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
@@ -786,33 +599,46 @@ var sanitizeValue = (value, depth, traceProcessor) => {
786
599
  return value.toString();
787
600
  }
788
601
  };
789
- var areEqualShallow = (a, b) => {
790
- for (const key in a) {
791
- if (!(key in b) || a[key] !== b[key]) {
792
- return false;
793
- }
794
- }
795
- for (const key in b) {
796
- if (!(key in a) || a[key] !== b[key]) {
797
- return false;
798
- }
799
- }
800
- return true;
801
- };
802
602
  var sanitizeClassName = (className) => {
603
+ let name = className.replace(/^_+/, "");
803
604
  const SANITIZE_REGEX = /[^_](\d+)$/;
804
- const m = className.match(SANITIZE_REGEX);
805
- if (!m) {
806
- return className;
807
- } else {
808
- return className.slice(0, -m[1].length);
605
+ const m = name.match(SANITIZE_REGEX);
606
+ if (m) {
607
+ name = name.slice(0, -m[1].length);
809
608
  }
609
+ return name;
810
610
  };
811
611
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
812
612
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
813
613
 
814
614
  // src/api.ts
615
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/common/tracing/src/api.ts";
616
+ var LIFECYCLE_SPAN = /* @__PURE__ */ Symbol("dxos.tracing.lifecycle-span");
617
+ var TRACE_ALL_KEY = "dxos.debug.traceAll";
618
+ var collectSpanAttributes = (instance, spanAttributes) => {
619
+ const proto = Object.getPrototypeOf(instance);
620
+ if (!proto) {
621
+ return;
622
+ }
623
+ const tracingContext = getTracingContext(proto);
624
+ for (const [key, { options }] of Object.entries(tracingContext.infoProperties)) {
625
+ if (!options.spanAttribute) {
626
+ continue;
627
+ }
628
+ try {
629
+ const value = typeof instance[key] === "function" ? instance[key]() : instance[key];
630
+ if (value != null) {
631
+ const resolved = options.enum ? options.enum[value] : String(value);
632
+ spanAttributes[`ctx.${key}`] = resolved;
633
+ }
634
+ } catch {
635
+ }
636
+ }
637
+ };
815
638
  var resource = (options) => (constructor) => {
639
+ if (options?.lifecycle && !(constructor.prototype instanceof Resource)) {
640
+ throw new Error(`@trace.resource({ lifecycle: true }) requires ${constructor.name} to extend Resource`);
641
+ }
816
642
  const klass = /* @__PURE__ */ (() => class extends constructor {
817
643
  constructor(...rest) {
818
644
  super(...rest);
@@ -823,6 +649,65 @@ var resource = (options) => (constructor) => {
823
649
  });
824
650
  }
825
651
  })();
652
+ if (options?.lifecycle) {
653
+ const sanitizedName = sanitizeClassName(constructor.name);
654
+ const proto = klass.prototype;
655
+ const originalOpen = proto.open;
656
+ const originalClose = proto.close;
657
+ proto.open = async function(ctx) {
658
+ const self = this;
659
+ if (self._lifecycleState !== LifecycleState.CLOSED) {
660
+ return originalOpen.call(this, ctx);
661
+ }
662
+ const parentSpanContext = ctx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
663
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
664
+ const spanAttributes = {};
665
+ if (resourceEntry) {
666
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
667
+ }
668
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend?.startSpan({
669
+ name: `${sanitizedName}.lifecycle`,
670
+ op: "lifecycle",
671
+ attributes: spanAttributes,
672
+ parentContext: parentSpanContext
673
+ });
674
+ self[LIFECYCLE_SPAN] = remoteSpan;
675
+ let openCtx = ctx;
676
+ if (remoteSpan?.spanContext != null) {
677
+ const traceAttrs = {
678
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
679
+ };
680
+ openCtx = ctx ? ctx.derive({
681
+ attributes: traceAttrs
682
+ }) : new Context2({
683
+ attributes: traceAttrs
684
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 79 });
685
+ }
686
+ try {
687
+ return await originalOpen.call(this, openCtx);
688
+ } catch (err) {
689
+ remoteSpan?.setError?.(err);
690
+ remoteSpan?.end();
691
+ self[LIFECYCLE_SPAN] = void 0;
692
+ throw err;
693
+ }
694
+ };
695
+ proto.close = async function(ctx) {
696
+ const self = this;
697
+ const remoteSpan = self[LIFECYCLE_SPAN];
698
+ try {
699
+ return await originalClose.call(this, ctx);
700
+ } catch (err) {
701
+ remoteSpan?.setError?.(err);
702
+ throw err;
703
+ } finally {
704
+ if (remoteSpan) {
705
+ remoteSpan.end();
706
+ self[LIFECYCLE_SPAN] = void 0;
707
+ }
708
+ }
709
+ };
710
+ }
826
711
  Object.defineProperty(klass, "name", {
827
712
  value: constructor.name
828
713
  });
@@ -836,45 +721,117 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
836
721
  var mark = (name) => {
837
722
  performance.mark(name);
838
723
  };
839
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
724
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
840
725
  const method = descriptor.value;
841
726
  descriptor.value = async function(...args) {
842
727
  const parentCtx = args[0] instanceof Context2 ? args[0] : null;
843
- const span2 = TRACE_PROCESSOR.traceSpan({
844
- parentCtx,
845
- methodName: propertyKey,
846
- instance: this,
847
- showInBrowserTimeline,
848
- op,
849
- attributes
850
- });
851
- const callArgs = span2.ctx ? [
852
- span2.ctx,
853
- ...args.slice(1)
854
- ] : args;
728
+ const startTs = performance.now();
729
+ const parentSpanContext = parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
730
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
731
+ const className = resourceEntry?.sanitizedClassName ?? sanitizeClassName(target.constructor?.name ?? "unknown");
732
+ const spanName = `${className}.${propertyKey}`;
733
+ const spanAttributes = {};
734
+ if (resourceEntry) {
735
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
736
+ }
737
+ collectSpanAttributes(this, spanAttributes);
738
+ if (attributes) {
739
+ for (const [key, value] of Object.entries(attributes)) {
740
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
741
+ }
742
+ }
743
+ const remoteSpan = showInRemoteTracing ? TRACE_PROCESSOR.tracingBackend?.startSpan({
744
+ name: spanName,
745
+ op: op ?? "function",
746
+ attributes: spanAttributes,
747
+ parentContext: parentSpanContext
748
+ }) : void 0;
749
+ let callArgs = args;
750
+ if (parentCtx) {
751
+ const childCtx = remoteSpan?.spanContext != null ? parentCtx.derive({
752
+ attributes: {
753
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
754
+ }
755
+ }) : parentCtx.derive();
756
+ callArgs = [
757
+ childCtx,
758
+ ...args.slice(1)
759
+ ];
760
+ }
855
761
  try {
856
762
  return await method.apply(this, callArgs);
857
763
  } catch (err) {
858
- span2.markError(err);
764
+ remoteSpan?.setError?.(err);
859
765
  throw err;
860
766
  } finally {
861
- span2.markSuccess();
767
+ remoteSpan?.end();
768
+ if (showInBrowserTimeline && typeof globalThis?.performance?.measure === "function") {
769
+ performance.measure(spanName, {
770
+ start: startTs,
771
+ end: performance.now()
772
+ });
773
+ }
862
774
  }
863
775
  };
864
776
  };
865
- var spans = /* @__PURE__ */ new Map();
777
+ var manualSpans = /* @__PURE__ */ new Map();
778
+ var manualSpanTimestamps = /* @__PURE__ */ new Map();
866
779
  var spanStart = (params) => {
867
- if (spans.has(params.id)) {
868
- return;
780
+ if (manualSpans.has(params.id) || manualSpanTimestamps.has(params.id)) {
781
+ return params.parentCtx;
782
+ }
783
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(params.instance);
784
+ const className = resourceEntry?.sanitizedClassName ?? "unknown";
785
+ const spanName = `${className}.${params.methodName}`;
786
+ if (params.showInBrowserTimeline) {
787
+ manualSpanTimestamps.set(params.id, {
788
+ name: spanName,
789
+ startTs: performance.now()
790
+ });
791
+ }
792
+ if (params.showInRemoteTracing === false || !TRACE_PROCESSOR.tracingBackend) {
793
+ return params.parentCtx;
794
+ }
795
+ const parentSpanContext = params.parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
796
+ const spanAttributes = {};
797
+ if (resourceEntry) {
798
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
869
799
  }
870
- const span2 = TRACE_PROCESSOR.traceSpan(params);
871
- spans.set(params.id, span2);
800
+ collectSpanAttributes(params.instance, spanAttributes);
801
+ if (params.attributes) {
802
+ for (const [key, value] of Object.entries(params.attributes)) {
803
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
804
+ }
805
+ }
806
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend.startSpan({
807
+ name: spanName,
808
+ op: params.op ?? "function",
809
+ attributes: spanAttributes,
810
+ parentContext: parentSpanContext
811
+ });
812
+ manualSpans.set(params.id, remoteSpan);
813
+ if (params.parentCtx && remoteSpan.spanContext != null) {
814
+ return params.parentCtx.derive({
815
+ attributes: {
816
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
817
+ }
818
+ });
819
+ }
820
+ return params.parentCtx;
872
821
  };
873
822
  var spanEnd = (id) => {
874
- const span2 = spans.get(id);
875
- if (span2) {
876
- span2.markSuccess();
877
- spans.delete(id);
823
+ const remoteSpan = manualSpans.get(id);
824
+ if (remoteSpan) {
825
+ remoteSpan.end();
826
+ manualSpans.delete(id);
827
+ }
828
+ const timestamps = manualSpanTimestamps.get(id);
829
+ if (timestamps && typeof globalThis?.performance?.measure === "function") {
830
+ performance.measure(timestamps.name, {
831
+ start: timestamps.startTs,
832
+ end: performance.now()
833
+ });
834
+ manualSpanTimestamps.delete(id);
878
835
  }
879
836
  };
880
837
  var metricsCounter = () => (target, propertyKey, descriptor) => {
@@ -901,6 +858,11 @@ var trace = {
901
858
 
902
859
  // src/metrics/base.ts
903
860
  var BaseCounter = class {
861
+ /**
862
+ * @internal
863
+ */
864
+ _instance;
865
+ name;
904
866
  /**
905
867
  * @internal
906
868
  */
@@ -914,9 +876,10 @@ var BaseCounter = class {
914
876
 
915
877
  // src/metrics/unary-counter.ts
916
878
  var UnaryCounter = class extends BaseCounter {
879
+ value = 0;
880
+ units;
917
881
  constructor({ units } = {}) {
918
882
  super();
919
- this.value = 0;
920
883
  this.units = units;
921
884
  }
922
885
  inc(by = 1) {
@@ -936,11 +899,12 @@ var UnaryCounter = class extends BaseCounter {
936
899
  // src/metrics/time-series-counter.ts
937
900
  var MAX_BUCKETS = 60;
938
901
  var TimeSeriesCounter = class extends BaseCounter {
902
+ _currentValue = 0;
903
+ _totalValue = 0;
904
+ _buckets = [];
905
+ units;
939
906
  constructor({ units } = {}) {
940
907
  super();
941
- this._currentValue = 0;
942
- this._totalValue = 0;
943
- this._buckets = [];
944
908
  this.units = units;
945
909
  }
946
910
  inc(by = 1) {
@@ -976,13 +940,10 @@ var TimeSeriesCounter = class extends BaseCounter {
976
940
  // src/metrics/time-usage-counter.ts
977
941
  var MAX_BUCKETS2 = 60;
978
942
  var TimeUsageCounter = class extends BaseCounter {
979
- constructor() {
980
- super(...arguments);
981
- this._currentValue = 0;
982
- this._totalValue = 0;
983
- this._buckets = [];
984
- this._lastTickTime = performance.now();
985
- }
943
+ _currentValue = 0;
944
+ _totalValue = 0;
945
+ _buckets = [];
946
+ _lastTickTime = performance.now();
986
947
  record(time) {
987
948
  this._currentValue += time;
988
949
  this._totalValue += time;
@@ -1027,9 +988,10 @@ var TimeUsageCounter = class extends BaseCounter {
1027
988
 
1028
989
  // src/metrics/map-counter.ts
1029
990
  var MapCounter = class extends BaseCounter {
991
+ values = /* @__PURE__ */ new Map();
992
+ units;
1030
993
  constructor({ units } = {}) {
1031
994
  super();
1032
- this.values = /* @__PURE__ */ new Map();
1033
995
  this.units = units;
1034
996
  }
1035
997
  inc(key, by = 1) {
@@ -1052,6 +1014,7 @@ var MapCounter = class extends BaseCounter {
1052
1014
 
1053
1015
  // src/metrics/custom-counter.ts
1054
1016
  var CustomCounter = class extends BaseCounter {
1017
+ _getData;
1055
1018
  constructor(_getData) {
1056
1019
  super(), this._getData = _getData;
1057
1020
  }
@@ -1086,16 +1049,13 @@ export {
1086
1049
  DiagnosticsManager,
1087
1050
  MapCounter,
1088
1051
  RemoteMetrics,
1089
- RemoteTracing,
1090
1052
  ResourceEntry,
1053
+ TRACE_ALL_KEY,
1091
1054
  TRACE_PROCESSOR,
1092
- TRACE_SPAN_ATTRIBUTE,
1093
1055
  TimeSeriesCounter,
1094
1056
  TimeUsageCounter,
1095
1057
  TraceDiagnosticImpl,
1096
1058
  TraceProcessor,
1097
- TraceSender,
1098
- TracingSpan,
1099
1059
  UnaryCounter,
1100
1060
  getTracingContext,
1101
1061
  sanitizeClassName,