@stainlessdev/xray-core 0.3.1 → 0.4.0-branch.bg-rework-exporters.8c5264b

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
@@ -159,13 +159,10 @@ var defaultRoute = {
159
159
  normalize: true,
160
160
  normalizer: normalizeRoutePattern
161
161
  };
162
- var DEFAULT_ENDPOINT_URL = "http://localhost:4318";
163
162
  var defaultExporterBase = {
164
- endpointUrl: DEFAULT_ENDPOINT_URL,
165
163
  headers: {},
166
- timeoutMs: 5e3,
167
- spanProcessor: "batch",
168
- sampler: { type: "ratio", ratio: 1 }
164
+ timeoutMs: 3e4,
165
+ spanProcessor: "batch"
169
166
  };
170
167
  var XrayConfigError = class extends Error {
171
168
  constructor(code, message) {
@@ -258,29 +255,23 @@ function normalizeExporter(endpointUrl, cfg) {
258
255
  const resolvedEndpoint = normalizeExporterEndpoint(cfg?.endpointUrl ?? endpointUrl);
259
256
  const rawHeaders = cfg?.headers ?? defaultExporterBase.headers ?? {};
260
257
  const parsed = applyEndpointAuth(resolvedEndpoint, rawHeaders);
261
- const enabled = cfg?.enabled ?? true;
262
258
  const exporter = {
263
- ...defaultExporterBase,
264
- ...cfg,
265
- enabled,
266
259
  endpointUrl: parsed.endpointUrl,
267
260
  headers: parsed.headers,
268
261
  timeoutMs: cfg?.timeoutMs ?? defaultExporterBase.timeoutMs,
269
- spanProcessor: cfg?.spanProcessor ?? defaultExporterBase.spanProcessor,
270
- sampler: cfg?.sampler ?? defaultExporterBase.sampler
262
+ spanProcessor: cfg?.spanProcessor ?? defaultExporterBase.spanProcessor
271
263
  };
272
- if (exporter.sampler.type === "ratio") {
273
- const ratio = exporter.sampler.ratio ?? 1;
274
- if (!Number.isFinite(ratio) || ratio < 0 || ratio > 1) {
275
- throw new XrayConfigError("INVALID_EXPORTER", "sampler.ratio must be between 0 and 1");
276
- }
277
- exporter.sampler = { type: "ratio", ratio };
278
- }
279
264
  return exporter;
280
265
  }
281
266
  function normalizeExporterEndpoint(endpointUrl) {
282
267
  const envUrl = typeof process !== "undefined" ? process.env?.["STAINLESS_XRAY_ENDPOINT_URL"] : void 0;
283
- const resolved = endpointUrl ?? envUrl ?? DEFAULT_ENDPOINT_URL;
268
+ const resolved = endpointUrl ?? envUrl;
269
+ if (!resolved || !resolved.trim()) {
270
+ throw new XrayConfigError(
271
+ "INVALID_CONFIG",
272
+ "endpointUrl is required (set endpointUrl or STAINLESS_XRAY_ENDPOINT_URL)"
273
+ );
274
+ }
284
275
  const trimmed = resolved.trim();
285
276
  const withoutTrailingSlash = trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
286
277
  if (withoutTrailingSlash.endsWith("/v1/traces")) {
@@ -750,6 +741,9 @@ function sanitizeHeaderValues(headers) {
750
741
  return sanitized;
751
742
  }
752
743
 
744
+ // src/attributes.ts
745
+ var import_incubating = require("@opentelemetry/semantic-conventions/incubating");
746
+
753
747
  // src/attrkey.ts
754
748
  var AttributeKeyRequestBody = "http.request.body";
755
749
  var AttributeKeyRequestBodyEncoding = "http.request.body.encoding";
@@ -761,14 +755,6 @@ var AttributeKeyResponseBodyTruncated = "http.response.body.truncated";
761
755
  var AttributeKeySpanDrop = "stainlessxray.internal.drop";
762
756
 
763
757
  // src/attributes.ts
764
- var attributeKeyEndUserId = "enduser.id";
765
- var attributeKeyHttpRequestBodySize = "http.request.body.size";
766
- var attributeKeyHttpRequestMethod = "http.request.method";
767
- var attributeKeyHttpResponseBodySize = "http.response.body.size";
768
- var attributeKeyHttpResponseStatusCode = "http.response.status_code";
769
- var attributeKeyHttpRoute = "http.route";
770
- var attributeKeyUrlPath = "url.path";
771
- var attributeKeyUrlFull = "url.full";
772
758
  function setHeaderAttributes(span, headers, prefix) {
773
759
  if (!headers) {
774
760
  return;
@@ -783,12 +769,12 @@ function setHeaderAttributes(span, headers, prefix) {
783
769
  }
784
770
  }
785
771
  function setRequestAttributes(span, method, urlFull) {
786
- span.setAttribute(attributeKeyHttpRequestMethod, method);
772
+ span.setAttribute(import_incubating.ATTR_HTTP_REQUEST_METHOD, method);
787
773
  if (urlFull) {
788
- span.setAttribute(attributeKeyUrlFull, urlFull);
774
+ span.setAttribute(import_incubating.ATTR_URL_FULL, urlFull);
789
775
  const path = extractPath(urlFull);
790
776
  if (path) {
791
- span.setAttribute(attributeKeyUrlPath, path);
777
+ span.setAttribute(import_incubating.ATTR_URL_PATH, path);
792
778
  }
793
779
  }
794
780
  }
@@ -811,7 +797,7 @@ function setRequestBodyAttributes(span, body) {
811
797
  }
812
798
  }
813
799
  function setRequestBodySizeAttribute(span, size) {
814
- span.setAttribute(attributeKeyHttpRequestBodySize, size);
800
+ span.setAttribute(import_incubating.ATTR_HTTP_REQUEST_BODY_SIZE, size);
815
801
  }
816
802
  function setResponseBodyAttributes(span, body) {
817
803
  if (!body.value) {
@@ -824,18 +810,18 @@ function setResponseBodyAttributes(span, body) {
824
810
  }
825
811
  }
826
812
  function setResponseBodySizeAttribute(span, size) {
827
- span.setAttribute(attributeKeyHttpResponseBodySize, size);
813
+ span.setAttribute(import_incubating.ATTR_HTTP_RESPONSE_BODY_SIZE, size);
828
814
  }
829
815
  function setResponseStatusAttribute(span, statusCode) {
830
- span.setAttribute(attributeKeyHttpResponseStatusCode, statusCode);
816
+ span.setAttribute(import_incubating.ATTR_HTTP_RESPONSE_STATUS_CODE, statusCode);
831
817
  }
832
818
  function setRouteAttribute(span, route) {
833
819
  if (route) {
834
- span.setAttribute(attributeKeyHttpRoute, route);
820
+ span.setAttribute(import_incubating.ATTR_HTTP_ROUTE, route);
835
821
  }
836
822
  }
837
823
  function setUserIdAttribute(span, userId) {
838
- span.setAttribute(attributeKeyEndUserId, userId);
824
+ span.setAttribute(import_incubating.ATTR_USER_ID, userId);
839
825
  }
840
826
  function setRequestIdAttribute(span, requestId) {
841
827
  span.setAttribute(AttributeKeyRequestID, requestId);
@@ -843,16 +829,11 @@ function setRequestIdAttribute(span, requestId) {
843
829
 
844
830
  // src/otel.ts
845
831
  var import_api = require("@opentelemetry/api");
846
- var import_core = require("@opentelemetry/core");
847
832
  var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
848
833
  var import_resources = require("@opentelemetry/resources");
849
834
  var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
850
- var import_otlp_transformer = require("@opentelemetry/otlp-transformer");
851
835
  var defaultAttributeCountLimit = 128;
852
- function createTracerProvider(config) {
853
- if (!config.exporter.enabled || !config.exporter.endpointUrl) {
854
- return null;
855
- }
836
+ function createTracerProvider(config, exporter) {
856
837
  if (config.exporter.endpointUrl.startsWith("http://")) {
857
838
  import_api.diag.warn("xray: OTLP endpoint uses plaintext HTTP");
858
839
  }
@@ -863,12 +844,6 @@ function createTracerProvider(config) {
863
844
  [import_semantic_conventions.ATTR_TELEMETRY_SDK_NAME]: "stainless-xray",
864
845
  [import_semantic_conventions.ATTR_TELEMETRY_SDK_VERSION]: sdkVersion()
865
846
  });
866
- const exporter = new FetchSpanExporter({
867
- endpointUrl: config.exporter.endpointUrl,
868
- headers: config.exporter.headers ?? {},
869
- timeoutMillis: config.exporter.timeoutMs
870
- });
871
- const sampler = createSampler(config);
872
847
  const spanProcessor = createSpanProcessor(config.exporter.spanProcessor, exporter);
873
848
  const dropProcessor = new DropFilterSpanProcessor(spanProcessor);
874
849
  const provider = new import_sdk_trace_base.BasicTracerProvider({
@@ -878,7 +853,7 @@ function createTracerProvider(config) {
878
853
  attributeValueLengthLimit
879
854
  },
880
855
  resource,
881
- sampler,
856
+ sampler: new import_sdk_trace_base.AlwaysOnSampler(),
882
857
  spanLimits: {
883
858
  attributeCountLimit: defaultAttributeCountLimit,
884
859
  attributePerEventCountLimit: defaultAttributeCountLimit,
@@ -927,69 +902,6 @@ var DropFilterSpanProcessor = class {
927
902
  return this.next.shutdown();
928
903
  }
929
904
  };
930
- var FetchSpanExporter = class {
931
- constructor(options) {
932
- this.endpointUrl = options.endpointUrl;
933
- this.headers = { ...options.headers };
934
- this.timeoutMillis = options.timeoutMillis;
935
- this.isShutdown = false;
936
- const protobufSerializer = import_otlp_transformer.ProtobufTraceSerializer && typeof import_otlp_transformer.ProtobufTraceSerializer.serializeRequest === "function" ? import_otlp_transformer.ProtobufTraceSerializer : null;
937
- this.serializer = protobufSerializer ?? import_otlp_transformer.JsonTraceSerializer;
938
- this.contentType = protobufSerializer ? "application/x-protobuf" : "application/json";
939
- }
940
- export(spans, resultCallback) {
941
- if (this.isShutdown) {
942
- resultCallback({ code: import_core.ExportResultCode.FAILED });
943
- return;
944
- }
945
- const payload = this.serializer.serializeRequest(spans);
946
- if (!payload) {
947
- resultCallback({
948
- code: import_core.ExportResultCode.FAILED,
949
- error: new Error("OTLP export failed: empty payload")
950
- });
951
- return;
952
- }
953
- const headers = {
954
- ...this.headers,
955
- "Content-Type": this.contentType
956
- };
957
- const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
958
- let timeout;
959
- if (controller) {
960
- timeout = setTimeout(() => controller.abort(), this.timeoutMillis);
961
- }
962
- const doExport = async () => {
963
- const response = await fetch(this.endpointUrl, {
964
- method: "POST",
965
- headers,
966
- body: payload,
967
- signal: controller?.signal
968
- });
969
- if (!response.ok) {
970
- throw new Error(`OTLP export failed: ${response.status}`);
971
- }
972
- };
973
- doExport().then(() => {
974
- if (timeout) {
975
- clearTimeout(timeout);
976
- }
977
- resultCallback({ code: import_core.ExportResultCode.SUCCESS });
978
- }).catch((err) => {
979
- if (timeout) {
980
- clearTimeout(timeout);
981
- }
982
- import_api.diag.error("OTLP export failed", err);
983
- resultCallback({ code: import_core.ExportResultCode.FAILED, error: err });
984
- });
985
- }
986
- async forceFlush() {
987
- return;
988
- }
989
- async shutdown() {
990
- this.isShutdown = true;
991
- }
992
- };
993
905
  function createSpanProcessor(mode, exporter) {
994
906
  if (mode === "simple") {
995
907
  return new import_sdk_trace_base.SimpleSpanProcessor(exporter);
@@ -1001,19 +913,9 @@ function createSpanProcessor(mode, exporter) {
1001
913
  exportTimeoutMillis: 3e4
1002
914
  });
1003
915
  }
1004
- function createSampler(config) {
1005
- const sampler = config.exporter.sampler;
1006
- if (sampler.type === "always_off") {
1007
- return new import_sdk_trace_base.AlwaysOffSampler();
1008
- }
1009
- if (sampler.type === "ratio") {
1010
- return new import_sdk_trace_base.TraceIdRatioBasedSampler(sampler.ratio ?? 1);
1011
- }
1012
- return new import_sdk_trace_base.AlwaysOnSampler();
1013
- }
1014
916
  function sdkVersion() {
1015
917
  if (true) {
1016
- return "0.3.0";
918
+ return "0.4.0";
1017
919
  }
1018
920
  return "unknown";
1019
921
  }
@@ -1051,16 +953,29 @@ function getContextState(ctx) {
1051
953
  }
1052
954
 
1053
955
  // src/emitter.ts
1054
- function createEmitter(config) {
956
+ function createEmitter(config, exporter) {
1055
957
  const resolved = normalizeConfig(config);
1056
- const tracerProvider = createTracerProvider(resolved);
1057
- const tracer = tracerProvider ? tracerFromProvider(tracerProvider) : null;
958
+ if (!exporter) {
959
+ throw new XrayConfigError(
960
+ "INVALID_CONFIG",
961
+ "exporter is required (use @stainlessdev/xray-node or @stainlessdev/xray-fetch)"
962
+ );
963
+ }
964
+ logWithLevel(resolved.logger, "info", resolved.logLevel, "xray: emitter configured", {
965
+ serviceName: resolved.serviceName,
966
+ environment: resolved.environment,
967
+ version: resolved.version,
968
+ exporterEndpoint: resolved.exporter.endpointUrl,
969
+ spanProcessor: resolved.exporter.spanProcessor
970
+ });
971
+ const tracerProvider = createTracerProvider(resolved, exporter);
972
+ const tracer = tracerFromProvider(tracerProvider);
1058
973
  return {
1059
974
  config: resolved,
1060
975
  startRequest: (req) => startRequest(resolved, tracer, req),
1061
976
  endRequest: (ctx, res, err) => endRequest(resolved, ctx, res, err),
1062
- flush: () => tracerProvider ? tracerProvider.forceFlush() : Promise.resolve(),
1063
- shutdown: () => tracerProvider ? tracerProvider.shutdown() : Promise.resolve()
977
+ flush: () => tracerProvider.forceFlush(),
978
+ shutdown: () => tracerProvider.shutdown()
1064
979
  };
1065
980
  }
1066
981
  function startRequest(config, tracer, req) {
@@ -1071,7 +986,7 @@ function startRequest(config, tracer, req) {
1071
986
  if (req.route && config.route.normalize) {
1072
987
  req.route = config.route.normalizer ? config.route.normalizer(req.route) : normalizeRoutePattern(req.route);
1073
988
  }
1074
- const span = tracer ? spanFromTracer(tracer, spanNameFromRequest(req)) : void 0;
989
+ const span = spanFromTracer(tracer, spanNameFromRequest(req));
1075
990
  const context = {
1076
991
  requestId,
1077
992
  traceId: span?.spanContext().traceId,