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