@autter/runtime-node 1.2.1 → 1.3.0

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.
package/dist/index.cjs CHANGED
@@ -238,6 +238,7 @@ function createBrowserRelayHandler(opts) {
238
238
 
239
239
  // src/server.ts
240
240
  var import_node_http = require("http");
241
+ var import_node_crypto = require("crypto");
241
242
  var import_api = require("@opentelemetry/api");
242
243
  var import_core2 = require("@opentelemetry/core");
243
244
  var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
@@ -764,12 +765,16 @@ function withSampledFlag(span) {
764
765
  };
765
766
  }
766
767
  var ErrorTraceRetentionProcessor = class {
767
- constructor(inner) {
768
+ constructor(inner, retainErrors = true, slowThresholdMs = 0) {
768
769
  this.inner = inner;
770
+ this.retainErrors = retainErrors;
771
+ this.slowThresholdMs = slowThresholdMs;
769
772
  this.sweeper = setInterval(() => this.sweep(), RETENTION_SWEEP_INTERVAL_MS);
770
773
  this.sweeper.unref?.();
771
774
  }
772
775
  inner;
776
+ retainErrors;
777
+ slowThresholdMs;
773
778
  traces = /* @__PURE__ */ new Map();
774
779
  bufferedSpans = 0;
775
780
  sweeper;
@@ -781,7 +786,7 @@ var ErrorTraceRetentionProcessor = class {
781
786
  const entry = this.entryFor(ctx.traceId);
782
787
  if (entry.retained) {
783
788
  this.forward(span);
784
- } else if (spanIndicatesError(span)) {
789
+ } else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind === import_api.SpanKind.SERVER && span.duration[0] * 1e3 + span.duration[1] / 1e6 >= this.slowThresholdMs) {
785
790
  entry.retained = true;
786
791
  this.flush(entry);
787
792
  this.forward(span);
@@ -815,6 +820,10 @@ var ErrorTraceRetentionProcessor = class {
815
820
  entryFor(traceId) {
816
821
  let entry = this.traces.get(traceId);
817
822
  if (!entry) {
823
+ if (this.traces.size >= RETENTION_MAX_BUFFERED_SPANS) {
824
+ const oldest = this.traces.keys().next().value;
825
+ if (oldest) this.drop(oldest);
826
+ }
818
827
  entry = { spans: [], retained: false, firstSeenAtMs: Date.now() };
819
828
  this.traces.set(traceId, entry);
820
829
  }
@@ -996,6 +1005,7 @@ function initAutterServer(options) {
996
1005
  activeRedactor = makeRedactor(options.redactAttributes ?? true);
997
1006
  const resource = new import_resources.Resource({
998
1007
  [import_semantic_conventions.ATTR_SERVICE_NAME]: options.service,
1008
+ "service.instance.id": (0, import_node_crypto.randomUUID)(),
999
1009
  ...options.release ? { [import_semantic_conventions.ATTR_SERVICE_VERSION]: options.release } : {},
1000
1010
  "deployment.environment": environment,
1001
1011
  // Tells the ingester request metrics arrive on the metrics pipe, so
@@ -1007,15 +1017,19 @@ function initAutterServer(options) {
1007
1017
  root: new import_sdk_trace_base.TraceIdRatioBasedSampler(options.traceSampleRate ?? 0.01)
1008
1018
  });
1009
1019
  const retainOnError = options.retainTracesOnError !== false;
1010
- let sampler = retainOnError ? new RecordUnsampledSampler(headSampler) : headSampler;
1020
+ const slowThresholdMs = Number.isFinite(options.retainTracesAboveMs) ? Math.max(0, options.retainTracesAboveMs) : 0;
1021
+ const retainTraces = retainOnError || slowThresholdMs > 0;
1022
+ let sampler = retainTraces ? new RecordUnsampledSampler(headSampler) : headSampler;
1011
1023
  if (options.llmTracing !== false) sampler = new LlmAwareSampler(sampler);
1012
- const errorTraceBuffer = retainOnError ? new ErrorTraceRetentionProcessor(
1024
+ const errorTraceBuffer = retainTraces ? new ErrorTraceRetentionProcessor(
1013
1025
  new import_sdk_trace_base.BatchSpanProcessor(
1014
1026
  new CountingExporter(
1015
1027
  new import_exporter_trace_otlp_http.OTLPTraceExporter({ url: `${endpoint}/v1/traces`, headers })
1016
1028
  ),
1017
1029
  { scheduledDelayMillis: 2e3 }
1018
- )
1030
+ ),
1031
+ retainOnError,
1032
+ slowThresholdMs
1019
1033
  ) : null;
1020
1034
  const mainSpanProcessor = new import_sdk_trace_base.BatchSpanProcessor(
1021
1035
  new CountingExporter(
@@ -1042,6 +1056,14 @@ function initAutterServer(options) {
1042
1056
  ...errorTraceBuffer ? [errorTraceBuffer] : []
1043
1057
  ],
1044
1058
  metricReader,
1059
+ views: [
1060
+ new import_sdk_metrics.View({ instrumentName: "http.server.duration", aggregation: new import_sdk_metrics.ExplicitBucketHistogramAggregation(
1061
+ [5, 10, 25, 50, 100, 250, 500, 750, 1e3, 1500, 2e3, 3e3, 5e3, 7500, 1e4, 15e3, 2e4, 3e4, 6e4, 12e4]
1062
+ ) }),
1063
+ new import_sdk_metrics.View({ instrumentName: "http.server.request.duration", aggregation: new import_sdk_metrics.ExplicitBucketHistogramAggregation(
1064
+ [5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 7.5, 10, 15, 20, 30, 60, 120]
1065
+ ) })
1066
+ ],
1045
1067
  instrumentations: [
1046
1068
  new import_instrumentation_http.HttpInstrumentation({ responseHook: captureExpressRoute }),
1047
1069
  ...options.instrumentations ?? []
package/dist/index.d.cts CHANGED
@@ -116,6 +116,7 @@ interface AutterServerOptions {
116
116
  * Default true.
117
117
  */
118
118
  retainTracesOnError?: boolean;
119
+ retainTracesAboveMs?: number;
119
120
  /** Metric export interval. Default 60_000 ms. */
120
121
  metricIntervalMs?: number;
121
122
  /** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
package/dist/index.d.ts CHANGED
@@ -116,6 +116,7 @@ interface AutterServerOptions {
116
116
  * Default true.
117
117
  */
118
118
  retainTracesOnError?: boolean;
119
+ retainTracesAboveMs?: number;
119
120
  /** Metric export interval. Default 60_000 ms. */
120
121
  metricIntervalMs?: number;
121
122
  /** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
package/dist/index.js CHANGED
@@ -199,6 +199,7 @@ function createBrowserRelayHandler(opts) {
199
199
 
200
200
  // src/server.ts
201
201
  import { ServerResponse } from "http";
202
+ import { randomUUID } from "crypto";
202
203
  import {
203
204
  context,
204
205
  trace,
@@ -214,7 +215,7 @@ import {
214
215
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
215
216
  import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
216
217
  import { Resource } from "@opentelemetry/resources";
217
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
218
+ import { ExplicitBucketHistogramAggregation, PeriodicExportingMetricReader, View } from "@opentelemetry/sdk-metrics";
218
219
  import { NodeSDK } from "@opentelemetry/sdk-node";
219
220
  import {
220
221
  BatchSpanProcessor,
@@ -744,12 +745,16 @@ function withSampledFlag(span) {
744
745
  };
745
746
  }
746
747
  var ErrorTraceRetentionProcessor = class {
747
- constructor(inner) {
748
+ constructor(inner, retainErrors = true, slowThresholdMs = 0) {
748
749
  this.inner = inner;
750
+ this.retainErrors = retainErrors;
751
+ this.slowThresholdMs = slowThresholdMs;
749
752
  this.sweeper = setInterval(() => this.sweep(), RETENTION_SWEEP_INTERVAL_MS);
750
753
  this.sweeper.unref?.();
751
754
  }
752
755
  inner;
756
+ retainErrors;
757
+ slowThresholdMs;
753
758
  traces = /* @__PURE__ */ new Map();
754
759
  bufferedSpans = 0;
755
760
  sweeper;
@@ -761,7 +766,7 @@ var ErrorTraceRetentionProcessor = class {
761
766
  const entry = this.entryFor(ctx.traceId);
762
767
  if (entry.retained) {
763
768
  this.forward(span);
764
- } else if (spanIndicatesError(span)) {
769
+ } else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind === SpanKind.SERVER && span.duration[0] * 1e3 + span.duration[1] / 1e6 >= this.slowThresholdMs) {
765
770
  entry.retained = true;
766
771
  this.flush(entry);
767
772
  this.forward(span);
@@ -795,6 +800,10 @@ var ErrorTraceRetentionProcessor = class {
795
800
  entryFor(traceId) {
796
801
  let entry = this.traces.get(traceId);
797
802
  if (!entry) {
803
+ if (this.traces.size >= RETENTION_MAX_BUFFERED_SPANS) {
804
+ const oldest = this.traces.keys().next().value;
805
+ if (oldest) this.drop(oldest);
806
+ }
798
807
  entry = { spans: [], retained: false, firstSeenAtMs: Date.now() };
799
808
  this.traces.set(traceId, entry);
800
809
  }
@@ -976,6 +985,7 @@ function initAutterServer(options) {
976
985
  activeRedactor = makeRedactor(options.redactAttributes ?? true);
977
986
  const resource = new Resource({
978
987
  [ATTR_SERVICE_NAME]: options.service,
988
+ "service.instance.id": randomUUID(),
979
989
  ...options.release ? { [ATTR_SERVICE_VERSION]: options.release } : {},
980
990
  "deployment.environment": environment,
981
991
  // Tells the ingester request metrics arrive on the metrics pipe, so
@@ -987,15 +997,19 @@ function initAutterServer(options) {
987
997
  root: new TraceIdRatioBasedSampler(options.traceSampleRate ?? 0.01)
988
998
  });
989
999
  const retainOnError = options.retainTracesOnError !== false;
990
- let sampler = retainOnError ? new RecordUnsampledSampler(headSampler) : headSampler;
1000
+ const slowThresholdMs = Number.isFinite(options.retainTracesAboveMs) ? Math.max(0, options.retainTracesAboveMs) : 0;
1001
+ const retainTraces = retainOnError || slowThresholdMs > 0;
1002
+ let sampler = retainTraces ? new RecordUnsampledSampler(headSampler) : headSampler;
991
1003
  if (options.llmTracing !== false) sampler = new LlmAwareSampler(sampler);
992
- const errorTraceBuffer = retainOnError ? new ErrorTraceRetentionProcessor(
1004
+ const errorTraceBuffer = retainTraces ? new ErrorTraceRetentionProcessor(
993
1005
  new BatchSpanProcessor(
994
1006
  new CountingExporter(
995
1007
  new OTLPTraceExporter({ url: `${endpoint}/v1/traces`, headers })
996
1008
  ),
997
1009
  { scheduledDelayMillis: 2e3 }
998
- )
1010
+ ),
1011
+ retainOnError,
1012
+ slowThresholdMs
999
1013
  ) : null;
1000
1014
  const mainSpanProcessor = new BatchSpanProcessor(
1001
1015
  new CountingExporter(
@@ -1022,6 +1036,14 @@ function initAutterServer(options) {
1022
1036
  ...errorTraceBuffer ? [errorTraceBuffer] : []
1023
1037
  ],
1024
1038
  metricReader,
1039
+ views: [
1040
+ new View({ instrumentName: "http.server.duration", aggregation: new ExplicitBucketHistogramAggregation(
1041
+ [5, 10, 25, 50, 100, 250, 500, 750, 1e3, 1500, 2e3, 3e3, 5e3, 7500, 1e4, 15e3, 2e4, 3e4, 6e4, 12e4]
1042
+ ) }),
1043
+ new View({ instrumentName: "http.server.request.duration", aggregation: new ExplicitBucketHistogramAggregation(
1044
+ [5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 7.5, 10, 15, 20, 30, 60, 120]
1045
+ ) })
1046
+ ],
1025
1047
  instrumentations: [
1026
1048
  new HttpInstrumentation({ responseHook: captureExpressRoute }),
1027
1049
  ...options.instrumentations ?? []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autter/runtime-node",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Autter Runtime for Node.js: same-origin browser relay handler + curated OpenTelemetry server tracker",
5
5
  "license": "MIT",
6
6
  "type": "module",