@dxos/tracing 0.8.4-main.f5c0578 → 0.8.4-main.fcc0d83b33

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