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

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 (39) hide show
  1. package/dist/lib/browser/index.mjs +392 -384
  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 +392 -384
  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/index.d.ts +1 -2
  14. package/dist/types/src/index.d.ts.map +1 -1
  15. package/dist/types/src/remote/index.d.ts +0 -1
  16. package/dist/types/src/remote/index.d.ts.map +1 -1
  17. package/dist/types/src/symbols.d.ts +0 -1
  18. package/dist/types/src/symbols.d.ts.map +1 -1
  19. package/dist/types/src/trace-processor.d.ts +16 -52
  20. package/dist/types/src/trace-processor.d.ts.map +1 -1
  21. package/dist/types/src/tracing-types.d.ts +67 -0
  22. package/dist/types/src/tracing-types.d.ts.map +1 -0
  23. package/dist/types/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +15 -11
  25. package/src/api.ts +237 -35
  26. package/src/buffering-backend.ts +112 -0
  27. package/src/diagnostic.ts +2 -2
  28. package/src/index.ts +1 -2
  29. package/src/remote/index.ts +0 -1
  30. package/src/symbols.ts +0 -2
  31. package/src/trace-processor.ts +51 -252
  32. package/src/tracing-types.ts +77 -0
  33. package/src/tracing.test.ts +513 -4
  34. package/dist/types/src/remote/tracing.d.ts +0 -23
  35. package/dist/types/src/remote/tracing.d.ts.map +0 -1
  36. package/dist/types/src/trace-sender.d.ts +0 -9
  37. package/dist/types/src/trace-sender.d.ts.map +0 -1
  38. package/src/remote/tracing.ts +0 -53
  39. 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
14
  import { LogLevel, getContextFromEntry, 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
  }
@@ -115,17 +201,19 @@ var __dxlog_file2 = "/__w/dxos/dxos/packages/common/tracing/src/diagnostics-chan
115
201
  var DEFAULT_CHANNEL_NAME = "dxos-diagnostics";
116
202
  var DISCOVER_TIME = 500;
117
203
  var DiagnosticsChannel = class _DiagnosticsChannel {
204
+ _channelName;
118
205
  static get supported() {
119
206
  return globalThis.BroadcastChannel != null;
120
207
  }
208
+ _ctx = new Context(void 0, {
209
+ F: __dxlog_file2,
210
+ L: 46
211
+ });
212
+ // Separate channels becauase the client and server may be in the same process.
213
+ _serveChannel = void 0;
214
+ _clientChannel = void 0;
121
215
  constructor(_channelName = DEFAULT_CHANNEL_NAME) {
122
216
  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
217
  if (_DiagnosticsChannel.supported) {
130
218
  this._serveChannel = new BroadcastChannel(_channelName);
131
219
  this._clientChannel = new BroadcastChannel(_channelName);
@@ -260,9 +348,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
260
348
 
261
349
  // src/remote/metrics.ts
262
350
  var RemoteMetrics = class {
263
- constructor() {
264
- this._metrics = /* @__PURE__ */ new Set();
265
- }
351
+ _metrics = /* @__PURE__ */ new Set();
266
352
  registerProcessor(processor) {
267
353
  this._metrics.add(processor);
268
354
  }
@@ -280,124 +366,6 @@ var RemoteMetrics = class {
280
366
  }
281
367
  };
282
368
 
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
369
  // src/weak-ref.ts
402
370
  var WeakRefMock = class {
403
371
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -412,6 +380,15 @@ var WeakRef = globalThis.WeakRef ?? WeakRefMock;
412
380
  // src/trace-processor.ts
413
381
  var __dxlog_file3 = "/__w/dxos/dxos/packages/common/tracing/src/trace-processor.ts";
414
382
  var ResourceEntry = class {
383
+ data;
384
+ instance;
385
+ annotation;
386
+ /**
387
+ * Sometimes bundlers mangle class names: WebFile -> WebFile2.
388
+ *
389
+ * We use a heuristic to remove the suffix.
390
+ */
391
+ sanitizedClassName;
415
392
  constructor(data, instance, annotation) {
416
393
  this.data = data;
417
394
  this.instance = instance;
@@ -423,66 +400,61 @@ var ResourceEntry = class {
423
400
  }
424
401
  };
425
402
  var MAX_RESOURCE_RECORDS = 2e3;
426
- var MAX_SPAN_RECORDS = 1e3;
427
403
  var MAX_LOG_RECORDS = 1e3;
428
- var REFRESH_INTERVAL = 1e3;
429
404
  var MAX_INFO_OBJECT_DEPTH = 8;
430
- var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
431
405
  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);
406
+ diagnostics = new DiagnosticsManager();
407
+ diagnosticsChannel = new DiagnosticsChannel();
408
+ remoteMetrics = new RemoteMetrics();
409
+ #bufferingBackend = new BufferingTracingBackend();
410
+ #activeBackend = this.#bufferingBackend;
411
+ /**
412
+ * Tracing backend. Initially a buffering backend that records spans;
413
+ * once the observability package sets a real backend, the buffer is drained
414
+ * and a thin translating wrapper is installed that resolves stale buffered
415
+ * parent IDs still held by in-flight {@link Context} objects.
416
+ *
417
+ * The wrapper only allocates when a `buffered-*` parent is actually encountered;
418
+ * the common path is a single `startsWith` check and direct passthrough.
419
+ */
420
+ get tracingBackend() {
421
+ return this.#activeBackend;
422
+ }
423
+ set tracingBackend(backend) {
424
+ if (!backend || backend === this.#bufferingBackend) {
425
+ this.#bufferingBackend.clear();
426
+ this.#activeBackend = this.#bufferingBackend;
427
+ return;
428
+ }
429
+ const idMap = this.#bufferingBackend.drain(backend);
430
+ this.#activeBackend = {
431
+ startSpan: (options) => {
432
+ const parent = options.parentContext;
433
+ if (parent?.traceparent.startsWith(BUFFERED_PREFIX)) {
434
+ const translated = idMap.get(parent.traceparent);
435
+ if (translated) {
436
+ return backend.startSpan({
437
+ ...options,
438
+ parentContext: translated
439
+ });
458
440
  }
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
441
  }
473
- default:
442
+ return backend.startSpan(options);
474
443
  }
475
444
  };
445
+ }
446
+ resources = /* @__PURE__ */ new Map();
447
+ resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
448
+ resourceIdList = [];
449
+ logs = [];
450
+ _instanceTag = null;
451
+ constructor() {
476
452
  log.addProcessor(this._logProcessor.bind(this), void 0, {
477
453
  F: __dxlog_file3,
478
- L: 103,
454
+ L: 108,
479
455
  S: this,
480
456
  C: (f, a) => f(...a)
481
457
  });
482
- if (!IS_CLOUDFLARE_WORKERS) {
483
- const refreshInterval = setInterval(this.refresh.bind(this), REFRESH_INTERVAL);
484
- unrefTimeout(refreshInterval);
485
- }
486
458
  if (DiagnosticsChannel.supported) {
487
459
  this.diagnosticsChannel.serve(this.diagnostics);
488
460
  }
@@ -492,10 +464,7 @@ var TraceProcessor = class {
492
464
  this._instanceTag = tag;
493
465
  this.diagnostics.setInstanceTag(tag);
494
466
  }
495
- /**
496
- * @internal
497
- */
498
- // TODO(burdon): Comment.
467
+ /** @internal */
499
468
  createTraceResource(params) {
500
469
  const id = this.resources.size;
501
470
  const tracingContext = getTracingContext(Object.getPrototypeOf(params.instance));
@@ -516,24 +485,10 @@ var TraceProcessor = class {
516
485
  if (this.resourceIdList.length > MAX_RESOURCE_RECORDS) {
517
486
  this._clearResources();
518
487
  }
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
488
  }
529
489
  // TODO(burdon): Not implemented.
530
490
  addLink(parent, child, opts) {
531
491
  }
532
- //
533
- // Getters
534
- //
535
- // TODO(burdon): Define type.
536
- // TODO(burdon): Reconcile with system service.
537
492
  getDiagnostics() {
538
493
  this.refresh();
539
494
  return {
@@ -541,7 +496,6 @@ var TraceProcessor = class {
541
496
  `${entry.sanitizedClassName}#${entry.data.instanceId}`,
542
497
  entry.data
543
498
  ])),
544
- spans: Array.from(this.spans.values()),
545
499
  logs: this.logs.filter((log2) => log2.level >= LogLevel.INFO)
546
500
  };
547
501
  }
@@ -596,43 +550,8 @@ var TraceProcessor = class {
596
550
  for (const key of Object.keys(tracingContext.metricsProperties)) {
597
551
  instance[key]._tick?.(time);
598
552
  }
599
- let _changed = false;
600
- const oldInfo = resource2.data.info;
601
553
  resource2.data.info = this.getResourceInfo(instance);
602
- _changed ||= !areEqualShallow(oldInfo, resource2.data.info);
603
- const oldMetrics = resource2.data.metrics;
604
554
  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
555
  }
637
556
  }
638
557
  _clearResources() {
@@ -641,103 +560,42 @@ var TraceProcessor = class {
641
560
  this.resources.delete(id);
642
561
  }
643
562
  }
644
- _clearSpans() {
645
- while (this.spanIdList.length > MAX_SPAN_RECORDS) {
646
- const id = this.spanIdList.shift();
647
- this.spans.delete(id);
648
- }
649
- }
650
563
  _pushLog(log2) {
651
564
  this.logs.push(log2);
652
565
  if (this.logs.length > MAX_LOG_RECORDS) {
653
566
  this.logs.shift();
654
567
  }
655
- for (const subscription of this.subscriptions) {
656
- subscription.newLogs.push(log2);
657
- }
658
568
  }
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
569
+ _logProcessor = (config, entry) => {
570
+ switch (entry.level) {
571
+ case LogLevel.ERROR:
572
+ case LogLevel.WARN:
573
+ case LogLevel.TRACE: {
574
+ const scope = entry.meta?.S;
575
+ const resource2 = this.resourceInstanceIndex.get(scope);
576
+ if (!resource2) {
577
+ return;
682
578
  }
683
- });
684
- const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
685
- if (typeof parentId === "number") {
686
- this.parentId = parentId;
579
+ const context = getContextFromEntry(entry) ?? {};
580
+ for (const key of Object.keys(context)) {
581
+ context[key] = sanitizeValue(context[key], 0, this);
582
+ }
583
+ const entryToPush = {
584
+ level: entry.level,
585
+ message: entry.message ?? (entry.error ? entry.error.message ?? String(entry.error) : ""),
586
+ context,
587
+ timestamp: /* @__PURE__ */ new Date(),
588
+ meta: {
589
+ file: entry.meta?.F ?? "",
590
+ line: entry.meta?.L ?? 0,
591
+ resourceId: resource2.data.id
592
+ }
593
+ };
594
+ this._pushLog(entryToPush);
595
+ break;
687
596
  }
597
+ default:
688
598
  }
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
599
  };
742
600
  };
743
601
  var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
@@ -786,33 +644,46 @@ var sanitizeValue = (value, depth, traceProcessor) => {
786
644
  return value.toString();
787
645
  }
788
646
  };
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
647
  var sanitizeClassName = (className) => {
648
+ let name = className.replace(/^_+/, "");
803
649
  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);
650
+ const m = name.match(SANITIZE_REGEX);
651
+ if (m) {
652
+ name = name.slice(0, -m[1].length);
809
653
  }
654
+ return name;
810
655
  };
811
656
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
812
657
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
813
658
 
814
659
  // src/api.ts
660
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/common/tracing/src/api.ts";
661
+ var LIFECYCLE_SPAN = /* @__PURE__ */ Symbol("dxos.tracing.lifecycle-span");
662
+ var TRACE_ALL_KEY = "dxos.debug.traceAll";
663
+ var collectSpanAttributes = (instance, spanAttributes) => {
664
+ const proto = Object.getPrototypeOf(instance);
665
+ if (!proto) {
666
+ return;
667
+ }
668
+ const tracingContext = getTracingContext(proto);
669
+ for (const [key, { options }] of Object.entries(tracingContext.infoProperties)) {
670
+ if (!options.spanAttribute) {
671
+ continue;
672
+ }
673
+ try {
674
+ const value = typeof instance[key] === "function" ? instance[key]() : instance[key];
675
+ if (value != null) {
676
+ const resolved = options.enum ? options.enum[value] : String(value);
677
+ spanAttributes[`ctx.${key}`] = resolved;
678
+ }
679
+ } catch {
680
+ }
681
+ }
682
+ };
815
683
  var resource = (options) => (constructor) => {
684
+ if (options?.lifecycle && !(constructor.prototype instanceof Resource)) {
685
+ throw new Error(`@trace.resource({ lifecycle: true }) requires ${constructor.name} to extend Resource`);
686
+ }
816
687
  const klass = /* @__PURE__ */ (() => class extends constructor {
817
688
  constructor(...rest) {
818
689
  super(...rest);
@@ -823,6 +694,68 @@ var resource = (options) => (constructor) => {
823
694
  });
824
695
  }
825
696
  })();
697
+ if (options?.lifecycle) {
698
+ const sanitizedName = sanitizeClassName(constructor.name);
699
+ const proto = klass.prototype;
700
+ const originalOpen = proto.open;
701
+ const originalClose = proto.close;
702
+ proto.open = async function(ctx) {
703
+ const self = this;
704
+ if (self._lifecycleState !== LifecycleState.CLOSED) {
705
+ return originalOpen.call(this, ctx);
706
+ }
707
+ const parentSpanContext = ctx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
708
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
709
+ const spanAttributes = {};
710
+ if (resourceEntry) {
711
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
712
+ }
713
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend?.startSpan({
714
+ name: `${sanitizedName}.lifecycle`,
715
+ op: "lifecycle",
716
+ attributes: spanAttributes,
717
+ parentContext: parentSpanContext
718
+ });
719
+ self[LIFECYCLE_SPAN] = remoteSpan;
720
+ let openCtx = ctx;
721
+ if (remoteSpan?.spanContext != null) {
722
+ const traceAttrs = {
723
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
724
+ };
725
+ openCtx = ctx ? ctx.derive({
726
+ attributes: traceAttrs
727
+ }) : new Context2({
728
+ attributes: traceAttrs
729
+ }, {
730
+ F: __dxlog_file4,
731
+ L: 104
732
+ });
733
+ }
734
+ try {
735
+ return await originalOpen.call(this, openCtx);
736
+ } catch (err) {
737
+ remoteSpan?.setError?.(err);
738
+ remoteSpan?.end();
739
+ self[LIFECYCLE_SPAN] = void 0;
740
+ throw err;
741
+ }
742
+ };
743
+ proto.close = async function(ctx) {
744
+ const self = this;
745
+ const remoteSpan = self[LIFECYCLE_SPAN];
746
+ try {
747
+ return await originalClose.call(this, ctx);
748
+ } catch (err) {
749
+ remoteSpan?.setError?.(err);
750
+ throw err;
751
+ } finally {
752
+ if (remoteSpan) {
753
+ remoteSpan.end();
754
+ self[LIFECYCLE_SPAN] = void 0;
755
+ }
756
+ }
757
+ };
758
+ }
826
759
  Object.defineProperty(klass, "name", {
827
760
  value: constructor.name
828
761
  });
@@ -836,45 +769,117 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
836
769
  var mark = (name) => {
837
770
  performance.mark(name);
838
771
  };
839
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
772
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
840
773
  const method = descriptor.value;
841
774
  descriptor.value = async function(...args) {
842
775
  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;
776
+ const startTs = performance.now();
777
+ const parentSpanContext = parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
778
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
779
+ const className = resourceEntry?.sanitizedClassName ?? sanitizeClassName(target.constructor?.name ?? "unknown");
780
+ const spanName = `${className}.${propertyKey}`;
781
+ const spanAttributes = {};
782
+ if (resourceEntry) {
783
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
784
+ }
785
+ collectSpanAttributes(this, spanAttributes);
786
+ if (attributes) {
787
+ for (const [key, value] of Object.entries(attributes)) {
788
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
789
+ }
790
+ }
791
+ const remoteSpan = showInRemoteTracing ? TRACE_PROCESSOR.tracingBackend?.startSpan({
792
+ name: spanName,
793
+ op: op ?? "function",
794
+ attributes: spanAttributes,
795
+ parentContext: parentSpanContext
796
+ }) : void 0;
797
+ let callArgs = args;
798
+ if (parentCtx) {
799
+ const childCtx = remoteSpan?.spanContext != null ? parentCtx.derive({
800
+ attributes: {
801
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
802
+ }
803
+ }) : parentCtx.derive();
804
+ callArgs = [
805
+ childCtx,
806
+ ...args.slice(1)
807
+ ];
808
+ }
855
809
  try {
856
810
  return await method.apply(this, callArgs);
857
811
  } catch (err) {
858
- span2.markError(err);
812
+ remoteSpan?.setError?.(err);
859
813
  throw err;
860
814
  } finally {
861
- span2.markSuccess();
815
+ remoteSpan?.end();
816
+ if (showInBrowserTimeline && typeof globalThis?.performance?.measure === "function") {
817
+ performance.measure(spanName, {
818
+ start: startTs,
819
+ end: performance.now()
820
+ });
821
+ }
862
822
  }
863
823
  };
864
824
  };
865
- var spans = /* @__PURE__ */ new Map();
825
+ var manualSpans = /* @__PURE__ */ new Map();
826
+ var manualSpanTimestamps = /* @__PURE__ */ new Map();
866
827
  var spanStart = (params) => {
867
- if (spans.has(params.id)) {
868
- return;
828
+ if (manualSpans.has(params.id) || manualSpanTimestamps.has(params.id)) {
829
+ return params.parentCtx;
830
+ }
831
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(params.instance);
832
+ const className = resourceEntry?.sanitizedClassName ?? "unknown";
833
+ const spanName = `${className}.${params.methodName}`;
834
+ if (params.showInBrowserTimeline) {
835
+ manualSpanTimestamps.set(params.id, {
836
+ name: spanName,
837
+ startTs: performance.now()
838
+ });
839
+ }
840
+ if (params.showInRemoteTracing === false || !TRACE_PROCESSOR.tracingBackend) {
841
+ return params.parentCtx;
842
+ }
843
+ const parentSpanContext = params.parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
844
+ const spanAttributes = {};
845
+ if (resourceEntry) {
846
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
847
+ }
848
+ collectSpanAttributes(params.instance, spanAttributes);
849
+ if (params.attributes) {
850
+ for (const [key, value] of Object.entries(params.attributes)) {
851
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
852
+ }
853
+ }
854
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend.startSpan({
855
+ name: spanName,
856
+ op: params.op ?? "function",
857
+ attributes: spanAttributes,
858
+ parentContext: parentSpanContext
859
+ });
860
+ manualSpans.set(params.id, remoteSpan);
861
+ if (params.parentCtx && remoteSpan.spanContext != null) {
862
+ return params.parentCtx.derive({
863
+ attributes: {
864
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
865
+ }
866
+ });
869
867
  }
870
- const span2 = TRACE_PROCESSOR.traceSpan(params);
871
- spans.set(params.id, span2);
868
+ return params.parentCtx;
872
869
  };
873
870
  var spanEnd = (id) => {
874
- const span2 = spans.get(id);
875
- if (span2) {
876
- span2.markSuccess();
877
- spans.delete(id);
871
+ const remoteSpan = manualSpans.get(id);
872
+ if (remoteSpan) {
873
+ remoteSpan.end();
874
+ manualSpans.delete(id);
875
+ }
876
+ const timestamps = manualSpanTimestamps.get(id);
877
+ if (timestamps && typeof globalThis?.performance?.measure === "function") {
878
+ performance.measure(timestamps.name, {
879
+ start: timestamps.startTs,
880
+ end: performance.now()
881
+ });
882
+ manualSpanTimestamps.delete(id);
878
883
  }
879
884
  };
880
885
  var metricsCounter = () => (target, propertyKey, descriptor) => {
@@ -901,6 +906,11 @@ var trace = {
901
906
 
902
907
  // src/metrics/base.ts
903
908
  var BaseCounter = class {
909
+ /**
910
+ * @internal
911
+ */
912
+ _instance;
913
+ name;
904
914
  /**
905
915
  * @internal
906
916
  */
@@ -914,9 +924,10 @@ var BaseCounter = class {
914
924
 
915
925
  // src/metrics/unary-counter.ts
916
926
  var UnaryCounter = class extends BaseCounter {
927
+ value = 0;
928
+ units;
917
929
  constructor({ units } = {}) {
918
930
  super();
919
- this.value = 0;
920
931
  this.units = units;
921
932
  }
922
933
  inc(by = 1) {
@@ -936,11 +947,12 @@ var UnaryCounter = class extends BaseCounter {
936
947
  // src/metrics/time-series-counter.ts
937
948
  var MAX_BUCKETS = 60;
938
949
  var TimeSeriesCounter = class extends BaseCounter {
950
+ _currentValue = 0;
951
+ _totalValue = 0;
952
+ _buckets = [];
953
+ units;
939
954
  constructor({ units } = {}) {
940
955
  super();
941
- this._currentValue = 0;
942
- this._totalValue = 0;
943
- this._buckets = [];
944
956
  this.units = units;
945
957
  }
946
958
  inc(by = 1) {
@@ -976,13 +988,10 @@ var TimeSeriesCounter = class extends BaseCounter {
976
988
  // src/metrics/time-usage-counter.ts
977
989
  var MAX_BUCKETS2 = 60;
978
990
  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
- }
991
+ _currentValue = 0;
992
+ _totalValue = 0;
993
+ _buckets = [];
994
+ _lastTickTime = performance.now();
986
995
  record(time) {
987
996
  this._currentValue += time;
988
997
  this._totalValue += time;
@@ -1027,9 +1036,10 @@ var TimeUsageCounter = class extends BaseCounter {
1027
1036
 
1028
1037
  // src/metrics/map-counter.ts
1029
1038
  var MapCounter = class extends BaseCounter {
1039
+ values = /* @__PURE__ */ new Map();
1040
+ units;
1030
1041
  constructor({ units } = {}) {
1031
1042
  super();
1032
- this.values = /* @__PURE__ */ new Map();
1033
1043
  this.units = units;
1034
1044
  }
1035
1045
  inc(key, by = 1) {
@@ -1052,6 +1062,7 @@ var MapCounter = class extends BaseCounter {
1052
1062
 
1053
1063
  // src/metrics/custom-counter.ts
1054
1064
  var CustomCounter = class extends BaseCounter {
1065
+ _getData;
1055
1066
  constructor(_getData) {
1056
1067
  super(), this._getData = _getData;
1057
1068
  }
@@ -1086,16 +1097,13 @@ export {
1086
1097
  DiagnosticsManager,
1087
1098
  MapCounter,
1088
1099
  RemoteMetrics,
1089
- RemoteTracing,
1090
1100
  ResourceEntry,
1101
+ TRACE_ALL_KEY,
1091
1102
  TRACE_PROCESSOR,
1092
- TRACE_SPAN_ATTRIBUTE,
1093
1103
  TimeSeriesCounter,
1094
1104
  TimeUsageCounter,
1095
1105
  TraceDiagnosticImpl,
1096
1106
  TraceProcessor,
1097
- TraceSender,
1098
- TracingSpan,
1099
1107
  UnaryCounter,
1100
1108
  getTracingContext,
1101
1109
  sanitizeClassName,