@stainlessdev/xray-core 0.5.1 → 0.6.0-branch.bg-fix-req-id-gen.6d17621

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.js CHANGED
@@ -4,8 +4,9 @@ import {
4
4
  getContextState,
5
5
  logWithLevel,
6
6
  sanitizeHeaderValues,
7
- sanitizeLogString
8
- } from "./chunk-XI5E6C7G.js";
7
+ sanitizeLogString,
8
+ uuidv7
9
+ } from "./chunk-HGFCHC7O.js";
9
10
 
10
11
  // src/route.ts
11
12
  function normalizeRoutePattern(route) {
@@ -111,8 +112,7 @@ var defaultRedaction = {
111
112
  replacement: "[REDACTED]"
112
113
  };
113
114
  var defaultRequestId = {
114
- header: "x-request-id",
115
- generate: true
115
+ header: "request-id"
116
116
  };
117
117
  var defaultRoute = {
118
118
  normalize: true,
@@ -660,6 +660,7 @@ function redactJsonPath(value, segments, replacement) {
660
660
 
661
661
  // src/attributes.ts
662
662
  import {
663
+ ATTR_CLIENT_ADDRESS,
663
664
  ATTR_HTTP_REQUEST_BODY_SIZE,
664
665
  ATTR_HTTP_REQUEST_METHOD,
665
666
  ATTR_HTTP_RESPONSE_BODY_SIZE,
@@ -694,15 +695,24 @@ function setHeaderAttributes(span, headers, prefix) {
694
695
  span.setAttribute(prefix + key.toLowerCase(), Array.isArray(values) ? values : [values]);
695
696
  }
696
697
  }
697
- function setRequestAttributes(span, method, urlFull) {
698
- span.setAttribute(ATTR_HTTP_REQUEST_METHOD, method);
699
- if (urlFull) {
700
- span.setAttribute(ATTR_URL_FULL, urlFull);
701
- const path = extractPath(urlFull);
698
+ function setRequestAttributes(span, request, urlFull) {
699
+ span.setAttribute(ATTR_HTTP_REQUEST_METHOD, request.method);
700
+ const effectiveUrl = urlFull ?? request.url;
701
+ if (effectiveUrl) {
702
+ span.setAttribute(ATTR_URL_FULL, effectiveUrl);
703
+ const path = extractPath(effectiveUrl);
702
704
  if (path) {
703
705
  span.setAttribute(ATTR_URL_PATH, path);
704
706
  }
705
707
  }
708
+ const clientAddress = clientAddressForRequest(
709
+ request.headers,
710
+ request.remoteAddress,
711
+ request.redactionReplacement
712
+ );
713
+ if (clientAddress) {
714
+ span.setAttribute(ATTR_CLIENT_ADDRESS, clientAddress);
715
+ }
706
716
  }
707
717
  function extractPath(url) {
708
718
  try {
@@ -712,6 +722,144 @@ function extractPath(url) {
712
722
  return match?.[0] || void 0;
713
723
  }
714
724
  }
725
+ function clientAddressForRequest(headers, remoteAddress, redactionReplacement) {
726
+ const forwarded = forwardedClientAddress(
727
+ headerValues(headers, "forwarded"),
728
+ redactionReplacement
729
+ );
730
+ if (forwarded) {
731
+ return forwarded;
732
+ }
733
+ const xForwarded = xForwardedForClientAddress(
734
+ headerValues(headers, "x-forwarded-for"),
735
+ redactionReplacement
736
+ );
737
+ if (xForwarded) {
738
+ return xForwarded;
739
+ }
740
+ const xRealIp = xRealIpClientAddress(headerValues(headers, "x-real-ip"), redactionReplacement);
741
+ if (xRealIp) {
742
+ return xRealIp;
743
+ }
744
+ if (!remoteAddress) {
745
+ return void 0;
746
+ }
747
+ return remoteAddrHost(remoteAddress);
748
+ }
749
+ function forwardedClientAddress(values, redactionReplacement) {
750
+ for (const value of values) {
751
+ if (!value) {
752
+ continue;
753
+ }
754
+ const entries = value.split(",");
755
+ for (const entry of entries) {
756
+ const params = entry.split(";");
757
+ for (const param of params) {
758
+ const [rawKey, ...rest] = param.split("=");
759
+ if (!rawKey) {
760
+ continue;
761
+ }
762
+ if (rawKey.trim().toLowerCase() !== "for") {
763
+ continue;
764
+ }
765
+ const rawValue = rest.join("=").trim();
766
+ const normalized = normalizeKnownAddress(rawValue, redactionReplacement);
767
+ if (normalized) {
768
+ return normalized;
769
+ }
770
+ }
771
+ }
772
+ }
773
+ return void 0;
774
+ }
775
+ function xForwardedForClientAddress(values, redactionReplacement) {
776
+ for (const value of values) {
777
+ if (!value) {
778
+ continue;
779
+ }
780
+ const entries = value.split(",");
781
+ for (const entry of entries) {
782
+ const normalized = normalizeKnownAddress(entry, redactionReplacement);
783
+ if (normalized) {
784
+ return normalized;
785
+ }
786
+ }
787
+ }
788
+ return void 0;
789
+ }
790
+ function xRealIpClientAddress(values, redactionReplacement) {
791
+ for (const value of values) {
792
+ if (!value) {
793
+ continue;
794
+ }
795
+ const normalized = normalizeKnownAddress(value, redactionReplacement);
796
+ if (normalized) {
797
+ return normalized;
798
+ }
799
+ }
800
+ return void 0;
801
+ }
802
+ function normalizeKnownAddress(value, redactionReplacement) {
803
+ const normalized = normalizeAddress(value, redactionReplacement);
804
+ if (!normalized) {
805
+ return void 0;
806
+ }
807
+ if (normalized.toLowerCase() === "unknown") {
808
+ return void 0;
809
+ }
810
+ return normalized;
811
+ }
812
+ function normalizeAddress(value, redactionReplacement) {
813
+ if (!value) {
814
+ return void 0;
815
+ }
816
+ let trimmed = value.trim();
817
+ if (!trimmed) {
818
+ return void 0;
819
+ }
820
+ if (redactionReplacement && trimmed === redactionReplacement) {
821
+ return void 0;
822
+ }
823
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length > 1) {
824
+ trimmed = trimmed.slice(1, -1).trim();
825
+ }
826
+ if (!trimmed) {
827
+ return void 0;
828
+ }
829
+ if (trimmed.startsWith("[")) {
830
+ const end = trimmed.indexOf("]");
831
+ if (end !== -1) {
832
+ return trimmed.slice(1, end);
833
+ }
834
+ }
835
+ const colonCount = (trimmed.match(/:/g) ?? []).length;
836
+ if (colonCount === 1) {
837
+ const host = trimmed.split(":")[0];
838
+ return host || void 0;
839
+ }
840
+ return trimmed;
841
+ }
842
+ function remoteAddrHost(value) {
843
+ return normalizeAddress(value);
844
+ }
845
+ function headerValues(headers, name) {
846
+ if (!headers) {
847
+ return [];
848
+ }
849
+ const target = name.toLowerCase();
850
+ const values = [];
851
+ for (const [key, value] of Object.entries(headers)) {
852
+ if (key.toLowerCase() !== target) {
853
+ continue;
854
+ }
855
+ if (Array.isArray(value)) {
856
+ values.push(...value);
857
+ } else {
858
+ values.push(value);
859
+ }
860
+ }
861
+ return values;
862
+ }
715
863
  function setRequestBodyAttributes(span, body) {
716
864
  if (!body.value) {
717
865
  return;
@@ -856,7 +1004,7 @@ function createSpanProcessor(mode, exporter) {
856
1004
  }
857
1005
  function sdkVersion() {
858
1006
  if (true) {
859
- return "0.5.0";
1007
+ return "0.6.0";
860
1008
  }
861
1009
  return "unknown";
862
1010
  }
@@ -865,25 +1013,6 @@ function isNodeRuntime() {
865
1013
  return !!maybeProcess?.versions?.node;
866
1014
  }
867
1015
 
868
- // src/uuid.ts
869
- function uuidv7() {
870
- const bytes = new Uint8Array(16);
871
- crypto.getRandomValues(bytes);
872
- const timestamp = BigInt(Date.now());
873
- bytes[0] = Number(timestamp >> 40n & 0xffn);
874
- bytes[1] = Number(timestamp >> 32n & 0xffn);
875
- bytes[2] = Number(timestamp >> 24n & 0xffn);
876
- bytes[3] = Number(timestamp >> 16n & 0xffn);
877
- bytes[4] = Number(timestamp >> 8n & 0xffn);
878
- bytes[5] = Number(timestamp & 0xffn);
879
- const byte6 = bytes[6] ?? 0;
880
- const byte8 = bytes[8] ?? 0;
881
- bytes[6] = byte6 & 15 | 112;
882
- bytes[8] = byte8 & 63 | 128;
883
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
884
- return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
885
- }
886
-
887
1016
  // src/emitter.ts
888
1017
  function createEmitter(config, exporter) {
889
1018
  const resolved = normalizeConfig(config);
@@ -913,14 +1042,14 @@ function createEmitter(config, exporter) {
913
1042
  function startRequest(config, tracer, req) {
914
1043
  const startTimeMs = Number.isFinite(req.startTimeMs) ? req.startTimeMs : Date.now();
915
1044
  req.startTimeMs = startTimeMs;
916
- const requestId = resolveRequestId(config, req.requestId, req.headers);
917
- req.requestId = requestId;
1045
+ const explicitRequestId = normalizeRequestIdCandidate(req.requestId);
1046
+ req.requestId = explicitRequestId;
918
1047
  if (req.route && config.route.normalize) {
919
1048
  req.route = config.route.normalizer ? config.route.normalizer(req.route) : normalizeRoutePattern(req.route);
920
1049
  }
921
1050
  const span = spanFromTracer(tracer, spanNameFromRequest(req));
922
1051
  const context = {
923
- requestId,
1052
+ requestId: explicitRequestId ?? "",
924
1053
  traceId: span?.spanContext().traceId,
925
1054
  spanId: span?.spanContext().spanId,
926
1055
  setUserId: (id) => {
@@ -999,8 +1128,10 @@ function endRequest(config, ctx, res, err) {
999
1128
  const endTimeMs = Number.isFinite(res.endTimeMs) ? res.endTimeMs : Date.now();
1000
1129
  res.endTimeMs = endTimeMs;
1001
1130
  if (!state) {
1131
+ const resolvedRequestId2 = resolveFinalRequestId(config, ctx.requestId, res.headers);
1132
+ ctx.requestId = resolvedRequestId2;
1002
1133
  const fallbackLog = {
1003
- requestId: ctx.requestId,
1134
+ requestId: resolvedRequestId2,
1004
1135
  serviceName: config.serviceName,
1005
1136
  method: res.statusCode ? "UNKNOWN" : "UNKNOWN",
1006
1137
  url: "",
@@ -1011,12 +1142,19 @@ function endRequest(config, ctx, res, err) {
1011
1142
  return fallbackLog;
1012
1143
  }
1013
1144
  const request = state.request;
1145
+ const resolvedRequestId = resolveFinalRequestId(
1146
+ config,
1147
+ request.requestId || ctx.requestId,
1148
+ res.headers
1149
+ );
1150
+ request.requestId = resolvedRequestId;
1151
+ ctx.requestId = resolvedRequestId;
1014
1152
  const capture = resolveCapture(config.capture, state.captureOverride);
1015
1153
  const redaction = resolveRedaction(config.redaction, state.redactionOverride);
1016
1154
  const route = request.route;
1017
1155
  const url = sanitizeLogString(request.url);
1018
1156
  const log = {
1019
- requestId: request.requestId ?? ctx.requestId,
1157
+ requestId: resolvedRequestId,
1020
1158
  traceId: state.span?.spanContext().traceId,
1021
1159
  spanId: state.span?.spanContext().spanId,
1022
1160
  serviceName: config.serviceName,
@@ -1043,7 +1181,16 @@ function endRequest(config, ctx, res, err) {
1043
1181
  const span = state.span;
1044
1182
  if (span) {
1045
1183
  try {
1046
- setRequestAttributes(span, request.method, redacted.url);
1184
+ const clientAddressHeaders = redactHeaders(request.headers, redaction);
1185
+ setRequestAttributes(
1186
+ span,
1187
+ {
1188
+ ...request,
1189
+ headers: clientAddressHeaders,
1190
+ redactionReplacement: redaction.replacement
1191
+ },
1192
+ redacted.url
1193
+ );
1047
1194
  setRequestIdAttribute(span, redacted.requestId);
1048
1195
  span.setAttribute("service.name", config.serviceName);
1049
1196
  if (redacted.statusCode != null) {
@@ -1084,22 +1231,40 @@ function endRequest(config, ctx, res, err) {
1084
1231
  }
1085
1232
  return redacted;
1086
1233
  }
1087
- function resolveRequestId(config, requestId, headers) {
1088
- if (requestId) {
1089
- return requestId;
1234
+ function resolveFinalRequestId(config, explicitRequestId, responseHeaders) {
1235
+ const explicit = normalizeRequestIdCandidate(explicitRequestId);
1236
+ if (explicit) {
1237
+ return explicit;
1090
1238
  }
1091
- const headerName = config.requestId.header.toLowerCase();
1092
- const headerValue = headers[headerName];
1239
+ const headerValue = resolveHeaderRequestId(config.requestId.header, responseHeaders);
1093
1240
  if (headerValue) {
1094
- const value = Array.isArray(headerValue) ? headerValue[0] : headerValue;
1095
- if (value && value.trim()) {
1096
- return value.trim();
1241
+ return headerValue;
1242
+ }
1243
+ return uuidv7();
1244
+ }
1245
+ function resolveHeaderRequestId(headerName, headers) {
1246
+ if (!headers) {
1247
+ return void 0;
1248
+ }
1249
+ const target = headerName.toLowerCase();
1250
+ for (const [name, value] of Object.entries(headers)) {
1251
+ if (name.toLowerCase() !== target) {
1252
+ continue;
1253
+ }
1254
+ const entry = Array.isArray(value) ? value[0] : value;
1255
+ const normalized = normalizeRequestIdCandidate(entry);
1256
+ if (normalized) {
1257
+ return normalized;
1097
1258
  }
1098
1259
  }
1099
- if (!config.requestId.generate) {
1100
- return uuidv7();
1260
+ return void 0;
1261
+ }
1262
+ function normalizeRequestIdCandidate(value) {
1263
+ if (!value) {
1264
+ return void 0;
1101
1265
  }
1102
- return uuidv7();
1266
+ const trimmed = value.trim();
1267
+ return trimmed ? trimmed : void 0;
1103
1268
  }
1104
1269
  function resolveCapture(base, override) {
1105
1270
  if (!override) {