@dxos/tracing 0.8.4-main.9be5663bfe → 0.8.4-main.abd8ff62ef

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 -513
  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 -513
  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 +31 -14
  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 -56
  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 -40
  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 +51 -263
  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 -57
  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 -175
  45. package/src/trace-sender.ts +0 -88
@@ -1,5 +1,5 @@
1
1
  // src/api.ts
2
- import { Context as Context3 } 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,14 +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 { Context as Context2 } from "@dxos/context";
17
- import { LogLevel, getContextFromEntry, log } from "@dxos/log";
14
+ import { LogLevel, log } from "@dxos/log";
18
15
  import { getPrototypeSpecificInstanceId } from "@dxos/util";
19
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
+
20
103
  // src/diagnostic.ts
21
104
  import { asyncTimeout } from "@dxos/async";
22
105
  import { invariant } from "@dxos/invariant";
@@ -71,27 +154,11 @@ var DiagnosticsManager = class {
71
154
  }
72
155
  async fetch(request) {
73
156
  if (request.instanceId != null) {
74
- invariant(request.instanceId === this.instanceId, "Invalid instance id", {
75
- F: __dxlog_file,
76
- L: 82,
77
- S: this,
78
- A: [
79
- "request.instanceId === this.instanceId",
80
- "'Invalid instance id'"
81
- ]
82
- });
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'"] });
83
158
  }
84
159
  const { id } = request;
85
160
  const diagnostic2 = this.registry.get(id);
86
- invariant(diagnostic2, "Diagnostic not found", {
87
- F: __dxlog_file,
88
- L: 86,
89
- S: this,
90
- A: [
91
- "diagnostic",
92
- "'Diagnostic not found'"
93
- ]
94
- });
161
+ invariant(diagnostic2, "Diagnostic not found", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 56, S: this, A: ["diagnostic", "'Diagnostic not found'"] });
95
162
  try {
96
163
  const data = await asyncTimeout(diagnostic2.fetch(), DIAGNOSTICS_TIMEOUT);
97
164
  return {
@@ -122,10 +189,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
122
189
  static get supported() {
123
190
  return globalThis.BroadcastChannel != null;
124
191
  }
125
- _ctx = new Context(void 0, {
126
- F: __dxlog_file2,
127
- L: 46
128
- });
192
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 16 });
129
193
  // Separate channels becauase the client and server may be in the same process.
130
194
  _serveChannel = void 0;
131
195
  _clientChannel = void 0;
@@ -153,15 +217,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
153
217
  }
154
218
  }
155
219
  serve(manager) {
156
- invariant2(this._serveChannel, void 0, {
157
- F: __dxlog_file2,
158
- L: 78,
159
- S: this,
160
- A: [
161
- "this._serveChannel",
162
- ""
163
- ]
164
- });
220
+ invariant2(this._serveChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 43, S: this, A: ["this._serveChannel", ""] });
165
221
  const listener = async (event) => {
166
222
  switch (event.data.type) {
167
223
  case "DIAGNOSTICS_DISCOVER": {
@@ -193,15 +249,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
193
249
  this._ctx.onDispose(() => this._serveChannel.removeEventListener("message", listener));
194
250
  }
195
251
  async discover() {
196
- invariant2(this._clientChannel, void 0, {
197
- F: __dxlog_file2,
198
- L: 114,
199
- S: this,
200
- A: [
201
- "this._clientChannel",
202
- ""
203
- ]
204
- });
252
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 77, S: this, A: ["this._clientChannel", ""] });
205
253
  const diagnostics = [];
206
254
  const collector = (event) => {
207
255
  const data = event.data;
@@ -229,15 +277,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
229
277
  }
230
278
  }
231
279
  async fetch(request) {
232
- invariant2(this._clientChannel, void 0, {
233
- F: __dxlog_file2,
234
- L: 147,
235
- S: this,
236
- A: [
237
- "this._clientChannel",
238
- ""
239
- ]
240
- });
280
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 106, S: this, A: ["this._clientChannel", ""] });
241
281
  const requestId = createId();
242
282
  const trigger = new Trigger();
243
283
  const listener = (event) => {
@@ -283,227 +323,6 @@ var RemoteMetrics = class {
283
323
  }
284
324
  };
285
325
 
286
- // src/remote/tracing.ts
287
- var MAX_ENDED_CONTEXTS = 1e4;
288
- var RemoteTracing = class {
289
- _tracing;
290
- _spanMap = /* @__PURE__ */ new Map();
291
- _idToSpan = /* @__PURE__ */ new Map();
292
- /**
293
- * Retains OTEL span contexts after spans end so that periodic/background
294
- * operations referencing a completed parent (via `this._ctx`) still produce
295
- * correlated child spans instead of orphaned root traces.
296
- */
297
- _endedSpanContexts = /* @__PURE__ */ new Map();
298
- /**
299
- * Buffers flushSpan calls that arrive before a processor is registered,
300
- * so early startup spans are not silently dropped.
301
- */
302
- _pendingFlushes = [];
303
- registerProcessor(processor) {
304
- this._tracing = processor;
305
- const pending = this._pendingFlushes;
306
- this._pendingFlushes = null;
307
- if (pending) {
308
- for (const { span: span2, isEnd } of pending) {
309
- this._replayFlush(span2, isEnd);
310
- }
311
- }
312
- }
313
- /** Returns the opaque OTEL context for the given DXOS span ID, if one exists. */
314
- getSpanContext(spanId) {
315
- const tracingSpan = this._idToSpan.get(spanId);
316
- if (tracingSpan) {
317
- return this._spanMap.get(tracingSpan)?.spanContext;
318
- }
319
- return this._endedSpanContexts.get(spanId);
320
- }
321
- /** Wraps execution so that the remote span is active as parent context. */
322
- wrapExecution(span2, fn) {
323
- const remoteSpan = this._spanMap.get(span2);
324
- if (remoteSpan?.wrapExecution) {
325
- return remoteSpan.wrapExecution(fn);
326
- }
327
- return fn();
328
- }
329
- flushSpan(span2) {
330
- if (!span2.showInRemoteTracing) {
331
- return;
332
- }
333
- if (!this._tracing) {
334
- this._pendingFlushes?.push({
335
- span: span2,
336
- isEnd: !!span2.endTs
337
- });
338
- return;
339
- }
340
- if (!span2.endTs) {
341
- this._startRemoteSpan(span2);
342
- } else {
343
- this._endRemoteSpan(span2);
344
- }
345
- }
346
- _startRemoteSpan(span2) {
347
- let parentContext;
348
- if (span2.parentId != null) {
349
- const parentTracingSpan = this._idToSpan.get(span2.parentId);
350
- if (parentTracingSpan) {
351
- parentContext = this._spanMap.get(parentTracingSpan)?.spanContext;
352
- }
353
- if (parentContext == null) {
354
- parentContext = this._endedSpanContexts.get(span2.parentId);
355
- }
356
- }
357
- const attributes = {};
358
- if (span2.sanitizedClassName) {
359
- attributes.entryPoint = span2.sanitizedClassName;
360
- }
361
- for (const [key, value] of Object.entries(span2.attributes)) {
362
- attributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
363
- }
364
- const remoteSpan = this._tracing.startSpan({
365
- name: span2.name,
366
- op: span2.op ?? "function",
367
- attributes,
368
- parentContext
369
- });
370
- this._spanMap.set(span2, remoteSpan);
371
- this._idToSpan.set(span2.id, span2);
372
- }
373
- _endRemoteSpan(span2) {
374
- const remoteSpan = this._spanMap.get(span2);
375
- if (remoteSpan) {
376
- if (remoteSpan.spanContext != null) {
377
- this._endedSpanContexts.set(span2.id, remoteSpan.spanContext);
378
- this._evictEndedContexts();
379
- }
380
- remoteSpan.end();
381
- this._spanMap.delete(span2);
382
- this._idToSpan.delete(span2.id);
383
- }
384
- }
385
- /**
386
- * Replays a buffered flush that was queued before the processor was registered.
387
- * For spans that started AND ended before registration, replays both events.
388
- */
389
- _replayFlush(span2, isEnd) {
390
- if (!isEnd) {
391
- this._startRemoteSpan(span2);
392
- if (span2.endTs != null) {
393
- this._endRemoteSpan(span2);
394
- }
395
- } else {
396
- if (!this._spanMap.has(span2)) {
397
- this._startRemoteSpan(span2);
398
- }
399
- this._endRemoteSpan(span2);
400
- }
401
- }
402
- _evictEndedContexts() {
403
- if (this._endedSpanContexts.size <= MAX_ENDED_CONTEXTS) {
404
- return;
405
- }
406
- const iterator = this._endedSpanContexts.keys();
407
- while (this._endedSpanContexts.size > MAX_ENDED_CONTEXTS) {
408
- const oldest = iterator.next();
409
- if (oldest.done) {
410
- break;
411
- }
412
- this._endedSpanContexts.delete(oldest.value);
413
- }
414
- }
415
- };
416
-
417
- // src/trace-sender.ts
418
- import { Stream } from "@dxos/codec-protobuf/stream";
419
- var TraceSender = class {
420
- _traceProcessor;
421
- constructor(_traceProcessor) {
422
- this._traceProcessor = _traceProcessor;
423
- }
424
- streamTrace(request) {
425
- return new Stream(({ ctx, next }) => {
426
- const flushEvents = (resources, spans2, logs) => {
427
- const event = {
428
- resourceAdded: [],
429
- resourceRemoved: [],
430
- spanAdded: [],
431
- logAdded: []
432
- };
433
- if (resources) {
434
- for (const id of resources) {
435
- const entry = this._traceProcessor.resources.get(id);
436
- if (entry) {
437
- event.resourceAdded.push({
438
- resource: entry.data
439
- });
440
- } else {
441
- event.resourceRemoved.push({
442
- id
443
- });
444
- }
445
- }
446
- } else {
447
- for (const entry of this._traceProcessor.resources.values()) {
448
- event.resourceAdded.push({
449
- resource: entry.data
450
- });
451
- }
452
- }
453
- if (spans2) {
454
- for (const id of spans2) {
455
- const span2 = this._traceProcessor.spans.get(id);
456
- if (span2) {
457
- event.spanAdded.push({
458
- span: span2
459
- });
460
- }
461
- }
462
- } else {
463
- for (const span2 of this._traceProcessor.spans.values()) {
464
- event.spanAdded.push({
465
- span: span2
466
- });
467
- }
468
- }
469
- if (logs) {
470
- for (const log2 of logs) {
471
- event.logAdded.push({
472
- log: log2
473
- });
474
- }
475
- } else {
476
- for (const log2 of this._traceProcessor.logs) {
477
- event.logAdded.push({
478
- log: log2
479
- });
480
- }
481
- }
482
- if (event.resourceAdded.length > 0 || event.resourceRemoved.length > 0 || event.spanAdded.length > 0) {
483
- next(event);
484
- }
485
- };
486
- const flush = () => {
487
- flushEvents(subscription.dirtyResources, subscription.dirtySpans, subscription.newLogs);
488
- subscription.dirtyResources.clear();
489
- subscription.dirtySpans.clear();
490
- subscription.newLogs.length = 0;
491
- };
492
- const subscription = {
493
- flush,
494
- dirtyResources: /* @__PURE__ */ new Set(),
495
- dirtySpans: /* @__PURE__ */ new Set(),
496
- newLogs: []
497
- };
498
- this._traceProcessor.subscriptions.add(subscription);
499
- ctx.onDispose(() => {
500
- this._traceProcessor.subscriptions.delete(subscription);
501
- });
502
- flushEvents(null, null, null);
503
- });
504
- }
505
- };
506
-
507
326
  // src/weak-ref.ts
508
327
  var WeakRefMock = class {
509
328
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -538,35 +357,56 @@ var ResourceEntry = class {
538
357
  }
539
358
  };
540
359
  var MAX_RESOURCE_RECORDS = 2e3;
541
- var MAX_SPAN_RECORDS = 1e3;
542
360
  var MAX_LOG_RECORDS = 1e3;
543
- var REFRESH_INTERVAL = 1e3;
544
361
  var MAX_INFO_OBJECT_DEPTH = 8;
545
- var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
546
362
  var TraceProcessor = class {
547
363
  diagnostics = new DiagnosticsManager();
548
364
  diagnosticsChannel = new DiagnosticsChannel();
549
365
  remoteMetrics = new RemoteMetrics();
550
- remoteTracing = new RemoteTracing();
551
- 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
+ }
552
403
  resources = /* @__PURE__ */ new Map();
553
404
  resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
554
405
  resourceIdList = [];
555
- spans = /* @__PURE__ */ new Map();
556
- spanIdList = [];
557
406
  logs = [];
558
407
  _instanceTag = null;
559
408
  constructor() {
560
- log.addProcessor(this._logProcessor.bind(this), void 0, {
561
- F: __dxlog_file3,
562
- L: 104,
563
- S: this,
564
- C: (f, a) => f(...a)
565
- });
566
- if (!IS_CLOUDFLARE_WORKERS) {
567
- const refreshInterval = setInterval(this.refresh.bind(this), REFRESH_INTERVAL);
568
- unrefTimeout(refreshInterval);
569
- }
409
+ log.addProcessor(this._logProcessor.bind(this), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 80, S: this });
570
410
  if (DiagnosticsChannel.supported) {
571
411
  this.diagnosticsChannel.serve(this.diagnostics);
572
412
  }
@@ -576,10 +416,7 @@ var TraceProcessor = class {
576
416
  this._instanceTag = tag;
577
417
  this.diagnostics.setInstanceTag(tag);
578
418
  }
579
- /**
580
- * @internal
581
- */
582
- // TODO(burdon): Comment.
419
+ /** @internal */
583
420
  createTraceResource(params) {
584
421
  const id = this.resources.size;
585
422
  const tracingContext = getTracingContext(Object.getPrototypeOf(params.instance));
@@ -600,24 +437,10 @@ var TraceProcessor = class {
600
437
  if (this.resourceIdList.length > MAX_RESOURCE_RECORDS) {
601
438
  this._clearResources();
602
439
  }
603
- this._markResourceDirty(id);
604
- }
605
- createTraceSender() {
606
- return new TraceSender(this);
607
- }
608
- traceSpan(params) {
609
- const span2 = new TracingSpan(this, params);
610
- this._flushSpan(span2);
611
- return span2;
612
440
  }
613
441
  // TODO(burdon): Not implemented.
614
442
  addLink(parent, child, opts) {
615
443
  }
616
- //
617
- // Getters
618
- //
619
- // TODO(burdon): Define type.
620
- // TODO(burdon): Reconcile with system service.
621
444
  getDiagnostics() {
622
445
  this.refresh();
623
446
  return {
@@ -625,7 +448,6 @@ var TraceProcessor = class {
625
448
  `${entry.sanitizedClassName}#${entry.data.instanceId}`,
626
449
  entry.data
627
450
  ])),
628
- spans: Array.from(this.spans.values()),
629
451
  logs: this.logs.filter((log2) => log2.level >= LogLevel.INFO)
630
452
  };
631
453
  }
@@ -680,43 +502,8 @@ var TraceProcessor = class {
680
502
  for (const key of Object.keys(tracingContext.metricsProperties)) {
681
503
  instance[key]._tick?.(time);
682
504
  }
683
- let _changed = false;
684
- const oldInfo = resource2.data.info;
685
505
  resource2.data.info = this.getResourceInfo(instance);
686
- _changed ||= !areEqualShallow(oldInfo, resource2.data.info);
687
- const oldMetrics = resource2.data.metrics;
688
506
  resource2.data.metrics = this.getResourceMetrics(instance);
689
- _changed ||= !areEqualShallow(oldMetrics, resource2.data.metrics);
690
- this._markResourceDirty(resource2.data.id);
691
- }
692
- for (const subscription of this.subscriptions) {
693
- subscription.flush();
694
- }
695
- }
696
- //
697
- // Implementation
698
- //
699
- /**
700
- * @internal
701
- */
702
- _flushSpan(runtimeSpan) {
703
- const span2 = runtimeSpan.serialize();
704
- this.spans.set(span2.id, span2);
705
- this.spanIdList.push(span2.id);
706
- if (this.spanIdList.length > MAX_SPAN_RECORDS) {
707
- this._clearSpans();
708
- }
709
- this._markSpanDirty(span2.id);
710
- this.remoteTracing.flushSpan(runtimeSpan);
711
- }
712
- _markResourceDirty(id) {
713
- for (const subscription of this.subscriptions) {
714
- subscription.dirtyResources.add(id);
715
- }
716
- }
717
- _markSpanDirty(id) {
718
- for (const subscription of this.subscriptions) {
719
- subscription.dirtySpans.add(id);
720
507
  }
721
508
  }
722
509
  _clearResources() {
@@ -725,20 +512,11 @@ var TraceProcessor = class {
725
512
  this.resources.delete(id);
726
513
  }
727
514
  }
728
- _clearSpans() {
729
- while (this.spanIdList.length > MAX_SPAN_RECORDS) {
730
- const id = this.spanIdList.shift();
731
- this.spans.delete(id);
732
- }
733
- }
734
515
  _pushLog(log2) {
735
516
  this.logs.push(log2);
736
517
  if (this.logs.length > MAX_LOG_RECORDS) {
737
518
  this.logs.shift();
738
519
  }
739
- for (const subscription of this.subscriptions) {
740
- subscription.newLogs.push(log2);
741
- }
742
520
  }
743
521
  _logProcessor = (config, entry) => {
744
522
  switch (entry.level) {
@@ -750,18 +528,21 @@ var TraceProcessor = class {
750
528
  if (!resource2) {
751
529
  return;
752
530
  }
753
- const context = getContextFromEntry(entry) ?? {};
754
- for (const key of Object.keys(context)) {
755
- 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;
756
536
  }
537
+ const { filename, line } = entry.computedMeta;
757
538
  const entryToPush = {
758
539
  level: entry.level,
759
- message: entry.message ?? (entry.error ? entry.error.message ?? String(entry.error) : ""),
540
+ message: entry.message ?? entry.computedError ?? "",
760
541
  context,
761
- timestamp: /* @__PURE__ */ new Date(),
542
+ timestamp: new Date(entry.timestamp),
762
543
  meta: {
763
- file: entry.meta?.F ?? "",
764
- line: entry.meta?.L ?? 0,
544
+ file: filename ?? "",
545
+ line: line ?? 0,
765
546
  resourceId: resource2.data.id
766
547
  }
767
548
  };
@@ -772,108 +553,6 @@ var TraceProcessor = class {
772
553
  }
773
554
  };
774
555
  };
775
- var TracingSpan = class _TracingSpan {
776
- _traceProcessor;
777
- static nextId = 0;
778
- id;
779
- parentId = null;
780
- methodName;
781
- resourceId = null;
782
- op;
783
- attributes;
784
- startTs;
785
- endTs = null;
786
- error = null;
787
- _showInBrowserTimeline;
788
- _showInRemoteTracing;
789
- _ctx;
790
- constructor(_traceProcessor, params) {
791
- this._traceProcessor = _traceProcessor;
792
- this.id = _TracingSpan.nextId++;
793
- this.methodName = params.methodName;
794
- this.resourceId = _traceProcessor.getResourceId(params.instance);
795
- this.startTs = performance.now();
796
- this._showInBrowserTimeline = params.showInBrowserTimeline;
797
- this._showInRemoteTracing = params.showInRemoteTracing ?? true;
798
- this.op = params.op;
799
- this.attributes = params.attributes ?? {};
800
- const baseCtx = params.parentCtx ?? new Context2(void 0, {
801
- F: __dxlog_file3,
802
- L: 397
803
- });
804
- this._ctx = this._showInRemoteTracing ? baseCtx.derive({
805
- attributes: {
806
- [TRACE_SPAN_ATTRIBUTE]: this.id
807
- }
808
- }) : baseCtx.derive();
809
- if (params.parentCtx) {
810
- const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
811
- if (typeof parentId === "number") {
812
- this.parentId = parentId;
813
- }
814
- }
815
- }
816
- get name() {
817
- const resource2 = this._traceProcessor.resources.get(this.resourceId);
818
- return resource2 ? `${resource2.sanitizedClassName}#${resource2.data.instanceId}.${this.methodName}` : this.methodName;
819
- }
820
- /** Sanitized class name of the owning resource, if available. */
821
- get sanitizedClassName() {
822
- const resource2 = this.resourceId != null ? this._traceProcessor.resources.get(this.resourceId) : void 0;
823
- return resource2?.sanitizedClassName;
824
- }
825
- get ctx() {
826
- return this._ctx;
827
- }
828
- get showInRemoteTracing() {
829
- return this._showInRemoteTracing;
830
- }
831
- markSuccess() {
832
- this.endTs = performance.now();
833
- this._traceProcessor._flushSpan(this);
834
- if (this._showInBrowserTimeline) {
835
- this._markInBrowserTimeline();
836
- }
837
- }
838
- markError(err) {
839
- this.endTs = performance.now();
840
- this.error = serializeError(err);
841
- this._traceProcessor._flushSpan(this);
842
- if (this._showInBrowserTimeline) {
843
- this._markInBrowserTimeline();
844
- }
845
- }
846
- serialize() {
847
- return {
848
- id: this.id,
849
- resourceId: this.resourceId ?? void 0,
850
- methodName: this.methodName,
851
- parentId: this.parentId ?? void 0,
852
- startTs: this.startTs.toFixed(3),
853
- endTs: this.endTs?.toFixed(3) ?? void 0,
854
- error: this.error ?? void 0
855
- };
856
- }
857
- _markInBrowserTimeline() {
858
- if (typeof globalThis?.performance?.measure === "function") {
859
- performance.measure(this.name, {
860
- start: this.startTs,
861
- end: this.endTs
862
- });
863
- }
864
- }
865
- };
866
- var serializeError = (err) => {
867
- if (err instanceof Error) {
868
- return {
869
- name: err.name,
870
- message: err.message
871
- };
872
- }
873
- return {
874
- message: String(err)
875
- };
876
- };
877
556
  var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
878
557
  var sanitizeValue = (value, depth, traceProcessor) => {
879
558
  switch (typeof value) {
@@ -920,19 +599,6 @@ var sanitizeValue = (value, depth, traceProcessor) => {
920
599
  return value.toString();
921
600
  }
922
601
  };
923
- var areEqualShallow = (a, b) => {
924
- for (const key in a) {
925
- if (!(key in b) || a[key] !== b[key]) {
926
- return false;
927
- }
928
- }
929
- for (const key in b) {
930
- if (!(key in a) || a[key] !== b[key]) {
931
- return false;
932
- }
933
- }
934
- return true;
935
- };
936
602
  var sanitizeClassName = (className) => {
937
603
  let name = className.replace(/^_+/, "");
938
604
  const SANITIZE_REGEX = /[^_](\d+)$/;
@@ -946,7 +612,33 @@ var isSetLike = (value) => value instanceof Set || typeof value === "object" &&
946
612
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
947
613
 
948
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
+ };
949
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
+ }
950
642
  const klass = /* @__PURE__ */ (() => class extends constructor {
951
643
  constructor(...rest) {
952
644
  super(...rest);
@@ -957,6 +649,65 @@ var resource = (options) => (constructor) => {
957
649
  });
958
650
  }
959
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
+ }
960
711
  Object.defineProperty(klass, "name", {
961
712
  value: constructor.name
962
713
  });
@@ -973,45 +724,114 @@ var mark = (name) => {
973
724
  var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
974
725
  const method = descriptor.value;
975
726
  descriptor.value = async function(...args) {
976
- const explicitCtx = args[0] instanceof Context3 ? args[0] : null;
977
- const span2 = TRACE_PROCESSOR.traceSpan({
978
- parentCtx: explicitCtx,
979
- methodName: propertyKey,
980
- instance: this,
981
- showInBrowserTimeline,
982
- showInRemoteTracing,
983
- op,
984
- attributes
985
- });
986
- const callArgs = explicitCtx ? [
987
- span2.ctx,
988
- ...args.slice(1)
989
- ] : args;
990
- return TRACE_PROCESSOR.remoteTracing.wrapExecution(span2, async () => {
991
- try {
992
- return await method.apply(this, callArgs);
993
- } catch (err) {
994
- span2.markError(err);
995
- throw err;
996
- } finally {
997
- span2.markSuccess();
727
+ const parentCtx = args[0] instanceof Context2 ? args[0] : null;
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;
998
741
  }
999
- });
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
+ }
761
+ try {
762
+ return await method.apply(this, callArgs);
763
+ } catch (err) {
764
+ remoteSpan?.setError?.(err);
765
+ throw err;
766
+ } finally {
767
+ remoteSpan?.end();
768
+ if (showInBrowserTimeline && typeof globalThis?.performance?.measure === "function") {
769
+ performance.measure(spanName, {
770
+ start: startTs,
771
+ end: performance.now()
772
+ });
773
+ }
774
+ }
1000
775
  };
1001
776
  };
1002
- var spans = /* @__PURE__ */ new Map();
777
+ var manualSpans = /* @__PURE__ */ new Map();
778
+ var manualSpanTimestamps = /* @__PURE__ */ new Map();
1003
779
  var spanStart = (params) => {
1004
- if (spans.has(params.id)) {
1005
- 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
+ }
1006
805
  }
1007
- const span2 = TRACE_PROCESSOR.traceSpan(params);
1008
- 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;
1009
821
  };
1010
822
  var spanEnd = (id) => {
1011
- const span2 = spans.get(id);
1012
- if (span2) {
1013
- span2.markSuccess();
1014
- 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);
1015
835
  }
1016
836
  };
1017
837
  var metricsCounter = () => (target, propertyKey, descriptor) => {
@@ -1229,16 +1049,13 @@ export {
1229
1049
  DiagnosticsManager,
1230
1050
  MapCounter,
1231
1051
  RemoteMetrics,
1232
- RemoteTracing,
1233
1052
  ResourceEntry,
1053
+ TRACE_ALL_KEY,
1234
1054
  TRACE_PROCESSOR,
1235
- TRACE_SPAN_ATTRIBUTE,
1236
1055
  TimeSeriesCounter,
1237
1056
  TimeUsageCounter,
1238
1057
  TraceDiagnosticImpl,
1239
1058
  TraceProcessor,
1240
- TraceSender,
1241
- TracingSpan,
1242
1059
  UnaryCounter,
1243
1060
  getTracingContext,
1244
1061
  sanitizeClassName,