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