@dxos/tracing 0.8.4-main.f5c0578 → 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 +323 -345
  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 +323 -345
  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 +13 -9
  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 +49 -250
  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";
@@ -121,18 +205,15 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
121
205
  static get supported() {
122
206
  return globalThis.BroadcastChannel != null;
123
207
  }
124
- _ctx;
208
+ _ctx = new Context(void 0, {
209
+ F: __dxlog_file2,
210
+ L: 46
211
+ });
125
212
  // Separate channels becauase the client and server may be in the same process.
126
- _serveChannel;
127
- _clientChannel;
213
+ _serveChannel = void 0;
214
+ _clientChannel = void 0;
128
215
  constructor(_channelName = DEFAULT_CHANNEL_NAME) {
129
216
  this._channelName = _channelName;
130
- this._ctx = new Context(void 0, {
131
- F: __dxlog_file2,
132
- L: 46
133
- });
134
- this._serveChannel = void 0;
135
- this._clientChannel = void 0;
136
217
  if (_DiagnosticsChannel.supported) {
137
218
  this._serveChannel = new BroadcastChannel(_channelName);
138
219
  this._clientChannel = new BroadcastChannel(_channelName);
@@ -285,124 +366,6 @@ var RemoteMetrics = class {
285
366
  }
286
367
  };
287
368
 
288
- // src/remote/tracing.ts
289
- var RemoteTracing = class {
290
- _tracing;
291
- _spanMap = /* @__PURE__ */ new Map();
292
- registerProcessor(processor) {
293
- this._tracing = processor;
294
- }
295
- flushSpan(span2) {
296
- if (!this._tracing) {
297
- return;
298
- }
299
- if (!span2.endTs) {
300
- const remoteSpan = this._tracing.startSpan({
301
- name: span2.methodName,
302
- op: span2.op ?? "function",
303
- attributes: span2.attributes
304
- });
305
- this._spanMap.set(span2, remoteSpan);
306
- } else {
307
- const remoteSpan = this._spanMap.get(span2);
308
- if (remoteSpan) {
309
- remoteSpan.end();
310
- this._spanMap.delete(span2);
311
- }
312
- }
313
- }
314
- };
315
-
316
- // src/trace-sender.ts
317
- import { Stream } from "@dxos/codec-protobuf/stream";
318
- var TraceSender = class {
319
- _traceProcessor;
320
- constructor(_traceProcessor) {
321
- this._traceProcessor = _traceProcessor;
322
- }
323
- streamTrace(request) {
324
- return new Stream(({ ctx, next }) => {
325
- const flushEvents = (resources, spans2, logs) => {
326
- const event = {
327
- resourceAdded: [],
328
- resourceRemoved: [],
329
- spanAdded: [],
330
- logAdded: []
331
- };
332
- if (resources) {
333
- for (const id of resources) {
334
- const entry = this._traceProcessor.resources.get(id);
335
- if (entry) {
336
- event.resourceAdded.push({
337
- resource: entry.data
338
- });
339
- } else {
340
- event.resourceRemoved.push({
341
- id
342
- });
343
- }
344
- }
345
- } else {
346
- for (const entry of this._traceProcessor.resources.values()) {
347
- event.resourceAdded.push({
348
- resource: entry.data
349
- });
350
- }
351
- }
352
- if (spans2) {
353
- for (const id of spans2) {
354
- const span2 = this._traceProcessor.spans.get(id);
355
- if (span2) {
356
- event.spanAdded.push({
357
- span: span2
358
- });
359
- }
360
- }
361
- } else {
362
- for (const span2 of this._traceProcessor.spans.values()) {
363
- event.spanAdded.push({
364
- span: span2
365
- });
366
- }
367
- }
368
- if (logs) {
369
- for (const log2 of logs) {
370
- event.logAdded.push({
371
- log: log2
372
- });
373
- }
374
- } else {
375
- for (const log2 of this._traceProcessor.logs) {
376
- event.logAdded.push({
377
- log: log2
378
- });
379
- }
380
- }
381
- if (event.resourceAdded.length > 0 || event.resourceRemoved.length > 0 || event.spanAdded.length > 0) {
382
- next(event);
383
- }
384
- };
385
- const flush = () => {
386
- flushEvents(subscription.dirtyResources, subscription.dirtySpans, subscription.newLogs);
387
- subscription.dirtyResources.clear();
388
- subscription.dirtySpans.clear();
389
- subscription.newLogs.length = 0;
390
- };
391
- const subscription = {
392
- flush,
393
- dirtyResources: /* @__PURE__ */ new Set(),
394
- dirtySpans: /* @__PURE__ */ new Set(),
395
- newLogs: []
396
- };
397
- this._traceProcessor.subscriptions.add(subscription);
398
- ctx.onDispose(() => {
399
- this._traceProcessor.subscriptions.delete(subscription);
400
- });
401
- flushEvents(null, null, null);
402
- });
403
- }
404
- };
405
-
406
369
  // src/weak-ref.ts
407
370
  var WeakRefMock = class {
408
371
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -437,35 +400,61 @@ var ResourceEntry = class {
437
400
  }
438
401
  };
439
402
  var MAX_RESOURCE_RECORDS = 2e3;
440
- var MAX_SPAN_RECORDS = 1e3;
441
403
  var MAX_LOG_RECORDS = 1e3;
442
- var REFRESH_INTERVAL = 1e3;
443
404
  var MAX_INFO_OBJECT_DEPTH = 8;
444
- var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
445
405
  var TraceProcessor = class {
446
406
  diagnostics = new DiagnosticsManager();
447
407
  diagnosticsChannel = new DiagnosticsChannel();
448
408
  remoteMetrics = new RemoteMetrics();
449
- remoteTracing = new RemoteTracing();
450
- subscriptions = /* @__PURE__ */ new Set();
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
+ });
440
+ }
441
+ }
442
+ return backend.startSpan(options);
443
+ }
444
+ };
445
+ }
451
446
  resources = /* @__PURE__ */ new Map();
452
447
  resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
453
448
  resourceIdList = [];
454
- spans = /* @__PURE__ */ new Map();
455
- spanIdList = [];
456
449
  logs = [];
457
450
  _instanceTag = null;
458
451
  constructor() {
459
452
  log.addProcessor(this._logProcessor.bind(this), void 0, {
460
453
  F: __dxlog_file3,
461
- L: 103,
454
+ L: 108,
462
455
  S: this,
463
456
  C: (f, a) => f(...a)
464
457
  });
465
- if (!IS_CLOUDFLARE_WORKERS) {
466
- const refreshInterval = setInterval(this.refresh.bind(this), REFRESH_INTERVAL);
467
- unrefTimeout(refreshInterval);
468
- }
469
458
  if (DiagnosticsChannel.supported) {
470
459
  this.diagnosticsChannel.serve(this.diagnostics);
471
460
  }
@@ -475,10 +464,7 @@ var TraceProcessor = class {
475
464
  this._instanceTag = tag;
476
465
  this.diagnostics.setInstanceTag(tag);
477
466
  }
478
- /**
479
- * @internal
480
- */
481
- // TODO(burdon): Comment.
467
+ /** @internal */
482
468
  createTraceResource(params) {
483
469
  const id = this.resources.size;
484
470
  const tracingContext = getTracingContext(Object.getPrototypeOf(params.instance));
@@ -499,24 +485,10 @@ var TraceProcessor = class {
499
485
  if (this.resourceIdList.length > MAX_RESOURCE_RECORDS) {
500
486
  this._clearResources();
501
487
  }
502
- this._markResourceDirty(id);
503
- }
504
- createTraceSender() {
505
- return new TraceSender(this);
506
- }
507
- traceSpan(params) {
508
- const span2 = new TracingSpan(this, params);
509
- this._flushSpan(span2);
510
- return span2;
511
488
  }
512
489
  // TODO(burdon): Not implemented.
513
490
  addLink(parent, child, opts) {
514
491
  }
515
- //
516
- // Getters
517
- //
518
- // TODO(burdon): Define type.
519
- // TODO(burdon): Reconcile with system service.
520
492
  getDiagnostics() {
521
493
  this.refresh();
522
494
  return {
@@ -524,7 +496,6 @@ var TraceProcessor = class {
524
496
  `${entry.sanitizedClassName}#${entry.data.instanceId}`,
525
497
  entry.data
526
498
  ])),
527
- spans: Array.from(this.spans.values()),
528
499
  logs: this.logs.filter((log2) => log2.level >= LogLevel.INFO)
529
500
  };
530
501
  }
@@ -579,43 +550,8 @@ var TraceProcessor = class {
579
550
  for (const key of Object.keys(tracingContext.metricsProperties)) {
580
551
  instance[key]._tick?.(time);
581
552
  }
582
- let _changed = false;
583
- const oldInfo = resource2.data.info;
584
553
  resource2.data.info = this.getResourceInfo(instance);
585
- _changed ||= !areEqualShallow(oldInfo, resource2.data.info);
586
- const oldMetrics = resource2.data.metrics;
587
554
  resource2.data.metrics = this.getResourceMetrics(instance);
588
- _changed ||= !areEqualShallow(oldMetrics, resource2.data.metrics);
589
- this._markResourceDirty(resource2.data.id);
590
- }
591
- for (const subscription of this.subscriptions) {
592
- subscription.flush();
593
- }
594
- }
595
- //
596
- // Implementation
597
- //
598
- /**
599
- * @internal
600
- */
601
- _flushSpan(runtimeSpan) {
602
- const span2 = runtimeSpan.serialize();
603
- this.spans.set(span2.id, span2);
604
- this.spanIdList.push(span2.id);
605
- if (this.spanIdList.length > MAX_SPAN_RECORDS) {
606
- this._clearSpans();
607
- }
608
- this._markSpanDirty(span2.id);
609
- this.remoteTracing.flushSpan(runtimeSpan);
610
- }
611
- _markResourceDirty(id) {
612
- for (const subscription of this.subscriptions) {
613
- subscription.dirtyResources.add(id);
614
- }
615
- }
616
- _markSpanDirty(id) {
617
- for (const subscription of this.subscriptions) {
618
- subscription.dirtySpans.add(id);
619
555
  }
620
556
  }
621
557
  _clearResources() {
@@ -624,20 +560,11 @@ var TraceProcessor = class {
624
560
  this.resources.delete(id);
625
561
  }
626
562
  }
627
- _clearSpans() {
628
- while (this.spanIdList.length > MAX_SPAN_RECORDS) {
629
- const id = this.spanIdList.shift();
630
- this.spans.delete(id);
631
- }
632
- }
633
563
  _pushLog(log2) {
634
564
  this.logs.push(log2);
635
565
  if (this.logs.length > MAX_LOG_RECORDS) {
636
566
  this.logs.shift();
637
567
  }
638
- for (const subscription of this.subscriptions) {
639
- subscription.newLogs.push(log2);
640
- }
641
568
  }
642
569
  _logProcessor = (config, entry) => {
643
570
  switch (entry.level) {
@@ -671,99 +598,6 @@ var TraceProcessor = class {
671
598
  }
672
599
  };
673
600
  };
674
- var TracingSpan = class _TracingSpan {
675
- _traceProcessor;
676
- static nextId = 0;
677
- id;
678
- parentId;
679
- methodName;
680
- resourceId;
681
- op;
682
- attributes;
683
- startTs;
684
- endTs;
685
- error;
686
- _showInBrowserTimeline;
687
- _ctx;
688
- constructor(_traceProcessor, params) {
689
- this._traceProcessor = _traceProcessor;
690
- this.parentId = null;
691
- this.resourceId = null;
692
- this.endTs = null;
693
- this.error = null;
694
- this._ctx = null;
695
- this.id = _TracingSpan.nextId++;
696
- this.methodName = params.methodName;
697
- this.resourceId = _traceProcessor.getResourceId(params.instance);
698
- this.startTs = performance.now();
699
- this._showInBrowserTimeline = params.showInBrowserTimeline;
700
- this.op = params.op;
701
- this.attributes = params.attributes ?? {};
702
- if (params.parentCtx) {
703
- this._ctx = params.parentCtx.derive({
704
- attributes: {
705
- [TRACE_SPAN_ATTRIBUTE]: this.id
706
- }
707
- });
708
- const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
709
- if (typeof parentId === "number") {
710
- this.parentId = parentId;
711
- }
712
- }
713
- }
714
- get name() {
715
- const resource2 = this._traceProcessor.resources.get(this.resourceId);
716
- return resource2 ? `${resource2.sanitizedClassName}#${resource2.data.instanceId}.${this.methodName}` : this.methodName;
717
- }
718
- get ctx() {
719
- return this._ctx;
720
- }
721
- markSuccess() {
722
- this.endTs = performance.now();
723
- this._traceProcessor._flushSpan(this);
724
- if (this._showInBrowserTimeline) {
725
- this._markInBrowserTimeline();
726
- }
727
- }
728
- markError(err) {
729
- this.endTs = performance.now();
730
- this.error = serializeError(err);
731
- this._traceProcessor._flushSpan(this);
732
- if (this._showInBrowserTimeline) {
733
- this._markInBrowserTimeline();
734
- }
735
- }
736
- serialize() {
737
- return {
738
- id: this.id,
739
- resourceId: this.resourceId ?? void 0,
740
- methodName: this.methodName,
741
- parentId: this.parentId ?? void 0,
742
- startTs: this.startTs.toFixed(3),
743
- endTs: this.endTs?.toFixed(3) ?? void 0,
744
- error: this.error ?? void 0
745
- };
746
- }
747
- _markInBrowserTimeline() {
748
- if (typeof globalThis?.performance?.measure === "function") {
749
- performance.measure(this.name, {
750
- start: this.startTs,
751
- end: this.endTs
752
- });
753
- }
754
- }
755
- };
756
- var serializeError = (err) => {
757
- if (err instanceof Error) {
758
- return {
759
- name: err.name,
760
- message: err.message
761
- };
762
- }
763
- return {
764
- message: String(err)
765
- };
766
- };
767
601
  var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
768
602
  var sanitizeValue = (value, depth, traceProcessor) => {
769
603
  switch (typeof value) {
@@ -810,33 +644,46 @@ var sanitizeValue = (value, depth, traceProcessor) => {
810
644
  return value.toString();
811
645
  }
812
646
  };
813
- var areEqualShallow = (a, b) => {
814
- for (const key in a) {
815
- if (!(key in b) || a[key] !== b[key]) {
816
- return false;
817
- }
818
- }
819
- for (const key in b) {
820
- if (!(key in a) || a[key] !== b[key]) {
821
- return false;
822
- }
823
- }
824
- return true;
825
- };
826
647
  var sanitizeClassName = (className) => {
648
+ let name = className.replace(/^_+/, "");
827
649
  const SANITIZE_REGEX = /[^_](\d+)$/;
828
- const m = className.match(SANITIZE_REGEX);
829
- if (!m) {
830
- return className;
831
- } else {
832
- 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);
833
653
  }
654
+ return name;
834
655
  };
835
656
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
836
657
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
837
658
 
838
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
+ };
839
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
+ }
840
687
  const klass = /* @__PURE__ */ (() => class extends constructor {
841
688
  constructor(...rest) {
842
689
  super(...rest);
@@ -847,6 +694,68 @@ var resource = (options) => (constructor) => {
847
694
  });
848
695
  }
849
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
+ }
850
759
  Object.defineProperty(klass, "name", {
851
760
  value: constructor.name
852
761
  });
@@ -860,45 +769,117 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
860
769
  var mark = (name) => {
861
770
  performance.mark(name);
862
771
  };
863
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
772
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
864
773
  const method = descriptor.value;
865
774
  descriptor.value = async function(...args) {
866
775
  const parentCtx = args[0] instanceof Context2 ? args[0] : null;
867
- const span2 = TRACE_PROCESSOR.traceSpan({
868
- parentCtx,
869
- methodName: propertyKey,
870
- instance: this,
871
- showInBrowserTimeline,
872
- op,
873
- attributes
874
- });
875
- const callArgs = span2.ctx ? [
876
- span2.ctx,
877
- ...args.slice(1)
878
- ] : 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
+ }
879
809
  try {
880
810
  return await method.apply(this, callArgs);
881
811
  } catch (err) {
882
- span2.markError(err);
812
+ remoteSpan?.setError?.(err);
883
813
  throw err;
884
814
  } finally {
885
- 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
+ }
886
822
  }
887
823
  };
888
824
  };
889
- var spans = /* @__PURE__ */ new Map();
825
+ var manualSpans = /* @__PURE__ */ new Map();
826
+ var manualSpanTimestamps = /* @__PURE__ */ new Map();
890
827
  var spanStart = (params) => {
891
- if (spans.has(params.id)) {
892
- 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
+ }
893
853
  }
894
- const span2 = TRACE_PROCESSOR.traceSpan(params);
895
- spans.set(params.id, span2);
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
+ });
867
+ }
868
+ return params.parentCtx;
896
869
  };
897
870
  var spanEnd = (id) => {
898
- const span2 = spans.get(id);
899
- if (span2) {
900
- span2.markSuccess();
901
- 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);
902
883
  }
903
884
  };
904
885
  var metricsCounter = () => (target, propertyKey, descriptor) => {
@@ -1116,16 +1097,13 @@ export {
1116
1097
  DiagnosticsManager,
1117
1098
  MapCounter,
1118
1099
  RemoteMetrics,
1119
- RemoteTracing,
1120
1100
  ResourceEntry,
1101
+ TRACE_ALL_KEY,
1121
1102
  TRACE_PROCESSOR,
1122
- TRACE_SPAN_ATTRIBUTE,
1123
1103
  TimeSeriesCounter,
1124
1104
  TimeUsageCounter,
1125
1105
  TraceDiagnosticImpl,
1126
1106
  TraceProcessor,
1127
- TraceSender,
1128
- TracingSpan,
1129
1107
  UnaryCounter,
1130
1108
  getTracingContext,
1131
1109
  sanitizeClassName,