@dxos/tracing 0.8.4-main.fffef41 → 0.8.4-staging.ac66bdf99f

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 +328 -339
  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 +328 -339
  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 +58 -258
  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
- import { LogLevel, getContextFromEntry, log } from "@dxos/log";
14
+ import { LogLevel, log } from "@dxos/log";
17
15
  import { getPrototypeSpecificInstanceId } from "@dxos/util";
18
16
 
17
+ // src/buffering-backend.ts
18
+ var BUFFERED_PREFIX = "buffered-";
19
+ var BufferedSpan = class {
20
+ options;
21
+ spanContext;
22
+ startTime;
23
+ delegate;
24
+ #ended = false;
25
+ #endTime;
26
+ #error;
27
+ #hasError = false;
28
+ constructor(options, id) {
29
+ this.options = options;
30
+ this.spanContext = {
31
+ traceparent: `${BUFFERED_PREFIX}${id}`
32
+ };
33
+ this.startTime = Date.now();
34
+ }
35
+ end(endTime) {
36
+ if (this.delegate) {
37
+ this.delegate.end(endTime);
38
+ return;
39
+ }
40
+ this.#endTime = endTime ?? Date.now();
41
+ this.#ended = true;
42
+ }
43
+ setError(err) {
44
+ if (this.delegate) {
45
+ this.delegate.setError?.(err);
46
+ return;
47
+ }
48
+ this.#error = err;
49
+ this.#hasError = true;
50
+ }
51
+ replay(real) {
52
+ if (this.#hasError) {
53
+ real.setError?.(this.#error);
54
+ }
55
+ if (this.#ended) {
56
+ real.end(this.#endTime);
57
+ } else {
58
+ this.delegate = real;
59
+ }
60
+ }
61
+ };
62
+ var BufferingTracingBackend = class {
63
+ #pending = [];
64
+ #counter = 0;
65
+ startSpan(options) {
66
+ const span2 = new BufferedSpan(options, ++this.#counter);
67
+ this.#pending.push(span2);
68
+ return span2;
69
+ }
70
+ /** Discard all buffered spans without replaying them. */
71
+ clear() {
72
+ this.#pending.length = 0;
73
+ }
74
+ /**
75
+ * Replay all buffered spans into {@link backend}.
76
+ *
77
+ * @returns Map from synthetic buffered traceparent to real {@link TraceContextData},
78
+ * used by the post-drain translating wrapper to resolve stale buffered IDs
79
+ * still present on in-flight {@link Context} objects.
80
+ */
81
+ drain(backend) {
82
+ const idMap = /* @__PURE__ */ new Map();
83
+ for (const buffered of this.#pending) {
84
+ let parentContext = buffered.options.parentContext;
85
+ if (parentContext && parentContext.traceparent.startsWith(BUFFERED_PREFIX)) {
86
+ parentContext = idMap.get(parentContext.traceparent) ?? parentContext;
87
+ }
88
+ const real = backend.startSpan({
89
+ ...buffered.options,
90
+ parentContext,
91
+ startTime: buffered.startTime
92
+ });
93
+ if (real.spanContext) {
94
+ idMap.set(buffered.spanContext.traceparent, real.spanContext);
95
+ }
96
+ buffered.replay(real);
97
+ }
98
+ this.#pending.length = 0;
99
+ return idMap;
100
+ }
101
+ };
102
+
19
103
  // src/diagnostic.ts
20
104
  import { asyncTimeout } from "@dxos/async";
21
105
  import { invariant } from "@dxos/invariant";
@@ -282,124 +366,6 @@ var RemoteMetrics = class {
282
366
  }
283
367
  };
284
368
 
285
- // src/remote/tracing.ts
286
- var RemoteTracing = class {
287
- _tracing;
288
- _spanMap = /* @__PURE__ */ new Map();
289
- registerProcessor(processor) {
290
- this._tracing = processor;
291
- }
292
- flushSpan(span2) {
293
- if (!this._tracing) {
294
- return;
295
- }
296
- if (!span2.endTs) {
297
- const remoteSpan = this._tracing.startSpan({
298
- name: span2.methodName,
299
- op: span2.op ?? "function",
300
- attributes: span2.attributes
301
- });
302
- this._spanMap.set(span2, remoteSpan);
303
- } else {
304
- const remoteSpan = this._spanMap.get(span2);
305
- if (remoteSpan) {
306
- remoteSpan.end();
307
- this._spanMap.delete(span2);
308
- }
309
- }
310
- }
311
- };
312
-
313
- // src/trace-sender.ts
314
- import { Stream } from "@dxos/codec-protobuf/stream";
315
- var TraceSender = class {
316
- _traceProcessor;
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
369
  // src/weak-ref.ts
404
370
  var WeakRefMock = class {
405
371
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -434,35 +400,61 @@ var ResourceEntry = class {
434
400
  }
435
401
  };
436
402
  var MAX_RESOURCE_RECORDS = 2e3;
437
- var MAX_SPAN_RECORDS = 1e3;
438
403
  var MAX_LOG_RECORDS = 1e3;
439
- var REFRESH_INTERVAL = 1e3;
440
404
  var MAX_INFO_OBJECT_DEPTH = 8;
441
- var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
442
405
  var TraceProcessor = class {
443
406
  diagnostics = new DiagnosticsManager();
444
407
  diagnosticsChannel = new DiagnosticsChannel();
445
408
  remoteMetrics = new RemoteMetrics();
446
- remoteTracing = new RemoteTracing();
447
- 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
+ }
448
446
  resources = /* @__PURE__ */ new Map();
449
447
  resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
450
448
  resourceIdList = [];
451
- spans = /* @__PURE__ */ new Map();
452
- spanIdList = [];
453
449
  logs = [];
454
450
  _instanceTag = null;
455
451
  constructor() {
456
452
  log.addProcessor(this._logProcessor.bind(this), void 0, {
457
453
  F: __dxlog_file3,
458
- L: 103,
454
+ L: 108,
459
455
  S: this,
460
456
  C: (f, a) => f(...a)
461
457
  });
462
- if (!IS_CLOUDFLARE_WORKERS) {
463
- const refreshInterval = setInterval(this.refresh.bind(this), REFRESH_INTERVAL);
464
- unrefTimeout(refreshInterval);
465
- }
466
458
  if (DiagnosticsChannel.supported) {
467
459
  this.diagnosticsChannel.serve(this.diagnostics);
468
460
  }
@@ -472,10 +464,7 @@ var TraceProcessor = class {
472
464
  this._instanceTag = tag;
473
465
  this.diagnostics.setInstanceTag(tag);
474
466
  }
475
- /**
476
- * @internal
477
- */
478
- // TODO(burdon): Comment.
467
+ /** @internal */
479
468
  createTraceResource(params) {
480
469
  const id = this.resources.size;
481
470
  const tracingContext = getTracingContext(Object.getPrototypeOf(params.instance));
@@ -496,24 +485,10 @@ var TraceProcessor = class {
496
485
  if (this.resourceIdList.length > MAX_RESOURCE_RECORDS) {
497
486
  this._clearResources();
498
487
  }
499
- this._markResourceDirty(id);
500
- }
501
- createTraceSender() {
502
- return new TraceSender(this);
503
- }
504
- traceSpan(params) {
505
- const span2 = new TracingSpan(this, params);
506
- this._flushSpan(span2);
507
- return span2;
508
488
  }
509
489
  // TODO(burdon): Not implemented.
510
490
  addLink(parent, child, opts) {
511
491
  }
512
- //
513
- // Getters
514
- //
515
- // TODO(burdon): Define type.
516
- // TODO(burdon): Reconcile with system service.
517
492
  getDiagnostics() {
518
493
  this.refresh();
519
494
  return {
@@ -521,7 +496,6 @@ var TraceProcessor = class {
521
496
  `${entry.sanitizedClassName}#${entry.data.instanceId}`,
522
497
  entry.data
523
498
  ])),
524
- spans: Array.from(this.spans.values()),
525
499
  logs: this.logs.filter((log2) => log2.level >= LogLevel.INFO)
526
500
  };
527
501
  }
@@ -576,43 +550,8 @@ var TraceProcessor = class {
576
550
  for (const key of Object.keys(tracingContext.metricsProperties)) {
577
551
  instance[key]._tick?.(time);
578
552
  }
579
- let _changed = false;
580
- const oldInfo = resource2.data.info;
581
553
  resource2.data.info = this.getResourceInfo(instance);
582
- _changed ||= !areEqualShallow(oldInfo, resource2.data.info);
583
- const oldMetrics = resource2.data.metrics;
584
554
  resource2.data.metrics = this.getResourceMetrics(instance);
585
- _changed ||= !areEqualShallow(oldMetrics, resource2.data.metrics);
586
- this._markResourceDirty(resource2.data.id);
587
- }
588
- for (const subscription of this.subscriptions) {
589
- subscription.flush();
590
- }
591
- }
592
- //
593
- // Implementation
594
- //
595
- /**
596
- * @internal
597
- */
598
- _flushSpan(runtimeSpan) {
599
- const span2 = runtimeSpan.serialize();
600
- this.spans.set(span2.id, span2);
601
- this.spanIdList.push(span2.id);
602
- if (this.spanIdList.length > MAX_SPAN_RECORDS) {
603
- this._clearSpans();
604
- }
605
- this._markSpanDirty(span2.id);
606
- this.remoteTracing.flushSpan(runtimeSpan);
607
- }
608
- _markResourceDirty(id) {
609
- for (const subscription of this.subscriptions) {
610
- subscription.dirtyResources.add(id);
611
- }
612
- }
613
- _markSpanDirty(id) {
614
- for (const subscription of this.subscriptions) {
615
- subscription.dirtySpans.add(id);
616
555
  }
617
556
  }
618
557
  _clearResources() {
@@ -621,20 +560,11 @@ var TraceProcessor = class {
621
560
  this.resources.delete(id);
622
561
  }
623
562
  }
624
- _clearSpans() {
625
- while (this.spanIdList.length > MAX_SPAN_RECORDS) {
626
- const id = this.spanIdList.shift();
627
- this.spans.delete(id);
628
- }
629
- }
630
563
  _pushLog(log2) {
631
564
  this.logs.push(log2);
632
565
  if (this.logs.length > MAX_LOG_RECORDS) {
633
566
  this.logs.shift();
634
567
  }
635
- for (const subscription of this.subscriptions) {
636
- subscription.newLogs.push(log2);
637
- }
638
568
  }
639
569
  _logProcessor = (config, entry) => {
640
570
  switch (entry.level) {
@@ -646,18 +576,21 @@ var TraceProcessor = class {
646
576
  if (!resource2) {
647
577
  return;
648
578
  }
649
- const context = getContextFromEntry(entry) ?? {};
650
- for (const key of Object.keys(context)) {
651
- context[key] = sanitizeValue(context[key], 0, this);
579
+ const context = {
580
+ ...entry.computedContext
581
+ };
582
+ if (entry.computedError !== void 0) {
583
+ context.error = entry.computedError;
652
584
  }
585
+ const { filename, line } = entry.computedMeta;
653
586
  const entryToPush = {
654
587
  level: entry.level,
655
- message: entry.message ?? (entry.error ? entry.error.message ?? String(entry.error) : ""),
588
+ message: entry.message ?? entry.computedError ?? "",
656
589
  context,
657
- timestamp: /* @__PURE__ */ new Date(),
590
+ timestamp: new Date(entry.timestamp),
658
591
  meta: {
659
- file: entry.meta?.F ?? "",
660
- line: entry.meta?.L ?? 0,
592
+ file: filename ?? "",
593
+ line: line ?? 0,
661
594
  resourceId: resource2.data.id
662
595
  }
663
596
  };
@@ -668,94 +601,6 @@ var TraceProcessor = class {
668
601
  }
669
602
  };
670
603
  };
671
- var TracingSpan = class _TracingSpan {
672
- _traceProcessor;
673
- static nextId = 0;
674
- id;
675
- parentId = null;
676
- methodName;
677
- resourceId = null;
678
- op;
679
- attributes;
680
- startTs;
681
- endTs = null;
682
- error = null;
683
- _showInBrowserTimeline;
684
- _ctx = null;
685
- constructor(_traceProcessor, params) {
686
- this._traceProcessor = _traceProcessor;
687
- this.id = _TracingSpan.nextId++;
688
- this.methodName = params.methodName;
689
- this.resourceId = _traceProcessor.getResourceId(params.instance);
690
- this.startTs = performance.now();
691
- this._showInBrowserTimeline = params.showInBrowserTimeline;
692
- this.op = params.op;
693
- this.attributes = params.attributes ?? {};
694
- if (params.parentCtx) {
695
- this._ctx = params.parentCtx.derive({
696
- attributes: {
697
- [TRACE_SPAN_ATTRIBUTE]: this.id
698
- }
699
- });
700
- const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
701
- if (typeof parentId === "number") {
702
- this.parentId = parentId;
703
- }
704
- }
705
- }
706
- get name() {
707
- const resource2 = this._traceProcessor.resources.get(this.resourceId);
708
- return resource2 ? `${resource2.sanitizedClassName}#${resource2.data.instanceId}.${this.methodName}` : this.methodName;
709
- }
710
- get ctx() {
711
- return this._ctx;
712
- }
713
- markSuccess() {
714
- this.endTs = performance.now();
715
- this._traceProcessor._flushSpan(this);
716
- if (this._showInBrowserTimeline) {
717
- this._markInBrowserTimeline();
718
- }
719
- }
720
- markError(err) {
721
- this.endTs = performance.now();
722
- this.error = serializeError(err);
723
- this._traceProcessor._flushSpan(this);
724
- if (this._showInBrowserTimeline) {
725
- this._markInBrowserTimeline();
726
- }
727
- }
728
- serialize() {
729
- return {
730
- id: this.id,
731
- resourceId: this.resourceId ?? void 0,
732
- methodName: this.methodName,
733
- parentId: this.parentId ?? void 0,
734
- startTs: this.startTs.toFixed(3),
735
- endTs: this.endTs?.toFixed(3) ?? void 0,
736
- error: this.error ?? void 0
737
- };
738
- }
739
- _markInBrowserTimeline() {
740
- if (typeof globalThis?.performance?.measure === "function") {
741
- performance.measure(this.name, {
742
- start: this.startTs,
743
- end: this.endTs
744
- });
745
- }
746
- }
747
- };
748
- var serializeError = (err) => {
749
- if (err instanceof Error) {
750
- return {
751
- name: err.name,
752
- message: err.message
753
- };
754
- }
755
- return {
756
- message: String(err)
757
- };
758
- };
759
604
  var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
760
605
  var sanitizeValue = (value, depth, traceProcessor) => {
761
606
  switch (typeof value) {
@@ -802,33 +647,46 @@ var sanitizeValue = (value, depth, traceProcessor) => {
802
647
  return value.toString();
803
648
  }
804
649
  };
805
- var areEqualShallow = (a, b) => {
806
- for (const key in a) {
807
- if (!(key in b) || a[key] !== b[key]) {
808
- return false;
809
- }
810
- }
811
- for (const key in b) {
812
- if (!(key in a) || a[key] !== b[key]) {
813
- return false;
814
- }
815
- }
816
- return true;
817
- };
818
650
  var sanitizeClassName = (className) => {
651
+ let name = className.replace(/^_+/, "");
819
652
  const SANITIZE_REGEX = /[^_](\d+)$/;
820
- const m = className.match(SANITIZE_REGEX);
821
- if (!m) {
822
- return className;
823
- } else {
824
- return className.slice(0, -m[1].length);
653
+ const m = name.match(SANITIZE_REGEX);
654
+ if (m) {
655
+ name = name.slice(0, -m[1].length);
825
656
  }
657
+ return name;
826
658
  };
827
659
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
828
660
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
829
661
 
830
662
  // src/api.ts
663
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/common/tracing/src/api.ts";
664
+ var LIFECYCLE_SPAN = /* @__PURE__ */ Symbol("dxos.tracing.lifecycle-span");
665
+ var TRACE_ALL_KEY = "dxos.debug.traceAll";
666
+ var collectSpanAttributes = (instance, spanAttributes) => {
667
+ const proto = Object.getPrototypeOf(instance);
668
+ if (!proto) {
669
+ return;
670
+ }
671
+ const tracingContext = getTracingContext(proto);
672
+ for (const [key, { options }] of Object.entries(tracingContext.infoProperties)) {
673
+ if (!options.spanAttribute) {
674
+ continue;
675
+ }
676
+ try {
677
+ const value = typeof instance[key] === "function" ? instance[key]() : instance[key];
678
+ if (value != null) {
679
+ const resolved = options.enum ? options.enum[value] : String(value);
680
+ spanAttributes[`ctx.${key}`] = resolved;
681
+ }
682
+ } catch {
683
+ }
684
+ }
685
+ };
831
686
  var resource = (options) => (constructor) => {
687
+ if (options?.lifecycle && !(constructor.prototype instanceof Resource)) {
688
+ throw new Error(`@trace.resource({ lifecycle: true }) requires ${constructor.name} to extend Resource`);
689
+ }
832
690
  const klass = /* @__PURE__ */ (() => class extends constructor {
833
691
  constructor(...rest) {
834
692
  super(...rest);
@@ -839,6 +697,68 @@ var resource = (options) => (constructor) => {
839
697
  });
840
698
  }
841
699
  })();
700
+ if (options?.lifecycle) {
701
+ const sanitizedName = sanitizeClassName(constructor.name);
702
+ const proto = klass.prototype;
703
+ const originalOpen = proto.open;
704
+ const originalClose = proto.close;
705
+ proto.open = async function(ctx) {
706
+ const self = this;
707
+ if (self._lifecycleState !== LifecycleState.CLOSED) {
708
+ return originalOpen.call(this, ctx);
709
+ }
710
+ const parentSpanContext = ctx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
711
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
712
+ const spanAttributes = {};
713
+ if (resourceEntry) {
714
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
715
+ }
716
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend?.startSpan({
717
+ name: `${sanitizedName}.lifecycle`,
718
+ op: "lifecycle",
719
+ attributes: spanAttributes,
720
+ parentContext: parentSpanContext
721
+ });
722
+ self[LIFECYCLE_SPAN] = remoteSpan;
723
+ let openCtx = ctx;
724
+ if (remoteSpan?.spanContext != null) {
725
+ const traceAttrs = {
726
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
727
+ };
728
+ openCtx = ctx ? ctx.derive({
729
+ attributes: traceAttrs
730
+ }) : new Context2({
731
+ attributes: traceAttrs
732
+ }, {
733
+ F: __dxlog_file4,
734
+ L: 104
735
+ });
736
+ }
737
+ try {
738
+ return await originalOpen.call(this, openCtx);
739
+ } catch (err) {
740
+ remoteSpan?.setError?.(err);
741
+ remoteSpan?.end();
742
+ self[LIFECYCLE_SPAN] = void 0;
743
+ throw err;
744
+ }
745
+ };
746
+ proto.close = async function(ctx) {
747
+ const self = this;
748
+ const remoteSpan = self[LIFECYCLE_SPAN];
749
+ try {
750
+ return await originalClose.call(this, ctx);
751
+ } catch (err) {
752
+ remoteSpan?.setError?.(err);
753
+ throw err;
754
+ } finally {
755
+ if (remoteSpan) {
756
+ remoteSpan.end();
757
+ self[LIFECYCLE_SPAN] = void 0;
758
+ }
759
+ }
760
+ };
761
+ }
842
762
  Object.defineProperty(klass, "name", {
843
763
  value: constructor.name
844
764
  });
@@ -852,45 +772,117 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
852
772
  var mark = (name) => {
853
773
  performance.mark(name);
854
774
  };
855
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
775
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
856
776
  const method = descriptor.value;
857
777
  descriptor.value = async function(...args) {
858
778
  const parentCtx = args[0] instanceof Context2 ? args[0] : null;
859
- const span2 = TRACE_PROCESSOR.traceSpan({
860
- parentCtx,
861
- methodName: propertyKey,
862
- instance: this,
863
- showInBrowserTimeline,
864
- op,
865
- attributes
866
- });
867
- const callArgs = span2.ctx ? [
868
- span2.ctx,
869
- ...args.slice(1)
870
- ] : args;
779
+ const startTs = performance.now();
780
+ const parentSpanContext = parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
781
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
782
+ const className = resourceEntry?.sanitizedClassName ?? sanitizeClassName(target.constructor?.name ?? "unknown");
783
+ const spanName = `${className}.${propertyKey}`;
784
+ const spanAttributes = {};
785
+ if (resourceEntry) {
786
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
787
+ }
788
+ collectSpanAttributes(this, spanAttributes);
789
+ if (attributes) {
790
+ for (const [key, value] of Object.entries(attributes)) {
791
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
792
+ }
793
+ }
794
+ const remoteSpan = showInRemoteTracing ? TRACE_PROCESSOR.tracingBackend?.startSpan({
795
+ name: spanName,
796
+ op: op ?? "function",
797
+ attributes: spanAttributes,
798
+ parentContext: parentSpanContext
799
+ }) : void 0;
800
+ let callArgs = args;
801
+ if (parentCtx) {
802
+ const childCtx = remoteSpan?.spanContext != null ? parentCtx.derive({
803
+ attributes: {
804
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
805
+ }
806
+ }) : parentCtx.derive();
807
+ callArgs = [
808
+ childCtx,
809
+ ...args.slice(1)
810
+ ];
811
+ }
871
812
  try {
872
813
  return await method.apply(this, callArgs);
873
814
  } catch (err) {
874
- span2.markError(err);
815
+ remoteSpan?.setError?.(err);
875
816
  throw err;
876
817
  } finally {
877
- span2.markSuccess();
818
+ remoteSpan?.end();
819
+ if (showInBrowserTimeline && typeof globalThis?.performance?.measure === "function") {
820
+ performance.measure(spanName, {
821
+ start: startTs,
822
+ end: performance.now()
823
+ });
824
+ }
878
825
  }
879
826
  };
880
827
  };
881
- var spans = /* @__PURE__ */ new Map();
828
+ var manualSpans = /* @__PURE__ */ new Map();
829
+ var manualSpanTimestamps = /* @__PURE__ */ new Map();
882
830
  var spanStart = (params) => {
883
- if (spans.has(params.id)) {
884
- return;
831
+ if (manualSpans.has(params.id) || manualSpanTimestamps.has(params.id)) {
832
+ return params.parentCtx;
833
+ }
834
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(params.instance);
835
+ const className = resourceEntry?.sanitizedClassName ?? "unknown";
836
+ const spanName = `${className}.${params.methodName}`;
837
+ if (params.showInBrowserTimeline) {
838
+ manualSpanTimestamps.set(params.id, {
839
+ name: spanName,
840
+ startTs: performance.now()
841
+ });
842
+ }
843
+ if (params.showInRemoteTracing === false || !TRACE_PROCESSOR.tracingBackend) {
844
+ return params.parentCtx;
845
+ }
846
+ const parentSpanContext = params.parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
847
+ const spanAttributes = {};
848
+ if (resourceEntry) {
849
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
850
+ }
851
+ collectSpanAttributes(params.instance, spanAttributes);
852
+ if (params.attributes) {
853
+ for (const [key, value] of Object.entries(params.attributes)) {
854
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
855
+ }
885
856
  }
886
- const span2 = TRACE_PROCESSOR.traceSpan(params);
887
- spans.set(params.id, span2);
857
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend.startSpan({
858
+ name: spanName,
859
+ op: params.op ?? "function",
860
+ attributes: spanAttributes,
861
+ parentContext: parentSpanContext
862
+ });
863
+ manualSpans.set(params.id, remoteSpan);
864
+ if (params.parentCtx && remoteSpan.spanContext != null) {
865
+ return params.parentCtx.derive({
866
+ attributes: {
867
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
868
+ }
869
+ });
870
+ }
871
+ return params.parentCtx;
888
872
  };
889
873
  var spanEnd = (id) => {
890
- const span2 = spans.get(id);
891
- if (span2) {
892
- span2.markSuccess();
893
- spans.delete(id);
874
+ const remoteSpan = manualSpans.get(id);
875
+ if (remoteSpan) {
876
+ remoteSpan.end();
877
+ manualSpans.delete(id);
878
+ }
879
+ const timestamps = manualSpanTimestamps.get(id);
880
+ if (timestamps && typeof globalThis?.performance?.measure === "function") {
881
+ performance.measure(timestamps.name, {
882
+ start: timestamps.startTs,
883
+ end: performance.now()
884
+ });
885
+ manualSpanTimestamps.delete(id);
894
886
  }
895
887
  };
896
888
  var metricsCounter = () => (target, propertyKey, descriptor) => {
@@ -1108,16 +1100,13 @@ export {
1108
1100
  DiagnosticsManager,
1109
1101
  MapCounter,
1110
1102
  RemoteMetrics,
1111
- RemoteTracing,
1112
1103
  ResourceEntry,
1104
+ TRACE_ALL_KEY,
1113
1105
  TRACE_PROCESSOR,
1114
- TRACE_SPAN_ATTRIBUTE,
1115
1106
  TimeSeriesCounter,
1116
1107
  TimeUsageCounter,
1117
1108
  TraceDiagnosticImpl,
1118
1109
  TraceProcessor,
1119
- TraceSender,
1120
- TracingSpan,
1121
1110
  UnaryCounter,
1122
1111
  getTracingContext,
1123
1112
  sanitizeClassName,