@dxos/tracing 0.8.4-main.fd6878d → 0.8.4-staging.60fe92afc8

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 +333 -403
  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 +333 -403
  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 +13 -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,18 +191,12 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
123
191
  static get supported() {
124
192
  return globalThis.BroadcastChannel != null;
125
193
  }
126
- _ctx;
194
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 16 });
127
195
  // Separate channels becauase the client and server may be in the same process.
128
- _serveChannel;
129
- _clientChannel;
196
+ _serveChannel = void 0;
197
+ _clientChannel = void 0;
130
198
  constructor(_channelName = DEFAULT_CHANNEL_NAME) {
131
199
  this._channelName = _channelName;
132
- this._ctx = new Context(void 0, {
133
- F: __dxlog_file2,
134
- L: 46
135
- });
136
- this._serveChannel = void 0;
137
- this._clientChannel = void 0;
138
200
  if (_DiagnosticsChannel.supported) {
139
201
  this._serveChannel = new BroadcastChannel(_channelName);
140
202
  this._clientChannel = new BroadcastChannel(_channelName);
@@ -157,15 +219,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
157
219
  }
158
220
  }
159
221
  serve(manager) {
160
- invariant2(this._serveChannel, void 0, {
161
- F: __dxlog_file2,
162
- L: 78,
163
- S: this,
164
- A: [
165
- "this._serveChannel",
166
- ""
167
- ]
168
- });
222
+ invariant2(this._serveChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 43, S: this, A: ["this._serveChannel", ""] });
169
223
  const listener = async (event) => {
170
224
  switch (event.data.type) {
171
225
  case "DIAGNOSTICS_DISCOVER": {
@@ -197,15 +251,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
197
251
  this._ctx.onDispose(() => this._serveChannel.removeEventListener("message", listener));
198
252
  }
199
253
  async discover() {
200
- invariant2(this._clientChannel, void 0, {
201
- F: __dxlog_file2,
202
- L: 114,
203
- S: this,
204
- A: [
205
- "this._clientChannel",
206
- ""
207
- ]
208
- });
254
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 77, S: this, A: ["this._clientChannel", ""] });
209
255
  const diagnostics = [];
210
256
  const collector = (event) => {
211
257
  const data = event.data;
@@ -233,15 +279,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
233
279
  }
234
280
  }
235
281
  async fetch(request) {
236
- invariant2(this._clientChannel, void 0, {
237
- F: __dxlog_file2,
238
- L: 147,
239
- S: this,
240
- A: [
241
- "this._clientChannel",
242
- ""
243
- ]
244
- });
282
+ invariant2(this._clientChannel, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 106, S: this, A: ["this._clientChannel", ""] });
245
283
  const requestId = createId();
246
284
  const trigger = new Trigger();
247
285
  const listener = (event) => {
@@ -287,124 +325,6 @@ var RemoteMetrics = class {
287
325
  }
288
326
  };
289
327
 
290
- // src/remote/tracing.ts
291
- var RemoteTracing = class {
292
- _tracing;
293
- _spanMap = /* @__PURE__ */ new Map();
294
- registerProcessor(processor) {
295
- this._tracing = processor;
296
- }
297
- flushSpan(span2) {
298
- if (!this._tracing) {
299
- return;
300
- }
301
- if (!span2.endTs) {
302
- const remoteSpan = this._tracing.startSpan({
303
- name: span2.methodName,
304
- op: span2.op ?? "function",
305
- attributes: span2.attributes
306
- });
307
- this._spanMap.set(span2, remoteSpan);
308
- } else {
309
- const remoteSpan = this._spanMap.get(span2);
310
- if (remoteSpan) {
311
- remoteSpan.end();
312
- this._spanMap.delete(span2);
313
- }
314
- }
315
- }
316
- };
317
-
318
- // src/trace-sender.ts
319
- import { Stream } from "@dxos/codec-protobuf/stream";
320
- var TraceSender = class {
321
- _traceProcessor;
322
- constructor(_traceProcessor) {
323
- this._traceProcessor = _traceProcessor;
324
- }
325
- streamTrace(request) {
326
- return new Stream(({ ctx, next }) => {
327
- const flushEvents = (resources, spans2, logs) => {
328
- const event = {
329
- resourceAdded: [],
330
- resourceRemoved: [],
331
- spanAdded: [],
332
- logAdded: []
333
- };
334
- if (resources) {
335
- for (const id of resources) {
336
- const entry = this._traceProcessor.resources.get(id);
337
- if (entry) {
338
- event.resourceAdded.push({
339
- resource: entry.data
340
- });
341
- } else {
342
- event.resourceRemoved.push({
343
- id
344
- });
345
- }
346
- }
347
- } else {
348
- for (const entry of this._traceProcessor.resources.values()) {
349
- event.resourceAdded.push({
350
- resource: entry.data
351
- });
352
- }
353
- }
354
- if (spans2) {
355
- for (const id of spans2) {
356
- const span2 = this._traceProcessor.spans.get(id);
357
- if (span2) {
358
- event.spanAdded.push({
359
- span: span2
360
- });
361
- }
362
- }
363
- } else {
364
- for (const span2 of this._traceProcessor.spans.values()) {
365
- event.spanAdded.push({
366
- span: span2
367
- });
368
- }
369
- }
370
- if (logs) {
371
- for (const log2 of logs) {
372
- event.logAdded.push({
373
- log: log2
374
- });
375
- }
376
- } else {
377
- for (const log2 of this._traceProcessor.logs) {
378
- event.logAdded.push({
379
- log: log2
380
- });
381
- }
382
- }
383
- if (event.resourceAdded.length > 0 || event.resourceRemoved.length > 0 || event.spanAdded.length > 0) {
384
- next(event);
385
- }
386
- };
387
- const flush = () => {
388
- flushEvents(subscription.dirtyResources, subscription.dirtySpans, subscription.newLogs);
389
- subscription.dirtyResources.clear();
390
- subscription.dirtySpans.clear();
391
- subscription.newLogs.length = 0;
392
- };
393
- const subscription = {
394
- flush,
395
- dirtyResources: /* @__PURE__ */ new Set(),
396
- dirtySpans: /* @__PURE__ */ new Set(),
397
- newLogs: []
398
- };
399
- this._traceProcessor.subscriptions.add(subscription);
400
- ctx.onDispose(() => {
401
- this._traceProcessor.subscriptions.delete(subscription);
402
- });
403
- flushEvents(null, null, null);
404
- });
405
- }
406
- };
407
-
408
328
  // src/weak-ref.ts
409
329
  var WeakRefMock = class {
410
330
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -439,35 +359,56 @@ var ResourceEntry = class {
439
359
  }
440
360
  };
441
361
  var MAX_RESOURCE_RECORDS = 2e3;
442
- var MAX_SPAN_RECORDS = 1e3;
443
362
  var MAX_LOG_RECORDS = 1e3;
444
- var REFRESH_INTERVAL = 1e3;
445
363
  var MAX_INFO_OBJECT_DEPTH = 8;
446
- var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
447
364
  var TraceProcessor = class {
448
365
  diagnostics = new DiagnosticsManager();
449
366
  diagnosticsChannel = new DiagnosticsChannel();
450
367
  remoteMetrics = new RemoteMetrics();
451
- remoteTracing = new RemoteTracing();
452
- 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
+ }
453
405
  resources = /* @__PURE__ */ new Map();
454
406
  resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
455
407
  resourceIdList = [];
456
- spans = /* @__PURE__ */ new Map();
457
- spanIdList = [];
458
408
  logs = [];
459
409
  _instanceTag = null;
460
410
  constructor() {
461
- log.addProcessor(this._logProcessor.bind(this), void 0, {
462
- F: __dxlog_file3,
463
- L: 103,
464
- S: this,
465
- C: (f, a) => f(...a)
466
- });
467
- if (!IS_CLOUDFLARE_WORKERS) {
468
- const refreshInterval = setInterval(this.refresh.bind(this), REFRESH_INTERVAL);
469
- unrefTimeout(refreshInterval);
470
- }
411
+ log.addProcessor(this._logProcessor.bind(this), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 80, S: this });
471
412
  if (DiagnosticsChannel.supported) {
472
413
  this.diagnosticsChannel.serve(this.diagnostics);
473
414
  }
@@ -477,10 +418,7 @@ var TraceProcessor = class {
477
418
  this._instanceTag = tag;
478
419
  this.diagnostics.setInstanceTag(tag);
479
420
  }
480
- /**
481
- * @internal
482
- */
483
- // TODO(burdon): Comment.
421
+ /** @internal */
484
422
  createTraceResource(params) {
485
423
  const id = this.resources.size;
486
424
  const tracingContext = getTracingContext(Object.getPrototypeOf(params.instance));
@@ -501,24 +439,10 @@ var TraceProcessor = class {
501
439
  if (this.resourceIdList.length > MAX_RESOURCE_RECORDS) {
502
440
  this._clearResources();
503
441
  }
504
- this._markResourceDirty(id);
505
- }
506
- createTraceSender() {
507
- return new TraceSender(this);
508
- }
509
- traceSpan(params) {
510
- const span2 = new TracingSpan(this, params);
511
- this._flushSpan(span2);
512
- return span2;
513
442
  }
514
443
  // TODO(burdon): Not implemented.
515
444
  addLink(parent, child, opts) {
516
445
  }
517
- //
518
- // Getters
519
- //
520
- // TODO(burdon): Define type.
521
- // TODO(burdon): Reconcile with system service.
522
446
  getDiagnostics() {
523
447
  this.refresh();
524
448
  return {
@@ -526,7 +450,6 @@ var TraceProcessor = class {
526
450
  `${entry.sanitizedClassName}#${entry.data.instanceId}`,
527
451
  entry.data
528
452
  ])),
529
- spans: Array.from(this.spans.values()),
530
453
  logs: this.logs.filter((log2) => log2.level >= LogLevel.INFO)
531
454
  };
532
455
  }
@@ -581,43 +504,8 @@ var TraceProcessor = class {
581
504
  for (const key of Object.keys(tracingContext.metricsProperties)) {
582
505
  instance[key]._tick?.(time);
583
506
  }
584
- let _changed = false;
585
- const oldInfo = resource2.data.info;
586
507
  resource2.data.info = this.getResourceInfo(instance);
587
- _changed ||= !areEqualShallow(oldInfo, resource2.data.info);
588
- const oldMetrics = resource2.data.metrics;
589
508
  resource2.data.metrics = this.getResourceMetrics(instance);
590
- _changed ||= !areEqualShallow(oldMetrics, resource2.data.metrics);
591
- this._markResourceDirty(resource2.data.id);
592
- }
593
- for (const subscription of this.subscriptions) {
594
- subscription.flush();
595
- }
596
- }
597
- //
598
- // Implementation
599
- //
600
- /**
601
- * @internal
602
- */
603
- _flushSpan(runtimeSpan) {
604
- const span2 = runtimeSpan.serialize();
605
- this.spans.set(span2.id, span2);
606
- this.spanIdList.push(span2.id);
607
- if (this.spanIdList.length > MAX_SPAN_RECORDS) {
608
- this._clearSpans();
609
- }
610
- this._markSpanDirty(span2.id);
611
- this.remoteTracing.flushSpan(runtimeSpan);
612
- }
613
- _markResourceDirty(id) {
614
- for (const subscription of this.subscriptions) {
615
- subscription.dirtyResources.add(id);
616
- }
617
- }
618
- _markSpanDirty(id) {
619
- for (const subscription of this.subscriptions) {
620
- subscription.dirtySpans.add(id);
621
509
  }
622
510
  }
623
511
  _clearResources() {
@@ -626,20 +514,11 @@ var TraceProcessor = class {
626
514
  this.resources.delete(id);
627
515
  }
628
516
  }
629
- _clearSpans() {
630
- while (this.spanIdList.length > MAX_SPAN_RECORDS) {
631
- const id = this.spanIdList.shift();
632
- this.spans.delete(id);
633
- }
634
- }
635
517
  _pushLog(log2) {
636
518
  this.logs.push(log2);
637
519
  if (this.logs.length > MAX_LOG_RECORDS) {
638
520
  this.logs.shift();
639
521
  }
640
- for (const subscription of this.subscriptions) {
641
- subscription.newLogs.push(log2);
642
- }
643
522
  }
644
523
  _logProcessor = (config, entry) => {
645
524
  switch (entry.level) {
@@ -651,18 +530,21 @@ var TraceProcessor = class {
651
530
  if (!resource2) {
652
531
  return;
653
532
  }
654
- const context = getContextFromEntry(entry) ?? {};
655
- for (const key of Object.keys(context)) {
656
- 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;
657
538
  }
539
+ const { filename, line } = entry.computedMeta;
658
540
  const entryToPush = {
659
541
  level: entry.level,
660
- message: entry.message ?? (entry.error ? entry.error.message ?? String(entry.error) : ""),
542
+ message: entry.message ?? entry.computedError ?? "",
661
543
  context,
662
- timestamp: /* @__PURE__ */ new Date(),
544
+ timestamp: new Date(entry.timestamp),
663
545
  meta: {
664
- file: entry.meta?.F ?? "",
665
- line: entry.meta?.L ?? 0,
546
+ file: filename ?? "",
547
+ line: line ?? 0,
666
548
  resourceId: resource2.data.id
667
549
  }
668
550
  };
@@ -673,99 +555,6 @@ var TraceProcessor = class {
673
555
  }
674
556
  };
675
557
  };
676
- var TracingSpan = class _TracingSpan {
677
- _traceProcessor;
678
- static nextId = 0;
679
- id;
680
- parentId;
681
- methodName;
682
- resourceId;
683
- op;
684
- attributes;
685
- startTs;
686
- endTs;
687
- error;
688
- _showInBrowserTimeline;
689
- _ctx;
690
- constructor(_traceProcessor, params) {
691
- this._traceProcessor = _traceProcessor;
692
- this.parentId = null;
693
- this.resourceId = null;
694
- this.endTs = null;
695
- this.error = null;
696
- this._ctx = null;
697
- this.id = _TracingSpan.nextId++;
698
- this.methodName = params.methodName;
699
- this.resourceId = _traceProcessor.getResourceId(params.instance);
700
- this.startTs = performance.now();
701
- this._showInBrowserTimeline = params.showInBrowserTimeline;
702
- this.op = params.op;
703
- this.attributes = params.attributes ?? {};
704
- if (params.parentCtx) {
705
- this._ctx = params.parentCtx.derive({
706
- attributes: {
707
- [TRACE_SPAN_ATTRIBUTE]: this.id
708
- }
709
- });
710
- const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
711
- if (typeof parentId === "number") {
712
- this.parentId = parentId;
713
- }
714
- }
715
- }
716
- get name() {
717
- const resource2 = this._traceProcessor.resources.get(this.resourceId);
718
- return resource2 ? `${resource2.sanitizedClassName}#${resource2.data.instanceId}.${this.methodName}` : this.methodName;
719
- }
720
- get ctx() {
721
- return this._ctx;
722
- }
723
- markSuccess() {
724
- this.endTs = performance.now();
725
- this._traceProcessor._flushSpan(this);
726
- if (this._showInBrowserTimeline) {
727
- this._markInBrowserTimeline();
728
- }
729
- }
730
- markError(err) {
731
- this.endTs = performance.now();
732
- this.error = serializeError(err);
733
- this._traceProcessor._flushSpan(this);
734
- if (this._showInBrowserTimeline) {
735
- this._markInBrowserTimeline();
736
- }
737
- }
738
- serialize() {
739
- return {
740
- id: this.id,
741
- resourceId: this.resourceId ?? void 0,
742
- methodName: this.methodName,
743
- parentId: this.parentId ?? void 0,
744
- startTs: this.startTs.toFixed(3),
745
- endTs: this.endTs?.toFixed(3) ?? void 0,
746
- error: this.error ?? void 0
747
- };
748
- }
749
- _markInBrowserTimeline() {
750
- if (typeof globalThis?.performance?.measure === "function") {
751
- performance.measure(this.name, {
752
- start: this.startTs,
753
- end: this.endTs
754
- });
755
- }
756
- }
757
- };
758
- var serializeError = (err) => {
759
- if (err instanceof Error) {
760
- return {
761
- name: err.name,
762
- message: err.message
763
- };
764
- }
765
- return {
766
- message: String(err)
767
- };
768
- };
769
558
  var TRACE_PROCESSOR = globalThis.TRACE_PROCESSOR ??= new TraceProcessor();
770
559
  var sanitizeValue = (value, depth, traceProcessor) => {
771
560
  switch (typeof value) {
@@ -812,33 +601,46 @@ var sanitizeValue = (value, depth, traceProcessor) => {
812
601
  return value.toString();
813
602
  }
814
603
  };
815
- var areEqualShallow = (a, b) => {
816
- for (const key in a) {
817
- if (!(key in b) || a[key] !== b[key]) {
818
- return false;
819
- }
820
- }
821
- for (const key in b) {
822
- if (!(key in a) || a[key] !== b[key]) {
823
- return false;
824
- }
825
- }
826
- return true;
827
- };
828
604
  var sanitizeClassName = (className) => {
605
+ let name = className.replace(/^_+/, "");
829
606
  const SANITIZE_REGEX = /[^_](\d+)$/;
830
- const m = className.match(SANITIZE_REGEX);
831
- if (!m) {
832
- return className;
833
- } else {
834
- 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);
835
610
  }
611
+ return name;
836
612
  };
837
613
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
838
614
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
839
615
 
840
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
+ };
841
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
+ }
842
644
  const klass = /* @__PURE__ */ (() => class extends constructor {
843
645
  constructor(...rest) {
844
646
  super(...rest);
@@ -849,6 +651,65 @@ var resource = (options) => (constructor) => {
849
651
  });
850
652
  }
851
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
+ }
852
713
  Object.defineProperty(klass, "name", {
853
714
  value: constructor.name
854
715
  });
@@ -862,45 +723,117 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
862
723
  var mark = (name) => {
863
724
  performance.mark(name);
864
725
  };
865
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
726
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
866
727
  const method = descriptor.value;
867
728
  descriptor.value = async function(...args) {
868
729
  const parentCtx = args[0] instanceof Context2 ? args[0] : null;
869
- const span2 = TRACE_PROCESSOR.traceSpan({
870
- parentCtx,
871
- methodName: propertyKey,
872
- instance: this,
873
- showInBrowserTimeline,
874
- op,
875
- attributes
876
- });
877
- const callArgs = span2.ctx ? [
878
- span2.ctx,
879
- ...args.slice(1)
880
- ] : 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
+ }
881
763
  try {
882
764
  return await method.apply(this, callArgs);
883
765
  } catch (err) {
884
- span2.markError(err);
766
+ remoteSpan?.setError?.(err);
885
767
  throw err;
886
768
  } finally {
887
- 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
+ }
888
776
  }
889
777
  };
890
778
  };
891
- var spans = /* @__PURE__ */ new Map();
779
+ var manualSpans = /* @__PURE__ */ new Map();
780
+ var manualSpanTimestamps = /* @__PURE__ */ new Map();
892
781
  var spanStart = (params) => {
893
- if (spans.has(params.id)) {
894
- 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
+ }
895
807
  }
896
- const span2 = TRACE_PROCESSOR.traceSpan(params);
897
- 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;
898
823
  };
899
824
  var spanEnd = (id) => {
900
- const span2 = spans.get(id);
901
- if (span2) {
902
- span2.markSuccess();
903
- 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);
904
837
  }
905
838
  };
906
839
  var metricsCounter = () => (target, propertyKey, descriptor) => {
@@ -1118,16 +1051,13 @@ export {
1118
1051
  DiagnosticsManager,
1119
1052
  MapCounter,
1120
1053
  RemoteMetrics,
1121
- RemoteTracing,
1122
1054
  ResourceEntry,
1055
+ TRACE_ALL_KEY,
1123
1056
  TRACE_PROCESSOR,
1124
- TRACE_SPAN_ATTRIBUTE,
1125
1057
  TimeSeriesCounter,
1126
1058
  TimeUsageCounter,
1127
1059
  TraceDiagnosticImpl,
1128
1060
  TraceProcessor,
1129
- TraceSender,
1130
- TracingSpan,
1131
1061
  UnaryCounter,
1132
1062
  getTracingContext,
1133
1063
  sanitizeClassName,