@opentelemetry/instrumentation-http 0.53.0 → 0.54.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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.headerCapture = exports.getIncomingRequestMetricAttributesOnResponse = exports.getIncomingRequestAttributesOnResponse = exports.getIncomingRequestMetricAttributes = exports.getIncomingRequestAttributes = exports.getOutgoingRequestMetricAttributesOnResponse = exports.getOutgoingRequestAttributesOnResponse = exports.setAttributesFromHttpKind = exports.getOutgoingRequestMetricAttributes = exports.getOutgoingRequestAttributes = exports.extractHostnameAndPort = exports.isValidOptionsType = exports.getRequestInfo = exports.isCompressed = exports.setResponseContentLengthAttribute = exports.setRequestContentLengthAttribute = exports.setSpanWithError = exports.isIgnored = exports.satisfiesPattern = exports.parseResponseStatus = exports.getAbsoluteUrl = void 0;
3
+ exports.headerCapture = exports.getIncomingStableRequestMetricAttributesOnResponse = exports.getIncomingRequestMetricAttributesOnResponse = exports.getIncomingRequestAttributesOnResponse = exports.getIncomingRequestMetricAttributes = exports.getIncomingRequestAttributes = exports.getRemoteClientAddress = exports.getOutgoingRequestMetricAttributesOnResponse = exports.getOutgoingRequestAttributesOnResponse = exports.setAttributesFromHttpKind = exports.getOutgoingRequestMetricAttributes = exports.getOutgoingRequestAttributes = exports.extractHostnameAndPort = exports.isValidOptionsType = exports.getRequestInfo = exports.isCompressed = exports.setResponseContentLengthAttribute = exports.setRequestContentLengthAttribute = exports.setSpanWithError = exports.satisfiesPattern = exports.parseResponseStatus = exports.getAbsoluteUrl = void 0;
4
4
  /*
5
5
  * Copyright The OpenTelemetry Authors
6
6
  *
@@ -21,6 +21,7 @@ const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
21
21
  const core_1 = require("@opentelemetry/core");
22
22
  const url = require("url");
23
23
  const AttributeNames_1 = require("./enums/AttributeNames");
24
+ const forwardedParse = require("forwarded-parse");
24
25
  /**
25
26
  * Get an absolute url
26
27
  */
@@ -75,44 +76,22 @@ const satisfiesPattern = (constant, pattern) => {
75
76
  }
76
77
  };
77
78
  exports.satisfiesPattern = satisfiesPattern;
78
- /**
79
- * Check whether the given request is ignored by configuration
80
- * It will not re-throw exceptions from `list` provided by the client
81
- * @param constant e.g URL of request
82
- * @param [list] List of ignore patterns
83
- * @param [onException] callback for doing something when an exception has
84
- * occurred
85
- */
86
- const isIgnored = (constant, list, onException) => {
87
- if (!list) {
88
- // No ignored urls - trace everything
89
- return false;
90
- }
91
- // Try/catch outside the loop for failing fast
92
- try {
93
- for (const pattern of list) {
94
- if ((0, exports.satisfiesPattern)(constant, pattern)) {
95
- return true;
96
- }
97
- }
98
- }
99
- catch (e) {
100
- if (onException) {
101
- onException(e);
102
- }
103
- }
104
- return false;
105
- };
106
- exports.isIgnored = isIgnored;
107
79
  /**
108
80
  * Sets the span with the error passed in params
109
81
  * @param {Span} span the span that need to be set
110
82
  * @param {Error} error error that will be set to span
83
+ * @param {SemconvStability} semconvStability determines which semconv version to use
111
84
  */
112
- const setSpanWithError = (span, error) => {
85
+ const setSpanWithError = (span, error, semconvStability) => {
113
86
  const message = error.message;
114
- span.setAttribute(AttributeNames_1.AttributeNames.HTTP_ERROR_NAME, error.name);
115
- span.setAttribute(AttributeNames_1.AttributeNames.HTTP_ERROR_MESSAGE, message);
87
+ if ((semconvStability & 2 /* OLD */) === 2 /* OLD */) {
88
+ span.setAttribute(AttributeNames_1.AttributeNames.HTTP_ERROR_NAME, error.name);
89
+ span.setAttribute(AttributeNames_1.AttributeNames.HTTP_ERROR_MESSAGE, message);
90
+ }
91
+ if ((semconvStability & 1 /* STABLE */) ===
92
+ 1 /* STABLE */) {
93
+ span.setAttribute(semantic_conventions_1.ATTR_ERROR_TYPE, error.name);
94
+ }
116
95
  span.setStatus({ code: api_1.SpanStatusCode.ERROR, message });
117
96
  span.recordException(error);
118
97
  };
@@ -120,7 +99,7 @@ exports.setSpanWithError = setSpanWithError;
120
99
  /**
121
100
  * Adds attributes for request content-length and content-encoding HTTP headers
122
101
  * @param { IncomingMessage } Request object whose headers will be analyzed
123
- * @param { SpanAttributes } SpanAttributes object to be modified
102
+ * @param { Attributes } Attributes object to be modified
124
103
  */
125
104
  const setRequestContentLengthAttribute = (request, attributes) => {
126
105
  const length = getContentLength(request.headers);
@@ -137,7 +116,9 @@ exports.setRequestContentLengthAttribute = setRequestContentLengthAttribute;
137
116
  /**
138
117
  * Adds attributes for response content-length and content-encoding HTTP headers
139
118
  * @param { IncomingMessage } Response object whose headers will be analyzed
140
- * @param { SpanAttributes } SpanAttributes object to be modified
119
+ * @param { Attributes } Attributes object to be modified
120
+ *
121
+ * @deprecated this is for an older version of semconv. It is retained for compatibility using OTEL_SEMCONV_STABILITY_OPT_IN
141
122
  */
142
123
  const setResponseContentLengthAttribute = (response, attributes) => {
143
124
  const length = getContentLength(response.headers);
@@ -258,32 +239,55 @@ exports.extractHostnameAndPort = extractHostnameAndPort;
258
239
  /**
259
240
  * Returns outgoing request attributes scoped to the options passed to the request
260
241
  * @param {ParsedRequestOptions} requestOptions the same options used to make the request
261
- * @param {{ component: string, hostname: string, hookAttributes?: SpanAttributes }} options used to pass data needed to create attributes
242
+ * @param {{ component: string, hostname: string, hookAttributes?: Attributes }} options used to pass data needed to create attributes
243
+ * @param {SemconvStability} semconvStability determines which semconv version to use
262
244
  */
263
- const getOutgoingRequestAttributes = (requestOptions, options) => {
264
- var _a;
245
+ const getOutgoingRequestAttributes = (requestOptions, options, semconvStability) => {
246
+ var _a, _b;
265
247
  const hostname = options.hostname;
266
248
  const port = options.port;
267
- const requestMethod = requestOptions.method;
268
- const method = requestMethod ? requestMethod.toUpperCase() : 'GET';
249
+ const method = (_a = requestOptions.method) !== null && _a !== void 0 ? _a : 'GET';
250
+ const normalizedMethod = normalizeMethod(method);
269
251
  const headers = requestOptions.headers || {};
270
252
  const userAgent = headers['user-agent'];
271
- const attributes = {
272
- [semantic_conventions_1.SEMATTRS_HTTP_URL]: (0, exports.getAbsoluteUrl)(requestOptions, headers, `${options.component}:`),
253
+ const urlFull = (0, exports.getAbsoluteUrl)(requestOptions, headers, `${options.component}:`);
254
+ const oldAttributes = {
255
+ [semantic_conventions_1.SEMATTRS_HTTP_URL]: urlFull,
273
256
  [semantic_conventions_1.SEMATTRS_HTTP_METHOD]: method,
274
257
  [semantic_conventions_1.SEMATTRS_HTTP_TARGET]: requestOptions.path || '/',
275
258
  [semantic_conventions_1.SEMATTRS_NET_PEER_NAME]: hostname,
276
- [semantic_conventions_1.SEMATTRS_HTTP_HOST]: (_a = headers.host) !== null && _a !== void 0 ? _a : `${hostname}:${port}`,
259
+ [semantic_conventions_1.SEMATTRS_HTTP_HOST]: (_b = headers.host) !== null && _b !== void 0 ? _b : `${hostname}:${port}`,
260
+ };
261
+ const newAttributes = {
262
+ // Required attributes
263
+ [semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD]: normalizedMethod,
264
+ [semantic_conventions_1.ATTR_SERVER_ADDRESS]: hostname,
265
+ [semantic_conventions_1.ATTR_SERVER_PORT]: Number(port),
266
+ [semantic_conventions_1.ATTR_URL_FULL]: urlFull,
267
+ // leaving out protocol version, it is not yet negotiated
268
+ // leaving out protocol name, it is only required when protocol version is set
269
+ // retries and redirects not supported
270
+ // Opt-in attributes left off for now
277
271
  };
272
+ // conditionally required if request method required case normalization
273
+ if (method !== normalizedMethod) {
274
+ newAttributes[semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = method;
275
+ }
278
276
  if (userAgent !== undefined) {
279
- attributes[semantic_conventions_1.SEMATTRS_HTTP_USER_AGENT] = userAgent;
277
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_USER_AGENT] = userAgent;
278
+ }
279
+ switch (semconvStability) {
280
+ case 1 /* STABLE */:
281
+ return Object.assign(newAttributes, options.hookAttributes);
282
+ case 2 /* OLD */:
283
+ return Object.assign(oldAttributes, options.hookAttributes);
280
284
  }
281
- return Object.assign(attributes, options.hookAttributes);
285
+ return Object.assign(oldAttributes, newAttributes, options.hookAttributes);
282
286
  };
283
287
  exports.getOutgoingRequestAttributes = getOutgoingRequestAttributes;
284
288
  /**
285
289
  * Returns outgoing request Metric attributes scoped to the request data
286
- * @param {SpanAttributes} spanAttributes the span attributes
290
+ * @param {Attributes} spanAttributes the span attributes
287
291
  */
288
292
  const getOutgoingRequestMetricAttributes = (spanAttributes) => {
289
293
  const metricAttributes = {};
@@ -313,28 +317,42 @@ exports.setAttributesFromHttpKind = setAttributesFromHttpKind;
313
317
  /**
314
318
  * Returns outgoing request attributes scoped to the response data
315
319
  * @param {IncomingMessage} response the response object
316
- * @param {{ hostname: string }} options used to pass data needed to create attributes
320
+ * @param {SemconvStability} semconvStability determines which semconv version to use
317
321
  */
318
- const getOutgoingRequestAttributesOnResponse = (response) => {
322
+ const getOutgoingRequestAttributesOnResponse = (response, semconvStability) => {
319
323
  const { statusCode, statusMessage, httpVersion, socket } = response;
320
- const attributes = {};
324
+ const oldAttributes = {};
325
+ const stableAttributes = {};
326
+ if (statusCode != null) {
327
+ stableAttributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE] = statusCode;
328
+ }
321
329
  if (socket) {
322
330
  const { remoteAddress, remotePort } = socket;
323
- attributes[semantic_conventions_1.SEMATTRS_NET_PEER_IP] = remoteAddress;
324
- attributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT] = remotePort;
331
+ oldAttributes[semantic_conventions_1.SEMATTRS_NET_PEER_IP] = remoteAddress;
332
+ oldAttributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT] = remotePort;
333
+ // Recommended
334
+ stableAttributes[semantic_conventions_1.ATTR_NETWORK_PEER_ADDRESS] = remoteAddress;
335
+ stableAttributes[semantic_conventions_1.ATTR_NETWORK_PEER_PORT] = remotePort;
336
+ stableAttributes[semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION] = response.httpVersion;
325
337
  }
326
- (0, exports.setResponseContentLengthAttribute)(response, attributes);
338
+ (0, exports.setResponseContentLengthAttribute)(response, oldAttributes);
327
339
  if (statusCode) {
328
- attributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE] = statusCode;
329
- attributes[AttributeNames_1.AttributeNames.HTTP_STATUS_TEXT] = (statusMessage || '').toUpperCase();
340
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE] = statusCode;
341
+ oldAttributes[AttributeNames_1.AttributeNames.HTTP_STATUS_TEXT] = (statusMessage || '').toUpperCase();
342
+ }
343
+ (0, exports.setAttributesFromHttpKind)(httpVersion, oldAttributes);
344
+ switch (semconvStability) {
345
+ case 1 /* STABLE */:
346
+ return stableAttributes;
347
+ case 2 /* OLD */:
348
+ return oldAttributes;
330
349
  }
331
- (0, exports.setAttributesFromHttpKind)(httpVersion, attributes);
332
- return attributes;
350
+ return Object.assign(oldAttributes, stableAttributes);
333
351
  };
334
352
  exports.getOutgoingRequestAttributesOnResponse = getOutgoingRequestAttributesOnResponse;
335
353
  /**
336
354
  * Returns outgoing request Metric attributes scoped to the response data
337
- * @param {SpanAttributes} spanAttributes the span attributes
355
+ * @param {Attributes} spanAttributes the span attributes
338
356
  */
339
357
  const getOutgoingRequestMetricAttributesOnResponse = (spanAttributes) => {
340
358
  const metricAttributes = {};
@@ -346,24 +364,162 @@ const getOutgoingRequestMetricAttributesOnResponse = (spanAttributes) => {
346
364
  return metricAttributes;
347
365
  };
348
366
  exports.getOutgoingRequestMetricAttributesOnResponse = getOutgoingRequestMetricAttributesOnResponse;
367
+ function parseHostHeader(hostHeader, proto) {
368
+ const parts = hostHeader.split(':');
369
+ // no semicolon implies ipv4 dotted syntax or host name without port
370
+ // x.x.x.x
371
+ // example.com
372
+ if (parts.length === 1) {
373
+ if (proto === 'http') {
374
+ return { host: parts[0], port: '80' };
375
+ }
376
+ if (proto === 'https') {
377
+ return { host: parts[0], port: '443' };
378
+ }
379
+ return { host: parts[0] };
380
+ }
381
+ // single semicolon implies ipv4 dotted syntax or host name with port
382
+ // x.x.x.x:yyyy
383
+ // example.com:yyyy
384
+ if (parts.length === 2) {
385
+ return {
386
+ host: parts[0],
387
+ port: parts[1],
388
+ };
389
+ }
390
+ // more than 2 parts implies ipv6 syntax with multiple colons
391
+ // [x:x:x:x:x:x:x:x]
392
+ // [x:x:x:x:x:x:x:x]:yyyy
393
+ if (parts[0].startsWith('[')) {
394
+ if (parts[parts.length - 1].endsWith(']')) {
395
+ if (proto === 'http') {
396
+ return { host: hostHeader, port: '80' };
397
+ }
398
+ if (proto === 'https') {
399
+ return { host: hostHeader, port: '443' };
400
+ }
401
+ }
402
+ else if (parts[parts.length - 2].endsWith(']')) {
403
+ return {
404
+ host: parts.slice(0, -1).join(':'),
405
+ port: parts[parts.length - 1],
406
+ };
407
+ }
408
+ }
409
+ // if nothing above matches just return the host header
410
+ return { host: hostHeader };
411
+ }
412
+ /**
413
+ * Get server.address and port according to http semconv 1.27
414
+ * https://github.com/open-telemetry/semantic-conventions/blob/bf0a2c1134f206f034408b201dbec37960ed60ec/docs/http/http-spans.md#setting-serveraddress-and-serverport-attributes
415
+ */
416
+ function getServerAddress(request, component) {
417
+ const forwardedHeader = request.headers['forwarded'];
418
+ if (forwardedHeader) {
419
+ for (const entry of forwardedParse(forwardedHeader)) {
420
+ if (entry.host) {
421
+ return parseHostHeader(entry.host, entry.proto);
422
+ }
423
+ }
424
+ }
425
+ const xForwardedHost = request.headers['x-forwarded-host'];
426
+ if (typeof xForwardedHost === 'string') {
427
+ if (typeof request.headers['x-forwarded-proto'] === 'string') {
428
+ return parseHostHeader(xForwardedHost, request.headers['x-forwarded-proto']);
429
+ }
430
+ if (Array.isArray(request.headers['x-forwarded-proto'])) {
431
+ return parseHostHeader(xForwardedHost, request.headers['x-forwarded-proto'][0]);
432
+ }
433
+ return parseHostHeader(xForwardedHost);
434
+ }
435
+ else if (Array.isArray(xForwardedHost) &&
436
+ typeof xForwardedHost[0] === 'string' &&
437
+ xForwardedHost[0].length > 0) {
438
+ if (typeof request.headers['x-forwarded-proto'] === 'string') {
439
+ return parseHostHeader(xForwardedHost[0], request.headers['x-forwarded-proto']);
440
+ }
441
+ if (Array.isArray(request.headers['x-forwarded-proto'])) {
442
+ return parseHostHeader(xForwardedHost[0], request.headers['x-forwarded-proto'][0]);
443
+ }
444
+ return parseHostHeader(xForwardedHost[0]);
445
+ }
446
+ const host = request.headers['host'];
447
+ if (typeof host === 'string' && host.length > 0) {
448
+ return parseHostHeader(host, component);
449
+ }
450
+ return null;
451
+ }
452
+ /**
453
+ * Get server.address and port according to http semconv 1.27
454
+ * https://github.com/open-telemetry/semantic-conventions/blob/bf0a2c1134f206f034408b201dbec37960ed60ec/docs/http/http-spans.md#setting-serveraddress-and-serverport-attributes
455
+ */
456
+ function getRemoteClientAddress(request) {
457
+ const forwardedHeader = request.headers['forwarded'];
458
+ if (forwardedHeader) {
459
+ for (const entry of forwardedParse(forwardedHeader)) {
460
+ if (entry.for) {
461
+ return entry.for;
462
+ }
463
+ }
464
+ }
465
+ const xForwardedFor = request.headers['x-forwarded-for'];
466
+ if (typeof xForwardedFor === 'string') {
467
+ return xForwardedFor;
468
+ }
469
+ else if (Array.isArray(xForwardedFor)) {
470
+ return xForwardedFor[0];
471
+ }
472
+ const remote = request.socket.remoteAddress;
473
+ if (remote) {
474
+ return remote;
475
+ }
476
+ return null;
477
+ }
478
+ exports.getRemoteClientAddress = getRemoteClientAddress;
349
479
  /**
350
480
  * Returns incoming request attributes scoped to the request data
351
481
  * @param {IncomingMessage} request the request object
352
- * @param {{ component: string, serverName?: string, hookAttributes?: SpanAttributes }} options used to pass data needed to create attributes
482
+ * @param {{ component: string, serverName?: string, hookAttributes?: Attributes }} options used to pass data needed to create attributes
483
+ * @param {SemconvStability} semconvStability determines which semconv version to use
353
484
  */
354
485
  const getIncomingRequestAttributes = (request, options) => {
355
486
  const headers = request.headers;
356
487
  const userAgent = headers['user-agent'];
357
488
  const ips = headers['x-forwarded-for'];
358
- const method = request.method || 'GET';
359
489
  const httpVersion = request.httpVersion;
360
490
  const requestUrl = request.url ? url.parse(request.url) : null;
361
491
  const host = (requestUrl === null || requestUrl === void 0 ? void 0 : requestUrl.host) || headers.host;
362
492
  const hostname = (requestUrl === null || requestUrl === void 0 ? void 0 : requestUrl.hostname) ||
363
493
  (host === null || host === void 0 ? void 0 : host.replace(/^(.*)(:[0-9]{1,5})/, '$1')) ||
364
494
  'localhost';
495
+ const method = request.method;
496
+ const normalizedMethod = normalizeMethod(method);
497
+ const serverAddress = getServerAddress(request, options.component);
365
498
  const serverName = options.serverName;
366
- const attributes = {
499
+ const remoteClientAddress = getRemoteClientAddress(request);
500
+ const newAttributes = {
501
+ [semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD]: normalizedMethod,
502
+ [semantic_conventions_1.ATTR_URL_SCHEME]: options.component,
503
+ [semantic_conventions_1.ATTR_SERVER_ADDRESS]: serverAddress === null || serverAddress === void 0 ? void 0 : serverAddress.host,
504
+ [semantic_conventions_1.ATTR_NETWORK_PEER_ADDRESS]: request.socket.remoteAddress,
505
+ [semantic_conventions_1.ATTR_NETWORK_PEER_PORT]: request.socket.remotePort,
506
+ [semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION]: request.httpVersion,
507
+ [semantic_conventions_1.ATTR_USER_AGENT_ORIGINAL]: userAgent,
508
+ };
509
+ if ((requestUrl === null || requestUrl === void 0 ? void 0 : requestUrl.pathname) != null) {
510
+ newAttributes[semantic_conventions_1.ATTR_URL_PATH] = requestUrl.pathname;
511
+ }
512
+ if (remoteClientAddress != null) {
513
+ newAttributes[semantic_conventions_1.ATTR_CLIENT_ADDRESS] = remoteClientAddress;
514
+ }
515
+ if ((serverAddress === null || serverAddress === void 0 ? void 0 : serverAddress.port) != null) {
516
+ newAttributes[semantic_conventions_1.ATTR_SERVER_PORT] = Number(serverAddress.port);
517
+ }
518
+ // conditionally required if request method required case normalization
519
+ if (method !== normalizedMethod) {
520
+ newAttributes[semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = method;
521
+ }
522
+ const oldAttributes = {
367
523
  [semantic_conventions_1.SEMATTRS_HTTP_URL]: (0, exports.getAbsoluteUrl)(requestUrl, headers, `${options.component}:`),
368
524
  [semantic_conventions_1.SEMATTRS_HTTP_HOST]: host,
369
525
  [semantic_conventions_1.SEMATTRS_NET_HOST_NAME]: hostname,
@@ -371,25 +527,31 @@ const getIncomingRequestAttributes = (request, options) => {
371
527
  [semantic_conventions_1.SEMATTRS_HTTP_SCHEME]: options.component,
372
528
  };
373
529
  if (typeof ips === 'string') {
374
- attributes[semantic_conventions_1.SEMATTRS_HTTP_CLIENT_IP] = ips.split(',')[0];
530
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_CLIENT_IP] = ips.split(',')[0];
375
531
  }
376
532
  if (typeof serverName === 'string') {
377
- attributes[semantic_conventions_1.SEMATTRS_HTTP_SERVER_NAME] = serverName;
533
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_SERVER_NAME] = serverName;
378
534
  }
379
535
  if (requestUrl) {
380
- attributes[semantic_conventions_1.SEMATTRS_HTTP_TARGET] = requestUrl.path || '/';
536
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_TARGET] = requestUrl.path || '/';
381
537
  }
382
538
  if (userAgent !== undefined) {
383
- attributes[semantic_conventions_1.SEMATTRS_HTTP_USER_AGENT] = userAgent;
539
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_USER_AGENT] = userAgent;
540
+ }
541
+ (0, exports.setRequestContentLengthAttribute)(request, oldAttributes);
542
+ (0, exports.setAttributesFromHttpKind)(httpVersion, oldAttributes);
543
+ switch (options.semconvStability) {
544
+ case 1 /* STABLE */:
545
+ return Object.assign(newAttributes, options.hookAttributes);
546
+ case 2 /* OLD */:
547
+ return Object.assign(oldAttributes, options.hookAttributes);
384
548
  }
385
- (0, exports.setRequestContentLengthAttribute)(request, attributes);
386
- (0, exports.setAttributesFromHttpKind)(httpVersion, attributes);
387
- return Object.assign(attributes, options.hookAttributes);
549
+ return Object.assign(oldAttributes, newAttributes, options.hookAttributes);
388
550
  };
389
551
  exports.getIncomingRequestAttributes = getIncomingRequestAttributes;
390
552
  /**
391
553
  * Returns incoming request Metric attributes scoped to the request data
392
- * @param {SpanAttributes} spanAttributes the span attributes
554
+ * @param {Attributes} spanAttributes the span attributes
393
555
  * @param {{ component: string }} options used to pass data needed to create attributes
394
556
  */
395
557
  const getIncomingRequestMetricAttributes = (spanAttributes) => {
@@ -407,31 +569,41 @@ exports.getIncomingRequestMetricAttributes = getIncomingRequestMetricAttributes;
407
569
  * Returns incoming request attributes scoped to the response data
408
570
  * @param {(ServerResponse & { socket: Socket; })} response the response object
409
571
  */
410
- const getIncomingRequestAttributesOnResponse = (request, response) => {
572
+ const getIncomingRequestAttributesOnResponse = (request, response, semconvStability) => {
411
573
  // take socket from the request,
412
574
  // since it may be detached from the response object in keep-alive mode
413
575
  const { socket } = request;
414
576
  const { statusCode, statusMessage } = response;
577
+ const newAttributes = {
578
+ [semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode,
579
+ };
415
580
  const rpcMetadata = (0, core_1.getRPCMetadata)(api_1.context.active());
416
- const attributes = {};
581
+ const oldAttributes = {};
417
582
  if (socket) {
418
583
  const { localAddress, localPort, remoteAddress, remotePort } = socket;
419
- attributes[semantic_conventions_1.SEMATTRS_NET_HOST_IP] = localAddress;
420
- attributes[semantic_conventions_1.SEMATTRS_NET_HOST_PORT] = localPort;
421
- attributes[semantic_conventions_1.SEMATTRS_NET_PEER_IP] = remoteAddress;
422
- attributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT] = remotePort;
584
+ oldAttributes[semantic_conventions_1.SEMATTRS_NET_HOST_IP] = localAddress;
585
+ oldAttributes[semantic_conventions_1.SEMATTRS_NET_HOST_PORT] = localPort;
586
+ oldAttributes[semantic_conventions_1.SEMATTRS_NET_PEER_IP] = remoteAddress;
587
+ oldAttributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT] = remotePort;
423
588
  }
424
- attributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE] = statusCode;
425
- attributes[AttributeNames_1.AttributeNames.HTTP_STATUS_TEXT] = (statusMessage || '').toUpperCase();
589
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE] = statusCode;
590
+ oldAttributes[AttributeNames_1.AttributeNames.HTTP_STATUS_TEXT] = (statusMessage || '').toUpperCase();
426
591
  if ((rpcMetadata === null || rpcMetadata === void 0 ? void 0 : rpcMetadata.type) === core_1.RPCType.HTTP && rpcMetadata.route !== undefined) {
427
- attributes[semantic_conventions_1.SEMATTRS_HTTP_ROUTE] = rpcMetadata.route;
592
+ oldAttributes[semantic_conventions_1.SEMATTRS_HTTP_ROUTE] = rpcMetadata.route;
593
+ newAttributes[semantic_conventions_1.ATTR_HTTP_ROUTE] = rpcMetadata.route;
594
+ }
595
+ switch (semconvStability) {
596
+ case 1 /* STABLE */:
597
+ return newAttributes;
598
+ case 2 /* OLD */:
599
+ return oldAttributes;
428
600
  }
429
- return attributes;
601
+ return Object.assign(oldAttributes, newAttributes);
430
602
  };
431
603
  exports.getIncomingRequestAttributesOnResponse = getIncomingRequestAttributesOnResponse;
432
604
  /**
433
605
  * Returns incoming request Metric attributes scoped to the request data
434
- * @param {SpanAttributes} spanAttributes the span attributes
606
+ * @param {Attributes} spanAttributes the span attributes
435
607
  */
436
608
  const getIncomingRequestMetricAttributesOnResponse = (spanAttributes) => {
437
609
  const metricAttributes = {};
@@ -445,6 +617,19 @@ const getIncomingRequestMetricAttributesOnResponse = (spanAttributes) => {
445
617
  return metricAttributes;
446
618
  };
447
619
  exports.getIncomingRequestMetricAttributesOnResponse = getIncomingRequestMetricAttributesOnResponse;
620
+ const getIncomingStableRequestMetricAttributesOnResponse = (spanAttributes) => {
621
+ const metricAttributes = {};
622
+ if (spanAttributes[semantic_conventions_1.ATTR_HTTP_ROUTE] !== undefined) {
623
+ metricAttributes[semantic_conventions_1.ATTR_HTTP_ROUTE] = spanAttributes[semantic_conventions_1.SEMATTRS_HTTP_ROUTE];
624
+ }
625
+ // required if and only if one was sent, same as span requirement
626
+ if (spanAttributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE]) {
627
+ metricAttributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE] =
628
+ spanAttributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE];
629
+ }
630
+ return metricAttributes;
631
+ };
632
+ exports.getIncomingStableRequestMetricAttributesOnResponse = getIncomingStableRequestMetricAttributesOnResponse;
448
633
  function headerCapture(type, headers) {
449
634
  const normalizedHeaders = new Map();
450
635
  for (let i = 0, len = headers.length; i < len; i++) {
@@ -472,4 +657,27 @@ function headerCapture(type, headers) {
472
657
  };
473
658
  }
474
659
  exports.headerCapture = headerCapture;
660
+ const KNOWN_METHODS = new Set([
661
+ // methods from https://www.rfc-editor.org/rfc/rfc9110.html#name-methods
662
+ 'GET',
663
+ 'HEAD',
664
+ 'POST',
665
+ 'PUT',
666
+ 'DELETE',
667
+ 'CONNECT',
668
+ 'OPTIONS',
669
+ 'TRACE',
670
+ // PATCH from https://www.rfc-editor.org/rfc/rfc5789.html
671
+ 'PATCH',
672
+ ]);
673
+ function normalizeMethod(method) {
674
+ if (method == null) {
675
+ return 'GET';
676
+ }
677
+ const upper = method.toUpperCase();
678
+ if (KNOWN_METHODS.has(upper)) {
679
+ return upper;
680
+ }
681
+ return '_OTHER';
682
+ }
475
683
  //# sourceMappingURL=utils.js.map