@dxos/tracing 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0

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
 
@@ -283,29 +284,132 @@ var RemoteMetrics = class {
283
284
  };
284
285
 
285
286
  // src/remote/tracing.ts
287
+ var MAX_ENDED_CONTEXTS = 1e4;
286
288
  var RemoteTracing = class {
287
289
  _tracing;
288
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 = [];
289
303
  registerProcessor(processor) {
290
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();
291
328
  }
292
329
  flushSpan(span2) {
330
+ if (!span2.showInRemoteTracing) {
331
+ return;
332
+ }
293
333
  if (!this._tracing) {
334
+ this._pendingFlushes?.push({
335
+ span: span2,
336
+ isEnd: !!span2.endTs
337
+ });
294
338
  return;
295
339
  }
296
340
  if (!span2.endTs) {
297
- const remoteSpan = this._tracing.startSpan({
298
- name: span2.methodName,
299
- op: span2.op ?? "function",
300
- attributes: span2.attributes
301
- });
302
- this._spanMap.set(span2, remoteSpan);
341
+ this._startRemoteSpan(span2);
342
+ } else {
343
+ this._endRemoteSpan(span2);
344
+ }
345
+ }
346
+ _startRemoteSpan(span2) {
347
+ let parentContext;
348
+ if (span2.parentId != null) {
349
+ const parentTracingSpan = this._idToSpan.get(span2.parentId);
350
+ if (parentTracingSpan) {
351
+ parentContext = this._spanMap.get(parentTracingSpan)?.spanContext;
352
+ }
353
+ if (parentContext == null) {
354
+ parentContext = this._endedSpanContexts.get(span2.parentId);
355
+ }
356
+ }
357
+ const attributes = {};
358
+ if (span2.sanitizedClassName) {
359
+ attributes.entryPoint = span2.sanitizedClassName;
360
+ }
361
+ for (const [key, value] of Object.entries(span2.attributes)) {
362
+ attributes[key.startsWith("ctx.") ? key : `ctx.${key}`] = value;
363
+ }
364
+ const remoteSpan = this._tracing.startSpan({
365
+ name: span2.name,
366
+ op: span2.op ?? "function",
367
+ attributes,
368
+ parentContext
369
+ });
370
+ this._spanMap.set(span2, remoteSpan);
371
+ this._idToSpan.set(span2.id, span2);
372
+ }
373
+ _endRemoteSpan(span2) {
374
+ const remoteSpan = this._spanMap.get(span2);
375
+ if (remoteSpan) {
376
+ if (remoteSpan.spanContext != null) {
377
+ this._endedSpanContexts.set(span2.id, remoteSpan.spanContext);
378
+ this._evictEndedContexts();
379
+ }
380
+ remoteSpan.end();
381
+ this._spanMap.delete(span2);
382
+ this._idToSpan.delete(span2.id);
383
+ }
384
+ }
385
+ /**
386
+ * Replays a buffered flush that was queued before the processor was registered.
387
+ * For spans that started AND ended before registration, replays both events.
388
+ */
389
+ _replayFlush(span2, isEnd) {
390
+ if (!isEnd) {
391
+ this._startRemoteSpan(span2);
392
+ if (span2.endTs != null) {
393
+ this._endRemoteSpan(span2);
394
+ }
303
395
  } else {
304
- const remoteSpan = this._spanMap.get(span2);
305
- if (remoteSpan) {
306
- remoteSpan.end();
307
- this._spanMap.delete(span2);
396
+ if (!this._spanMap.has(span2)) {
397
+ this._startRemoteSpan(span2);
308
398
  }
399
+ this._endRemoteSpan(span2);
400
+ }
401
+ }
402
+ _evictEndedContexts() {
403
+ if (this._endedSpanContexts.size <= MAX_ENDED_CONTEXTS) {
404
+ return;
405
+ }
406
+ const iterator = this._endedSpanContexts.keys();
407
+ while (this._endedSpanContexts.size > MAX_ENDED_CONTEXTS) {
408
+ const oldest = iterator.next();
409
+ if (oldest.done) {
410
+ break;
411
+ }
412
+ this._endedSpanContexts.delete(oldest.value);
309
413
  }
310
414
  }
311
415
  };
@@ -455,7 +559,7 @@ var TraceProcessor = class {
455
559
  constructor() {
456
560
  log.addProcessor(this._logProcessor.bind(this), void 0, {
457
561
  F: __dxlog_file3,
458
- L: 103,
562
+ L: 104,
459
563
  S: this,
460
564
  C: (f, a) => f(...a)
461
565
  });
@@ -681,7 +785,8 @@ var TracingSpan = class _TracingSpan {
681
785
  endTs = null;
682
786
  error = null;
683
787
  _showInBrowserTimeline;
684
- _ctx = null;
788
+ _showInRemoteTracing;
789
+ _ctx;
685
790
  constructor(_traceProcessor, params) {
686
791
  this._traceProcessor = _traceProcessor;
687
792
  this.id = _TracingSpan.nextId++;
@@ -689,14 +794,19 @@ var TracingSpan = class _TracingSpan {
689
794
  this.resourceId = _traceProcessor.getResourceId(params.instance);
690
795
  this.startTs = performance.now();
691
796
  this._showInBrowserTimeline = params.showInBrowserTimeline;
797
+ this._showInRemoteTracing = params.showInRemoteTracing ?? true;
692
798
  this.op = params.op;
693
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();
694
809
  if (params.parentCtx) {
695
- this._ctx = params.parentCtx.derive({
696
- attributes: {
697
- [TRACE_SPAN_ATTRIBUTE]: this.id
698
- }
699
- });
700
810
  const parentId = params.parentCtx.getAttribute(TRACE_SPAN_ATTRIBUTE);
701
811
  if (typeof parentId === "number") {
702
812
  this.parentId = parentId;
@@ -707,9 +817,17 @@ var TracingSpan = class _TracingSpan {
707
817
  const resource2 = this._traceProcessor.resources.get(this.resourceId);
708
818
  return resource2 ? `${resource2.sanitizedClassName}#${resource2.data.instanceId}.${this.methodName}` : this.methodName;
709
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
+ }
710
825
  get ctx() {
711
826
  return this._ctx;
712
827
  }
828
+ get showInRemoteTracing() {
829
+ return this._showInRemoteTracing;
830
+ }
713
831
  markSuccess() {
714
832
  this.endTs = performance.now();
715
833
  this._traceProcessor._flushSpan(this);
@@ -816,13 +934,13 @@ var areEqualShallow = (a, b) => {
816
934
  return true;
817
935
  };
818
936
  var sanitizeClassName = (className) => {
937
+ let name = className.replace(/^_+/, "");
819
938
  const SANITIZE_REGEX = /[^_](\d+)$/;
820
- const m = className.match(SANITIZE_REGEX);
821
- if (!m) {
822
- return className;
823
- } else {
824
- 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);
825
942
  }
943
+ return name;
826
944
  };
827
945
  var isSetLike = (value) => value instanceof Set || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexSet";
828
946
  var isMapLike = (value) => value instanceof Map || typeof value === "object" && value !== null && Object.getPrototypeOf(value).constructor.name === "ComplexMap";
@@ -852,30 +970,33 @@ var info = (opts = {}) => (target, propertyKey, descriptor) => {
852
970
  var mark = (name) => {
853
971
  performance.mark(name);
854
972
  };
855
- var span = ({ showInBrowserTimeline = false, op, attributes } = {}) => (target, propertyKey, descriptor) => {
973
+ var span = ({ showInBrowserTimeline = false, showInRemoteTracing = true, op, attributes } = {}) => (target, propertyKey, descriptor) => {
856
974
  const method = descriptor.value;
857
975
  descriptor.value = async function(...args) {
858
- const parentCtx = args[0] instanceof Context2 ? args[0] : null;
976
+ const explicitCtx = args[0] instanceof Context3 ? args[0] : null;
859
977
  const span2 = TRACE_PROCESSOR.traceSpan({
860
- parentCtx,
978
+ parentCtx: explicitCtx,
861
979
  methodName: propertyKey,
862
980
  instance: this,
863
981
  showInBrowserTimeline,
982
+ showInRemoteTracing,
864
983
  op,
865
984
  attributes
866
985
  });
867
- const callArgs = span2.ctx ? [
986
+ const callArgs = explicitCtx ? [
868
987
  span2.ctx,
869
988
  ...args.slice(1)
870
989
  ] : args;
871
- try {
872
- return await method.apply(this, callArgs);
873
- } catch (err) {
874
- span2.markError(err);
875
- throw err;
876
- } finally {
877
- span2.markSuccess();
878
- }
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
+ });
879
1000
  };
880
1001
  };
881
1002
  var spans = /* @__PURE__ */ new Map();