@dxos/tracing 0.8.4-main.d05673bc65 → 0.8.4-main.e00bdcdb52

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 (45) hide show
  1. package/dist/lib/browser/index.mjs +330 -392
  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 +330 -392
  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 +34 -15
  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.map +1 -1
  12. package/dist/types/src/diagnostics-channel.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/metrics/base.d.ts.map +1 -1
  16. package/dist/types/src/metrics/custom-counter.d.ts.map +1 -1
  17. package/dist/types/src/metrics/map-counter.d.ts.map +1 -1
  18. package/dist/types/src/metrics/time-series-counter.d.ts.map +1 -1
  19. package/dist/types/src/metrics/time-usage-counter.d.ts.map +1 -1
  20. package/dist/types/src/metrics/unary-counter.d.ts.map +1 -1
  21. package/dist/types/src/remote/index.d.ts +0 -1
  22. package/dist/types/src/remote/index.d.ts.map +1 -1
  23. package/dist/types/src/remote/metrics.d.ts.map +1 -1
  24. package/dist/types/src/symbols.d.ts +0 -1
  25. package/dist/types/src/symbols.d.ts.map +1 -1
  26. package/dist/types/src/trace-processor.d.ts +15 -51
  27. package/dist/types/src/trace-processor.d.ts.map +1 -1
  28. package/dist/types/src/tracing-types.d.ts +67 -0
  29. package/dist/types/src/tracing-types.d.ts.map +1 -0
  30. package/dist/types/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +9 -12
  32. package/src/api.ts +235 -33
  33. package/src/buffering-backend.ts +112 -0
  34. package/src/index.ts +1 -2
  35. package/src/remote/index.ts +0 -1
  36. package/src/symbols.ts +0 -2
  37. package/src/trace-processor.ts +56 -256
  38. package/src/tracing-types.ts +77 -0
  39. package/src/tracing.test.ts +513 -4
  40. package/dist/types/src/remote/tracing.d.ts +0 -23
  41. package/dist/types/src/remote/tracing.d.ts.map +0 -1
  42. package/dist/types/src/trace-sender.d.ts +0 -9
  43. package/dist/types/src/trace-sender.d.ts.map +0 -1
  44. package/src/remote/tracing.ts +0 -53
  45. package/src/trace-sender.ts +0 -88
@@ -1,5 +1,5 @@
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
5
  var symbolTracingContext = /* @__PURE__ */ Symbol("dxos.tracing.context");
@@ -9,13 +9,97 @@ var getTracingContext = (target) => {
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";
@@ -70,27 +154,11 @@ var DiagnosticsManager = class {
70
154
  }
71
155
  async fetch(request) {
72
156
  if (request.instanceId != null) {
73
- invariant(request.instanceId === this.instanceId, "Invalid instance id", {
74
- F: __dxlog_file,
75
- L: 82,
76
- S: this,
77
- A: [
78
- "request.instanceId === this.instanceId",
79
- "'Invalid instance id'"
80
- ]
81
- });
157
+ invariant(request.instanceId === this.instanceId, "Invalid instance id", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 52, S: this, A: ["request.instanceId === this.instanceId", "'Invalid instance id'"] });
82
158
  }
83
159
  const { id } = request;
84
160
  const diagnostic2 = this.registry.get(id);
85
- invariant(diagnostic2, "Diagnostic not found", {
86
- F: __dxlog_file,
87
- L: 86,
88
- S: this,
89
- A: [
90
- "diagnostic",
91
- "'Diagnostic not found'"
92
- ]
93
- });
161
+ invariant(diagnostic2, "Diagnostic not found", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 56, S: this, A: ["diagnostic", "'Diagnostic not found'"] });
94
162
  try {
95
163
  const data = await asyncTimeout(diagnostic2.fetch(), DIAGNOSTICS_TIMEOUT);
96
164
  return {
@@ -121,10 +189,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
121
189
  static get supported() {
122
190
  return globalThis.BroadcastChannel != null;
123
191
  }
124
- _ctx = new Context(void 0, {
125
- F: __dxlog_file2,
126
- L: 46
127
- });
192
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 16 });
128
193
  // Separate channels becauase the client and server may be in the same process.
129
194
  _serveChannel = void 0;
130
195
  _clientChannel = void 0;
@@ -152,15 +217,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
152
217
  }
153
218
  }
154
219
  serve(manager) {
155
- invariant2(this._serveChannel, void 0, {
156
- F: __dxlog_file2,
157
- L: 78,
158
- S: this,
159
- A: [
160
- "this._serveChannel",
161
- ""
162
- ]
163
- });
220
+ invariant2(this._serveChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 43, S: this, A: ["this._serveChannel", ""] });
164
221
  const listener = async (event) => {
165
222
  switch (event.data.type) {
166
223
  case "DIAGNOSTICS_DISCOVER": {
@@ -192,15 +249,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
192
249
  this._ctx.onDispose(() => this._serveChannel.removeEventListener("message", listener));
193
250
  }
194
251
  async discover() {
195
- invariant2(this._clientChannel, void 0, {
196
- F: __dxlog_file2,
197
- L: 114,
198
- S: this,
199
- A: [
200
- "this._clientChannel",
201
- ""
202
- ]
203
- });
252
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 77, S: this, A: ["this._clientChannel", ""] });
204
253
  const diagnostics = [];
205
254
  const collector = (event) => {
206
255
  const data = event.data;
@@ -228,15 +277,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
228
277
  }
229
278
  }
230
279
  async fetch(request) {
231
- invariant2(this._clientChannel, void 0, {
232
- F: __dxlog_file2,
233
- L: 147,
234
- S: this,
235
- A: [
236
- "this._clientChannel",
237
- ""
238
- ]
239
- });
280
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 106, S: this, A: ["this._clientChannel", ""] });
240
281
  const requestId = createId();
241
282
  const trigger = new Trigger();
242
283
  const listener = (event) => {
@@ -282,124 +323,6 @@ var RemoteMetrics = class {
282
323
  }
283
324
  };
284
325
 
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
326
  // src/weak-ref.ts
404
327
  var WeakRefMock = class {
405
328
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -434,35 +357,56 @@ var ResourceEntry = class {
434
357
  }
435
358
  };
436
359
  var MAX_RESOURCE_RECORDS = 2e3;
437
- var MAX_SPAN_RECORDS = 1e3;
438
360
  var MAX_LOG_RECORDS = 1e3;
439
- var REFRESH_INTERVAL = 1e3;
440
361
  var MAX_INFO_OBJECT_DEPTH = 8;
441
- var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
442
362
  var TraceProcessor = class {
443
363
  diagnostics = new DiagnosticsManager();
444
364
  diagnosticsChannel = new DiagnosticsChannel();
445
365
  remoteMetrics = new RemoteMetrics();
446
- remoteTracing = new RemoteTracing();
447
- subscriptions = /* @__PURE__ */ new Set();
366
+ #bufferingBackend = new BufferingTracingBackend();
367
+ #activeBackend = this.#bufferingBackend;
368
+ /**
369
+ * Tracing backend. Initially a buffering backend that records spans;
370
+ * once the observability package sets a real backend, the buffer is drained
371
+ * and a thin translating wrapper is installed that resolves stale buffered
372
+ * parent IDs still held by in-flight {@link Context} objects.
373
+ *
374
+ * The wrapper only allocates when a `buffered-*` parent is actually encountered;
375
+ * the common path is a single `startsWith` check and direct passthrough.
376
+ */
377
+ get tracingBackend() {
378
+ return this.#activeBackend;
379
+ }
380
+ set tracingBackend(backend) {
381
+ if (!backend || backend === this.#bufferingBackend) {
382
+ this.#bufferingBackend.clear();
383
+ this.#activeBackend = this.#bufferingBackend;
384
+ return;
385
+ }
386
+ const idMap = this.#bufferingBackend.drain(backend);
387
+ this.#activeBackend = {
388
+ startSpan: (options) => {
389
+ const parent = options.parentContext;
390
+ if (parent?.traceparent.startsWith(BUFFERED_PREFIX)) {
391
+ const translated = idMap.get(parent.traceparent);
392
+ if (translated) {
393
+ return backend.startSpan({
394
+ ...options,
395
+ parentContext: translated
396
+ });
397
+ }
398
+ }
399
+ return backend.startSpan(options);
400
+ }
401
+ };
402
+ }
448
403
  resources = /* @__PURE__ */ new Map();
449
404
  resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
450
405
  resourceIdList = [];
451
- spans = /* @__PURE__ */ new Map();
452
- spanIdList = [];
453
406
  logs = [];
454
407
  _instanceTag = null;
455
408
  constructor() {
456
- log.addProcessor(this._logProcessor.bind(this), void 0, {
457
- F: __dxlog_file3,
458
- L: 103,
459
- S: this,
460
- C: (f, a) => f(...a)
461
- });
462
- if (!IS_CLOUDFLARE_WORKERS) {
463
- const refreshInterval = setInterval(this.refresh.bind(this), REFRESH_INTERVAL);
464
- unrefTimeout(refreshInterval);
465
- }
409
+ log.addProcessor(this._logProcessor.bind(this), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 80, S: this });
466
410
  if (DiagnosticsChannel.supported) {
467
411
  this.diagnosticsChannel.serve(this.diagnostics);
468
412
  }
@@ -472,10 +416,7 @@ var TraceProcessor = class {
472
416
  this._instanceTag = tag;
473
417
  this.diagnostics.setInstanceTag(tag);
474
418
  }
475
- /**
476
- * @internal
477
- */
478
- // TODO(burdon): Comment.
419
+ /** @internal */
479
420
  createTraceResource(params) {
480
421
  const id = this.resources.size;
481
422
  const tracingContext = getTracingContext(Object.getPrototypeOf(params.instance));
@@ -496,24 +437,10 @@ var TraceProcessor = class {
496
437
  if (this.resourceIdList.length > MAX_RESOURCE_RECORDS) {
497
438
  this._clearResources();
498
439
  }
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
440
  }
509
441
  // TODO(burdon): Not implemented.
510
442
  addLink(parent, child, opts) {
511
443
  }
512
- //
513
- // Getters
514
- //
515
- // TODO(burdon): Define type.
516
- // TODO(burdon): Reconcile with system service.
517
444
  getDiagnostics() {
518
445
  this.refresh();
519
446
  return {
@@ -521,7 +448,6 @@ var TraceProcessor = class {
521
448
  `${entry.sanitizedClassName}#${entry.data.instanceId}`,
522
449
  entry.data
523
450
  ])),
524
- spans: Array.from(this.spans.values()),
525
451
  logs: this.logs.filter((log2) => log2.level >= LogLevel.INFO)
526
452
  };
527
453
  }
@@ -576,43 +502,8 @@ var TraceProcessor = class {
576
502
  for (const key of Object.keys(tracingContext.metricsProperties)) {
577
503
  instance[key]._tick?.(time);
578
504
  }
579
- let _changed = false;
580
- const oldInfo = resource2.data.info;
581
505
  resource2.data.info = this.getResourceInfo(instance);
582
- _changed ||= !areEqualShallow(oldInfo, resource2.data.info);
583
- const oldMetrics = resource2.data.metrics;
584
506
  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
507
  }
617
508
  }
618
509
  _clearResources() {
@@ -621,20 +512,11 @@ var TraceProcessor = class {
621
512
  this.resources.delete(id);
622
513
  }
623
514
  }
624
- _clearSpans() {
625
- while (this.spanIdList.length > MAX_SPAN_RECORDS) {
626
- const id = this.spanIdList.shift();
627
- this.spans.delete(id);
628
- }
629
- }
630
515
  _pushLog(log2) {
631
516
  this.logs.push(log2);
632
517
  if (this.logs.length > MAX_LOG_RECORDS) {
633
518
  this.logs.shift();
634
519
  }
635
- for (const subscription of this.subscriptions) {
636
- subscription.newLogs.push(log2);
637
- }
638
520
  }
639
521
  _logProcessor = (config, entry) => {
640
522
  switch (entry.level) {
@@ -646,18 +528,21 @@ var TraceProcessor = class {
646
528
  if (!resource2) {
647
529
  return;
648
530
  }
649
- const context = getContextFromEntry(entry) ?? {};
650
- for (const key of Object.keys(context)) {
651
- context[key] = sanitizeValue(context[key], 0, this);
531
+ const context = {
532
+ ...entry.computedContext
533
+ };
534
+ if (entry.computedError !== void 0) {
535
+ context.error = entry.computedError;
652
536
  }
537
+ const { filename, line } = entry.computedMeta;
653
538
  const entryToPush = {
654
539
  level: entry.level,
655
- message: entry.message ?? (entry.error ? entry.error.message ?? String(entry.error) : ""),
540
+ message: entry.message ?? entry.computedError ?? "",
656
541
  context,
657
- timestamp: /* @__PURE__ */ new Date(),
542
+ timestamp: new Date(entry.timestamp),
658
543
  meta: {
659
- file: entry.meta?.F ?? "",
660
- line: entry.meta?.L ?? 0,
544
+ file: filename ?? "",
545
+ line: line ?? 0,
661
546
  resourceId: resource2.data.id
662
547
  }
663
548
  };
@@ -668,94 +553,6 @@ var TraceProcessor = class {
668
553
  }
669
554
  };
670
555
  };
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
556
  var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
760
557
  var sanitizeValue = (value, depth, traceProcessor) => {
761
558
  switch (typeof value) {
@@ -802,33 +599,46 @@ var sanitizeValue = (value, depth, traceProcessor) => {
802
599
  return value.toString();
803
600
  }
804
601
  };
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
602
  var sanitizeClassName = (className) => {
603
+ let name = className.replace(/^_+/, "");
819
604
  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);
605
+ const m = name.match(SANITIZE_REGEX);
606
+ if (m) {
607
+ name = name.slice(0, -m[1].length);
825
608
  }
609
+ return name;
826
610
  };
827
611
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
828
612
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
829
613
 
830
614
  // src/api.ts
615
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/common/tracing/src/api.ts";
616
+ var LIFECYCLE_SPAN = /* @__PURE__ */ Symbol("dxos.tracing.lifecycle-span");
617
+ var TRACE_ALL_KEY = "dxos.debug.traceAll";
618
+ var collectSpanAttributes = (instance, spanAttributes) => {
619
+ const proto = Object.getPrototypeOf(instance);
620
+ if (!proto) {
621
+ return;
622
+ }
623
+ const tracingContext = getTracingContext(proto);
624
+ for (const [key, { options }] of Object.entries(tracingContext.infoProperties)) {
625
+ if (!options.spanAttribute) {
626
+ continue;
627
+ }
628
+ try {
629
+ const value = typeof instance[key] === "function" ? instance[key]() : instance[key];
630
+ if (value != null) {
631
+ const resolved = options.enum ? options.enum[value] : String(value);
632
+ spanAttributes[`ctx.${key}`] = resolved;
633
+ }
634
+ } catch {
635
+ }
636
+ }
637
+ };
831
638
  var resource = (options) => (constructor) => {
639
+ if (options?.lifecycle && !(constructor.prototype instanceof Resource)) {
640
+ throw new Error(`@trace.resource({ lifecycle: true }) requires ${constructor.name} to extend Resource`);
641
+ }
832
642
  const klass = /* @__PURE__ */ (() => class extends constructor {
833
643
  constructor(...rest) {
834
644
  super(...rest);
@@ -839,6 +649,65 @@ var resource = (options) => (constructor) => {
839
649
  });
840
650
  }
841
651
  })();
652
+ if (options?.lifecycle) {
653
+ const sanitizedName = sanitizeClassName(constructor.name);
654
+ const proto = klass.prototype;
655
+ const originalOpen = proto.open;
656
+ const originalClose = proto.close;
657
+ proto.open = async function(ctx) {
658
+ const self = this;
659
+ if (self._lifecycleState !== LifecycleState.CLOSED) {
660
+ return originalOpen.call(this, ctx);
661
+ }
662
+ const parentSpanContext = ctx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
663
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
664
+ const spanAttributes = {};
665
+ if (resourceEntry) {
666
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
667
+ }
668
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend?.startSpan({
669
+ name: `${sanitizedName}.lifecycle`,
670
+ op: "lifecycle",
671
+ attributes: spanAttributes,
672
+ parentContext: parentSpanContext
673
+ });
674
+ self[LIFECYCLE_SPAN] = remoteSpan;
675
+ let openCtx = ctx;
676
+ if (remoteSpan?.spanContext != null) {
677
+ const traceAttrs = {
678
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
679
+ };
680
+ openCtx = ctx ? ctx.derive({
681
+ attributes: traceAttrs
682
+ }) : new Context2({
683
+ attributes: traceAttrs
684
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 79 });
685
+ }
686
+ try {
687
+ return await originalOpen.call(this, openCtx);
688
+ } catch (err) {
689
+ remoteSpan?.setError?.(err);
690
+ remoteSpan?.end();
691
+ self[LIFECYCLE_SPAN] = void 0;
692
+ throw err;
693
+ }
694
+ };
695
+ proto.close = async function(ctx) {
696
+ const self = this;
697
+ const remoteSpan = self[LIFECYCLE_SPAN];
698
+ try {
699
+ return await originalClose.call(this, ctx);
700
+ } catch (err) {
701
+ remoteSpan?.setError?.(err);
702
+ throw err;
703
+ } finally {
704
+ if (remoteSpan) {
705
+ remoteSpan.end();
706
+ self[LIFECYCLE_SPAN] = void 0;
707
+ }
708
+ }
709
+ };
710
+ }
842
711
  Object.defineProperty(klass, "name", {
843
712
  value: constructor.name
844
713
  });
@@ -852,45 +721,117 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
852
721
  var mark = (name) => {
853
722
  performance.mark(name);
854
723
  };
855
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
724
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
856
725
  const method = descriptor.value;
857
726
  descriptor.value = async function(...args) {
858
727
  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;
728
+ const startTs = performance.now();
729
+ const parentSpanContext = parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
730
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(this);
731
+ const className = resourceEntry?.sanitizedClassName ?? sanitizeClassName(target.constructor?.name ?? "unknown");
732
+ const spanName = `${className}.${propertyKey}`;
733
+ const spanAttributes = {};
734
+ if (resourceEntry) {
735
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
736
+ }
737
+ collectSpanAttributes(this, spanAttributes);
738
+ if (attributes) {
739
+ for (const [key, value] of Object.entries(attributes)) {
740
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
741
+ }
742
+ }
743
+ const remoteSpan = showInRemoteTracing ? TRACE_PROCESSOR.tracingBackend?.startSpan({
744
+ name: spanName,
745
+ op: op ?? "function",
746
+ attributes: spanAttributes,
747
+ parentContext: parentSpanContext
748
+ }) : void 0;
749
+ let callArgs = args;
750
+ if (parentCtx) {
751
+ const childCtx = remoteSpan?.spanContext != null ? parentCtx.derive({
752
+ attributes: {
753
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
754
+ }
755
+ }) : parentCtx.derive();
756
+ callArgs = [
757
+ childCtx,
758
+ ...args.slice(1)
759
+ ];
760
+ }
871
761
  try {
872
762
  return await method.apply(this, callArgs);
873
763
  } catch (err) {
874
- span2.markError(err);
764
+ remoteSpan?.setError?.(err);
875
765
  throw err;
876
766
  } finally {
877
- span2.markSuccess();
767
+ remoteSpan?.end();
768
+ if (showInBrowserTimeline && typeof globalThis?.performance?.measure === "function") {
769
+ performance.measure(spanName, {
770
+ start: startTs,
771
+ end: performance.now()
772
+ });
773
+ }
878
774
  }
879
775
  };
880
776
  };
881
- var spans = /* @__PURE__ */ new Map();
777
+ var manualSpans = /* @__PURE__ */ new Map();
778
+ var manualSpanTimestamps = /* @__PURE__ */ new Map();
882
779
  var spanStart = (params) => {
883
- if (spans.has(params.id)) {
884
- return;
780
+ if (manualSpans.has(params.id) || manualSpanTimestamps.has(params.id)) {
781
+ return params.parentCtx;
782
+ }
783
+ const resourceEntry = TRACE_PROCESSOR.resourceInstanceIndex.get(params.instance);
784
+ const className = resourceEntry?.sanitizedClassName ?? "unknown";
785
+ const spanName = `${className}.${params.methodName}`;
786
+ if (params.showInBrowserTimeline) {
787
+ manualSpanTimestamps.set(params.id, {
788
+ name: spanName,
789
+ startTs: performance.now()
790
+ });
791
+ }
792
+ if (params.showInRemoteTracing === false || !TRACE_PROCESSOR.tracingBackend) {
793
+ return params.parentCtx;
794
+ }
795
+ const parentSpanContext = params.parentCtx?.getAttribute(TRACE_SPAN_ATTRIBUTE);
796
+ const spanAttributes = {};
797
+ if (resourceEntry) {
798
+ spanAttributes.entryPoint = resourceEntry.sanitizedClassName;
799
+ }
800
+ collectSpanAttributes(params.instance, spanAttributes);
801
+ if (params.attributes) {
802
+ for (const [key, value] of Object.entries(params.attributes)) {
803
+ spanAttributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
804
+ }
885
805
  }
886
- const span2 = TRACE_PROCESSOR.traceSpan(params);
887
- spans.set(params.id, span2);
806
+ const remoteSpan = TRACE_PROCESSOR.tracingBackend.startSpan({
807
+ name: spanName,
808
+ op: params.op ?? "function",
809
+ attributes: spanAttributes,
810
+ parentContext: parentSpanContext
811
+ });
812
+ manualSpans.set(params.id, remoteSpan);
813
+ if (params.parentCtx && remoteSpan.spanContext != null) {
814
+ return params.parentCtx.derive({
815
+ attributes: {
816
+ [TRACE_SPAN_ATTRIBUTE]: remoteSpan.spanContext
817
+ }
818
+ });
819
+ }
820
+ return params.parentCtx;
888
821
  };
889
822
  var spanEnd = (id) => {
890
- const span2 = spans.get(id);
891
- if (span2) {
892
- span2.markSuccess();
893
- spans.delete(id);
823
+ const remoteSpan = manualSpans.get(id);
824
+ if (remoteSpan) {
825
+ remoteSpan.end();
826
+ manualSpans.delete(id);
827
+ }
828
+ const timestamps = manualSpanTimestamps.get(id);
829
+ if (timestamps && typeof globalThis?.performance?.measure === "function") {
830
+ performance.measure(timestamps.name, {
831
+ start: timestamps.startTs,
832
+ end: performance.now()
833
+ });
834
+ manualSpanTimestamps.delete(id);
894
835
  }
895
836
  };
896
837
  var metricsCounter = () => (target, propertyKey, descriptor) => {
@@ -1108,16 +1049,13 @@ export {
1108
1049
  DiagnosticsManager,
1109
1050
  MapCounter,
1110
1051
  RemoteMetrics,
1111
- RemoteTracing,
1112
1052
  ResourceEntry,
1053
+ TRACE_ALL_KEY,
1113
1054
  TRACE_PROCESSOR,
1114
- TRACE_SPAN_ATTRIBUTE,
1115
1055
  TimeSeriesCounter,
1116
1056
  TimeUsageCounter,
1117
1057
  TraceDiagnosticImpl,
1118
1058
  TraceProcessor,
1119
- TraceSender,
1120
- TracingSpan,
1121
1059
  UnaryCounter,
1122
1060
  getTracingContext,
1123
1061
  sanitizeClassName,