@dxos/tracing 0.8.4-main.b97322e → 0.8.4-main.bcb3aa67d6

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.
@@ -1,8 +1,8 @@
1
1
  // src/api.ts
2
- import { Context as Context2 } from "@dxos/context";
2
+ import { Context as Context3 } 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: {},
@@ -13,6 +13,7 @@ var TRACE_SPAN_ATTRIBUTE = "dxos.trace-span";
13
13
 
14
14
  // src/trace-processor.ts
15
15
  import { unrefTimeout } from "@dxos/async";
16
+ import { Context as Context2 } from "@dxos/context";
16
17
  import { LogLevel, getContextFromEntry, log } from "@dxos/log";
17
18
  import { getPrototypeSpecificInstanceId } from "@dxos/util";
18
19
 
@@ -27,6 +28,10 @@ var createId = () => Math.random().toString(36).slice(2);
27
28
  var __dxlog_file = "/__w/dxos/dxos/packages/common/tracing/src/diagnostic.ts";
28
29
  var DIAGNOSTICS_TIMEOUT = 1e4;
29
30
  var TraceDiagnosticImpl = class {
31
+ id;
32
+ fetch;
33
+ name;
34
+ _onUnregister;
30
35
  constructor(id, fetch, name, _onUnregister) {
31
36
  this.id = id;
32
37
  this.fetch = fetch;
@@ -38,11 +43,9 @@ var TraceDiagnosticImpl = class {
38
43
  }
39
44
  };
40
45
  var DiagnosticsManager = class {
41
- constructor() {
42
- this.instanceId = createId();
43
- this.registry = /* @__PURE__ */ new Map();
44
- this._instanceTag = null;
45
- }
46
+ instanceId = createId();
47
+ registry = /* @__PURE__ */ new Map();
48
+ _instanceTag = null;
46
49
  get instanceTag() {
47
50
  return this._instanceTag;
48
51
  }
@@ -115,17 +118,19 @@ var __dxlog_file2 = "/__w/dxos/dxos/packages/common/tracing/src/diagnostics-chan
115
118
  var DEFAULT_CHANNEL_NAME = "dxos-diagnostics";
116
119
  var DISCOVER_TIME = 500;
117
120
  var DiagnosticsChannel = class _DiagnosticsChannel {
121
+ _channelName;
118
122
  static get supported() {
119
123
  return globalThis.BroadcastChannel != null;
120
124
  }
125
+ _ctx = new Context(void 0, {
126
+ F: __dxlog_file2,
127
+ L: 46
128
+ });
129
+ // Separate channels becauase the client and server may be in the same process.
130
+ _serveChannel = void 0;
131
+ _clientChannel = void 0;
121
132
  constructor(_channelName = DEFAULT_CHANNEL_NAME) {
122
133
  this._channelName = _channelName;
123
- this._ctx = new Context(void 0, {
124
- F: __dxlog_file2,
125
- L: 46
126
- });
127
- this._serveChannel = void 0;
128
- this._clientChannel = void 0;
129
134
  if (_DiagnosticsChannel.supported) {
130
135
  this._serveChannel = new BroadcastChannel(_channelName);
131
136
  this._clientChannel = new BroadcastChannel(_channelName);
@@ -260,9 +265,7 @@ var DiagnosticsChannel = class _DiagnosticsChannel {
260
265
 
261
266
  // src/remote/metrics.ts
262
267
  var RemoteMetrics = class {
263
- constructor() {
264
- this._metrics = /* @__PURE__ */ new Set();
265
- }
268
+ _metrics = /* @__PURE__ */ new Set();
266
269
  registerProcessor(processor) {
267
270
  this._metrics.add(processor);
268
271
  }
@@ -281,30 +284,132 @@ var RemoteMetrics = class {
281
284
  };
282
285
 
283
286
  // src/remote/tracing.ts
287
+ var MAX_ENDED_CONTEXTS = 1e4;
284
288
  var RemoteTracing = class {
285
- constructor() {
286
- this._spanMap = /* @__PURE__ */ new Map();
287
- }
289
+ _tracing;
290
+ _spanMap = /* @__PURE__ */ new Map();
291
+ _idToSpan = /* @__PURE__ */ new Map();
292
+ /**
293
+ * Retains OTEL span contexts after spans end so that periodic/background
294
+ * operations referencing a completed parent (via `this._ctx`) still produce
295
+ * correlated child spans instead of orphaned root traces.
296
+ */
297
+ _endedSpanContexts = /* @__PURE__ */ new Map();
298
+ /**
299
+ * Buffers flushSpan calls that arrive before a processor is registered,
300
+ * so early startup spans are not silently dropped.
301
+ */
302
+ _pendingFlushes = [];
288
303
  registerProcessor(processor) {
289
304
  this._tracing = processor;
305
+ const pending = this._pendingFlushes;
306
+ this._pendingFlushes = null;
307
+ if (pending) {
308
+ for (const { span: span2, isEnd } of pending) {
309
+ this._replayFlush(span2, isEnd);
310
+ }
311
+ }
312
+ }
313
+ /** Returns the opaque OTEL context for the given DXOS span ID, if one exists. */
314
+ getSpanContext(spanId) {
315
+ const tracingSpan = this._idToSpan.get(spanId);
316
+ if (tracingSpan) {
317
+ return this._spanMap.get(tracingSpan)?.spanContext;
318
+ }
319
+ return this._endedSpanContexts.get(spanId);
320
+ }
321
+ /** Wraps execution so that the remote span is active as parent context. */
322
+ wrapExecution(span2, fn) {
323
+ const remoteSpan = this._spanMap.get(span2);
324
+ if (remoteSpan?.wrapExecution) {
325
+ return remoteSpan.wrapExecution(fn);
326
+ }
327
+ return fn();
290
328
  }
291
329
  flushSpan(span2) {
330
+ if (!span2.showInRemoteTracing) {
331
+ return;
332
+ }
292
333
  if (!this._tracing) {
334
+ this._pendingFlushes?.push({
335
+ span: span2,
336
+ isEnd: !!span2.endTs
337
+ });
293
338
  return;
294
339
  }
295
340
  if (!span2.endTs) {
296
- const remoteSpan = this._tracing.startSpan({
297
- name: span2.methodName,
298
- op: span2.op ?? "function",
299
- attributes: span2.attributes
300
- });
301
- this._spanMap.set(span2, remoteSpan);
341
+ this._startRemoteSpan(span2);
302
342
  } else {
303
- const remoteSpan = this._spanMap.get(span2);
304
- if (remoteSpan) {
305
- remoteSpan.end();
306
- this._spanMap.delete(span2);
343
+ this._endRemoteSpan(span2);
344
+ }
345
+ }
346
+ _startRemoteSpan(span2) {
347
+ let parentContext;
348
+ if (span2.parentId != null) {
349
+ const parentTracingSpan = this._idToSpan.get(span2.parentId);
350
+ if (parentTracingSpan) {
351
+ parentContext = this._spanMap.get(parentTracingSpan)?.spanContext;
352
+ }
353
+ if (parentContext == null) {
354
+ parentContext = this._endedSpanContexts.get(span2.parentId);
355
+ }
356
+ }
357
+ const attributes = {};
358
+ if (span2.sanitizedClassName) {
359
+ attributes.entryPoint = span2.sanitizedClassName;
360
+ }
361
+ for (const [key, value] of Object.entries(span2.attributes)) {
362
+ attributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
363
+ }
364
+ const remoteSpan = this._tracing.startSpan({
365
+ name: span2.name,
366
+ op: span2.op ?? "function",
367
+ attributes,
368
+ parentContext
369
+ });
370
+ this._spanMap.set(span2, remoteSpan);
371
+ this._idToSpan.set(span2.id, span2);
372
+ }
373
+ _endRemoteSpan(span2) {
374
+ const remoteSpan = this._spanMap.get(span2);
375
+ if (remoteSpan) {
376
+ if (remoteSpan.spanContext != null) {
377
+ this._endedSpanContexts.set(span2.id, remoteSpan.spanContext);
378
+ this._evictEndedContexts();
379
+ }
380
+ remoteSpan.end();
381
+ this._spanMap.delete(span2);
382
+ this._idToSpan.delete(span2.id);
383
+ }
384
+ }
385
+ /**
386
+ * Replays a buffered flush that was queued before the processor was registered.
387
+ * For spans that started AND ended before registration, replays both events.
388
+ */
389
+ _replayFlush(span2, isEnd) {
390
+ if (!isEnd) {
391
+ this._startRemoteSpan(span2);
392
+ if (span2.endTs != null) {
393
+ this._endRemoteSpan(span2);
394
+ }
395
+ } else {
396
+ if (!this._spanMap.has(span2)) {
397
+ this._startRemoteSpan(span2);
398
+ }
399
+ this._endRemoteSpan(span2);
400
+ }
401
+ }
402
+ _evictEndedContexts() {
403
+ if (this._endedSpanContexts.size <= MAX_ENDED_CONTEXTS) {
404
+ return;
405
+ }
406
+ const iterator = this._endedSpanContexts.keys();
407
+ while (this._endedSpanContexts.size > MAX_ENDED_CONTEXTS) {
408
+ const oldest = iterator.next();
409
+ if (oldest.done) {
410
+ break;
307
411
  }
412
+ this._endedSpanContexts.delete(oldest.value);
308
413
  }
309
414
  }
310
415
  };
@@ -312,6 +417,7 @@ var RemoteTracing = class {
312
417
  // src/trace-sender.ts
313
418
  import { Stream } from "@dxos/codec-protobuf/stream";
314
419
  var TraceSender = class {
420
+ _traceProcessor;
315
421
  constructor(_traceProcessor) {
316
422
  this._traceProcessor = _traceProcessor;
317
423
  }
@@ -412,6 +518,15 @@ var WeakRef = globalThis.WeakRef ?? WeakRefMock;
412
518
  // src/trace-processor.ts
413
519
  var __dxlog_file3 = "/__w/dxos/dxos/packages/common/tracing/src/trace-processor.ts";
414
520
  var ResourceEntry = class {
521
+ data;
522
+ instance;
523
+ annotation;
524
+ /**
525
+ * Sometimes bundlers mangle class names: WebFile -> WebFile2.
526
+ *
527
+ * We use a heuristic to remove the suffix.
528
+ */
529
+ sanitizedClassName;
415
530
  constructor(data, instance, annotation) {
416
531
  this.data = data;
417
532
  this.instance = instance;
@@ -429,53 +544,22 @@ var REFRESH_INTERVAL = 1e3;
429
544
  var MAX_INFO_OBJECT_DEPTH = 8;
430
545
  var IS_CLOUDFLARE_WORKERS = !!globalThis?.navigator?.userAgent?.includes("Cloudflare-Workers");
431
546
  var TraceProcessor = class {
547
+ diagnostics = new DiagnosticsManager();
548
+ diagnosticsChannel = new DiagnosticsChannel();
549
+ remoteMetrics = new RemoteMetrics();
550
+ remoteTracing = new RemoteTracing();
551
+ subscriptions = /* @__PURE__ */ new Set();
552
+ resources = /* @__PURE__ */ new Map();
553
+ resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
554
+ resourceIdList = [];
555
+ spans = /* @__PURE__ */ new Map();
556
+ spanIdList = [];
557
+ logs = [];
558
+ _instanceTag = null;
432
559
  constructor() {
433
- this.diagnostics = new DiagnosticsManager();
434
- this.diagnosticsChannel = new DiagnosticsChannel();
435
- this.remoteMetrics = new RemoteMetrics();
436
- this.remoteTracing = new RemoteTracing();
437
- this.subscriptions = /* @__PURE__ */ new Set();
438
- this.resources = /* @__PURE__ */ new Map();
439
- this.resourceInstanceIndex = /* @__PURE__ */ new WeakMap();
440
- this.resourceIdList = [];
441
- this.spans = /* @__PURE__ */ new Map();
442
- this.spanIdList = [];
443
- this.logs = [];
444
- this._instanceTag = null;
445
- this._logProcessor = (config, entry) => {
446
- switch (entry.level) {
447
- case LogLevel.ERROR:
448
- case LogLevel.WARN:
449
- case LogLevel.TRACE: {
450
- const scope = entry.meta?.S;
451
- const resource2 = this.resourceInstanceIndex.get(scope);
452
- if (!resource2) {
453
- return;
454
- }
455
- const context = getContextFromEntry(entry) ?? {};
456
- for (const key of Object.keys(context)) {
457
- context[key] = sanitizeValue(context[key], 0, this);
458
- }
459
- const entryToPush = {
460
- level: entry.level,
461
- message: entry.message ?? (entry.error ? entry.error.message ?? String(entry.error) : ""),
462
- context,
463
- timestamp: /* @__PURE__ */ new Date(),
464
- meta: {
465
- file: entry.meta?.F ?? "",
466
- line: entry.meta?.L ?? 0,
467
- resourceId: resource2.data.id
468
- }
469
- };
470
- this._pushLog(entryToPush);
471
- break;
472
- }
473
- default:
474
- }
475
- };
476
560
  log.addProcessor(this._logProcessor.bind(this), void 0, {
477
561
  F: __dxlog_file3,
478
- L: 103,
562
+ L: 104,
479
563
  S: this,
480
564
  C: (f, a) => f(...a)
481
565
  });
@@ -656,31 +740,73 @@ var TraceProcessor = class {
656
740
  subscription.newLogs.push(log2);
657
741
  }
658
742
  }
743
+ _logProcessor = (config, entry) => {
744
+ switch (entry.level) {
745
+ case LogLevel.ERROR:
746
+ case LogLevel.WARN:
747
+ case LogLevel.TRACE: {
748
+ const scope = entry.meta?.S;
749
+ const resource2 = this.resourceInstanceIndex.get(scope);
750
+ if (!resource2) {
751
+ return;
752
+ }
753
+ const context = getContextFromEntry(entry) ?? {};
754
+ for (const key of Object.keys(context)) {
755
+ context[key] = sanitizeValue(context[key], 0, this);
756
+ }
757
+ const entryToPush = {
758
+ level: entry.level,
759
+ message: entry.message ?? (entry.error ? entry.error.message ?? String(entry.error) : ""),
760
+ context,
761
+ timestamp: /* @__PURE__ */ new Date(),
762
+ meta: {
763
+ file: entry.meta?.F ?? "",
764
+ line: entry.meta?.L ?? 0,
765
+ resourceId: resource2.data.id
766
+ }
767
+ };
768
+ this._pushLog(entryToPush);
769
+ break;
770
+ }
771
+ default:
772
+ }
773
+ };
659
774
  };
660
775
  var TracingSpan = class _TracingSpan {
661
- static {
662
- this.nextId = 0;
663
- }
776
+ _traceProcessor;
777
+ static nextId = 0;
778
+ id;
779
+ parentId = null;
780
+ methodName;
781
+ resourceId = null;
782
+ op;
783
+ attributes;
784
+ startTs;
785
+ endTs = null;
786
+ error = null;
787
+ _showInBrowserTimeline;
788
+ _showInRemoteTracing;
789
+ _ctx;
664
790
  constructor(_traceProcessor, params) {
665
791
  this._traceProcessor = _traceProcessor;
666
- this.parentId = null;
667
- this.resourceId = null;
668
- this.endTs = null;
669
- this.error = null;
670
- this._ctx = null;
671
792
  this.id = _TracingSpan.nextId++;
672
793
  this.methodName = params.methodName;
673
794
  this.resourceId = _traceProcessor.getResourceId(params.instance);
674
795
  this.startTs = performance.now();
675
796
  this._showInBrowserTimeline = params.showInBrowserTimeline;
797
+ this._showInRemoteTracing = params.showInRemoteTracing ?? true;
676
798
  this.op = params.op;
677
799
  this.attributes = params.attributes ?? {};
800
+ const baseCtx = params.parentCtx ?? new Context2(void 0, {
801
+ F: __dxlog_file3,
802
+ L: 397
803
+ });
804
+ this._ctx = this._showInRemoteTracing ? baseCtx.derive({
805
+ attributes: {
806
+ [TRACE_SPAN_ATTRIBUTE]: this.id
807
+ }
808
+ }) : baseCtx.derive();
678
809
  if (params.parentCtx) {
679
- this._ctx = params.parentCtx.derive({
680
- attributes: {
681
- [TRACE_SPAN_ATTRIBUTE]: this.id
682
- }
683
- });
684
810
  const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
685
811
  if (typeof parentId === "number") {
686
812
  this.parentId = parentId;
@@ -691,9 +817,17 @@ var TracingSpan = class _TracingSpan {
691
817
  const resource2 = this._traceProcessor.resources.get(this.resourceId);
692
818
  return resource2 ? `${resource2.sanitizedClassName}#${resource2.data.instanceId}.${this.methodName}` : this.methodName;
693
819
  }
820
+ /** Sanitized class name of the owning resource, if available. */
821
+ get sanitizedClassName() {
822
+ const resource2 = this.resourceId != null ? this._traceProcessor.resources.get(this.resourceId) : void 0;
823
+ return resource2?.sanitizedClassName;
824
+ }
694
825
  get ctx() {
695
826
  return this._ctx;
696
827
  }
828
+ get showInRemoteTracing() {
829
+ return this._showInRemoteTracing;
830
+ }
697
831
  markSuccess() {
698
832
  this.endTs = performance.now();
699
833
  this._traceProcessor._flushSpan(this);
@@ -800,13 +934,13 @@ var areEqualShallow = (a, b) => {
800
934
  return true;
801
935
  };
802
936
  var sanitizeClassName = (className) => {
937
+ let name = className.replace(/^_+/, "");
803
938
  const SANITIZE_REGEX = /[^_](\d+)$/;
804
- const m = className.match(SANITIZE_REGEX);
805
- if (!m) {
806
- return className;
807
- } else {
808
- return className.slice(0, -m[1].length);
939
+ const m = name.match(SANITIZE_REGEX);
940
+ if (m) {
941
+ name = name.slice(0, -m[1].length);
809
942
  }
943
+ return name;
810
944
  };
811
945
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
812
946
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
@@ -836,30 +970,33 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
836
970
  var mark = (name) => {
837
971
  performance.mark(name);
838
972
  };
839
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
973
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
840
974
  const method = descriptor.value;
841
975
  descriptor.value = async function(...args) {
842
- const parentCtx = args[0] instanceof Context2 ? args[0] : null;
976
+ const explicitCtx = args[0] instanceof Context3 ? args[0] : null;
843
977
  const span2 = TRACE_PROCESSOR.traceSpan({
844
- parentCtx,
978
+ parentCtx: explicitCtx,
845
979
  methodName: propertyKey,
846
980
  instance: this,
847
981
  showInBrowserTimeline,
982
+ showInRemoteTracing,
848
983
  op,
849
984
  attributes
850
985
  });
851
- const callArgs = span2.ctx ? [
986
+ const callArgs = explicitCtx ? [
852
987
  span2.ctx,
853
988
  ...args.slice(1)
854
989
  ] : args;
855
- try {
856
- return await method.apply(this, callArgs);
857
- } catch (err) {
858
- span2.markError(err);
859
- throw err;
860
- } finally {
861
- span2.markSuccess();
862
- }
990
+ return TRACE_PROCESSOR.remoteTracing.wrapExecution(span2, async () => {
991
+ try {
992
+ return await method.apply(this, callArgs);
993
+ } catch (err) {
994
+ span2.markError(err);
995
+ throw err;
996
+ } finally {
997
+ span2.markSuccess();
998
+ }
999
+ });
863
1000
  };
864
1001
  };
865
1002
  var spans = /* @__PURE__ */ new Map();
@@ -901,6 +1038,11 @@ var trace = {
901
1038
 
902
1039
  // src/metrics/base.ts
903
1040
  var BaseCounter = class {
1041
+ /**
1042
+ * @internal
1043
+ */
1044
+ _instance;
1045
+ name;
904
1046
  /**
905
1047
  * @internal
906
1048
  */
@@ -914,9 +1056,10 @@ var BaseCounter = class {
914
1056
 
915
1057
  // src/metrics/unary-counter.ts
916
1058
  var UnaryCounter = class extends BaseCounter {
1059
+ value = 0;
1060
+ units;
917
1061
  constructor({ units } = {}) {
918
1062
  super();
919
- this.value = 0;
920
1063
  this.units = units;
921
1064
  }
922
1065
  inc(by = 1) {
@@ -936,11 +1079,12 @@ var UnaryCounter = class extends BaseCounter {
936
1079
  // src/metrics/time-series-counter.ts
937
1080
  var MAX_BUCKETS = 60;
938
1081
  var TimeSeriesCounter = class extends BaseCounter {
1082
+ _currentValue = 0;
1083
+ _totalValue = 0;
1084
+ _buckets = [];
1085
+ units;
939
1086
  constructor({ units } = {}) {
940
1087
  super();
941
- this._currentValue = 0;
942
- this._totalValue = 0;
943
- this._buckets = [];
944
1088
  this.units = units;
945
1089
  }
946
1090
  inc(by = 1) {
@@ -976,13 +1120,10 @@ var TimeSeriesCounter = class extends BaseCounter {
976
1120
  // src/metrics/time-usage-counter.ts
977
1121
  var MAX_BUCKETS2 = 60;
978
1122
  var TimeUsageCounter = class extends BaseCounter {
979
- constructor() {
980
- super(...arguments);
981
- this._currentValue = 0;
982
- this._totalValue = 0;
983
- this._buckets = [];
984
- this._lastTickTime = performance.now();
985
- }
1123
+ _currentValue = 0;
1124
+ _totalValue = 0;
1125
+ _buckets = [];
1126
+ _lastTickTime = performance.now();
986
1127
  record(time) {
987
1128
  this._currentValue += time;
988
1129
  this._totalValue += time;
@@ -1027,9 +1168,10 @@ var TimeUsageCounter = class extends BaseCounter {
1027
1168
 
1028
1169
  // src/metrics/map-counter.ts
1029
1170
  var MapCounter = class extends BaseCounter {
1171
+ values = /* @__PURE__ */ new Map();
1172
+ units;
1030
1173
  constructor({ units } = {}) {
1031
1174
  super();
1032
- this.values = /* @__PURE__ */ new Map();
1033
1175
  this.units = units;
1034
1176
  }
1035
1177
  inc(key, by = 1) {
@@ -1052,6 +1194,7 @@ var MapCounter = class extends BaseCounter {
1052
1194
 
1053
1195
  // src/metrics/custom-counter.ts
1054
1196
  var CustomCounter = class extends BaseCounter {
1197
+ _getData;
1055
1198
  constructor(_getData) {
1056
1199
  super(), this._getData = _getData;
1057
1200
  }