@dxos/tracing 0.8.4-main.ae835ea → 0.8.4-main.bc2380dfbc

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