@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.
package/README.md CHANGED
@@ -68,15 +68,49 @@ Http instrumentation has few options available to choose from. You can set the f
68
68
  | [`requireParentforIncomingSpans`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-http/src/types.ts#L105) | Boolean | Require that is a parent span to create new span for incoming requests. |
69
69
  | [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-http/src/types.ts#L107) | `object` | List of case insensitive HTTP headers to convert to span attributes. Client (outgoing requests, incoming responses) and server (incoming requests, outgoing responses) headers will be converted to span attributes in the form of `http.{request\|response}.header.header_name`, e.g. `http.response.header.content_length` |
70
70
 
71
- The following options are deprecated:
71
+ ## Semantic Conventions
72
72
 
73
- | Options | Type | Description |
74
- | ------- | ---- | ----------- |
75
- | `ignoreIncomingPaths` | `IgnoreMatcher[]` | Http instrumentation will not trace all incoming requests that match paths |
73
+ Prior to version `0.54`, this instrumentation created spans targeting an experimental semantic convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md).
76
74
 
77
- ## Semantic Conventions
75
+ This package is capable of emitting both Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md) and [Version 1.27.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md).
76
+ It is controlled using the environment variable `OTEL_SEMCONV_STABILITY_OPT_IN`, which is a comma separated list of values.
77
+ The values `http` and `http/dup` control this instrumentation.
78
+ See details for the behavior of each of these values below.
79
+ If neither `http` or `http/dup` is included in `OTEL_SEMCONV_STABILITY_OPT_IN`, the old experimental semantic conventions will be used by default.
80
+
81
+ ### Stable Semantic Conventions 1.27
82
+
83
+ Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http` OR `http/dup`.
84
+ This is the recommended configuration, and will soon become the default behavior.
85
+
86
+ Follow all requirements and recommendations of HTTP Client and Server Semantic Conventions Version 1.27.0 for [spans](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md) and [metrics](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-metrics.md), including all required and recommended attributes.
87
+
88
+ Metrics Exported:
89
+
90
+ - [`http.server.request.duration`](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-metrics.md#metric-httpserverrequestduration)
91
+ - [`http.client.request.duration`](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-metrics.md#metric-httpclientrequestduration)
92
+
93
+ ### Upgrading Semantic Conventions
94
+
95
+ When upgrading to the new semantic conventions, it is recommended to do so in the following order:
96
+
97
+ 1. Upgrade `@opentelemetry/instrumentation-http` to the latest version
98
+ 2. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http/dup` to emit both old and new semantic conventions
99
+ 3. Modify alerts, dashboards, metrics, and other processes to expect the new semantic conventions
100
+ 4. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http` to emit only the new semantic conventions
101
+
102
+ This will cause both the old and new semantic conventions to be emitted during the transition period.
103
+
104
+ ### Legacy Behavior (default)
105
+
106
+ Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http/dup` or DOES NOT CONTAIN `http`.
107
+ This is the current default behavior.
108
+
109
+ Create HTTP client spans which implement Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md).
110
+
111
+ #### Server Spans (legacy)
78
112
 
79
- This package uses `@opentelemetry/semantic-conventions` version `1.22+`, which implements Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md)
113
+ When `OTEL_SEMCONV_STABILITY_OPT_IN` is not set or includes `http/dup`, this module implements Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md).
80
114
 
81
115
  Attributes collected:
82
116
 
@@ -4,16 +4,21 @@ import * as url from 'url';
4
4
  import { Func, HttpInstrumentationConfig, HttpRequestArgs } from './types';
5
5
  import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
6
6
  /**
7
- * Http instrumentation instrumentation for Opentelemetry
7
+ * `node:http` and `node:https` instrumentation for OpenTelemetry
8
8
  */
9
9
  export declare class HttpInstrumentation extends InstrumentationBase<HttpInstrumentationConfig> {
10
10
  /** keep track on spans not ended */
11
11
  private readonly _spanNotEnded;
12
12
  private _headerCapture;
13
- private _httpServerDurationHistogram;
14
- private _httpClientDurationHistogram;
13
+ private _oldHttpServerDurationHistogram;
14
+ private _stableHttpServerDurationHistogram;
15
+ private _oldHttpClientDurationHistogram;
16
+ private _stableHttpClientDurationHistogram;
17
+ private _semconvStability;
15
18
  constructor(config?: HttpInstrumentationConfig);
16
19
  protected _updateMetricInstruments(): void;
20
+ private _recordServerDuration;
21
+ private _recordClientDuration;
17
22
  setConfig(config?: HttpInstrumentationConfig): void;
18
23
  init(): [
19
24
  InstrumentationNodeModuleDefinition,
@@ -42,7 +47,8 @@ export declare class HttpInstrumentation extends InstrumentationBase<HttpInstrum
42
47
  * @param request The original request object.
43
48
  * @param span representing the current operation
44
49
  * @param startTime representing the start time of the request to calculate duration in Metric
45
- * @param metricAttributes metric attributes
50
+ * @param oldMetricAttributes metric attributes for old semantic conventions
51
+ * @param stableMetricAttributes metric attributes for new semantic conventions
46
52
  */
47
53
  private _traceClientRequest;
48
54
  private _incomingRequestFunction;
package/build/src/http.js CHANGED
@@ -20,33 +20,90 @@ const api_1 = require("@opentelemetry/api");
20
20
  const core_1 = require("@opentelemetry/core");
21
21
  const semver = require("semver");
22
22
  const url = require("url");
23
- const utils = require("./utils");
24
23
  const version_1 = require("./version");
25
24
  const instrumentation_1 = require("@opentelemetry/instrumentation");
26
25
  const core_2 = require("@opentelemetry/core");
27
26
  const events_1 = require("events");
28
27
  const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
28
+ const utils_1 = require("./utils");
29
29
  /**
30
- * Http instrumentation instrumentation for Opentelemetry
30
+ * `node:http` and `node:https` instrumentation for OpenTelemetry
31
31
  */
32
32
  class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
33
33
  constructor(config = {}) {
34
34
  super('@opentelemetry/instrumentation-http', version_1.VERSION, config);
35
35
  /** keep track on spans not ended */
36
36
  this._spanNotEnded = new WeakSet();
37
+ this._semconvStability = 2 /* OLD */;
37
38
  this._headerCapture = this._createHeaderCapture();
39
+ for (const entry in (0, core_2.getEnv)().OTEL_SEMCONV_STABILITY_OPT_IN) {
40
+ if (entry.toLowerCase() === 'http/dup') {
41
+ // http/dup takes highest precedence. If it is found, there is no need to read the rest of the list
42
+ this._semconvStability = 3 /* DUPLICATE */;
43
+ break;
44
+ }
45
+ else if (entry.toLowerCase() === 'http') {
46
+ this._semconvStability = 1 /* STABLE */;
47
+ }
48
+ }
38
49
  }
39
50
  _updateMetricInstruments() {
40
- this._httpServerDurationHistogram = this.meter.createHistogram('http.server.duration', {
51
+ this._oldHttpServerDurationHistogram = this.meter.createHistogram('http.server.duration', {
41
52
  description: 'Measures the duration of inbound HTTP requests.',
42
53
  unit: 'ms',
43
54
  valueType: api_1.ValueType.DOUBLE,
44
55
  });
45
- this._httpClientDurationHistogram = this.meter.createHistogram('http.client.duration', {
56
+ this._oldHttpClientDurationHistogram = this.meter.createHistogram('http.client.duration', {
46
57
  description: 'Measures the duration of outbound HTTP requests.',
47
58
  unit: 'ms',
48
59
  valueType: api_1.ValueType.DOUBLE,
49
60
  });
61
+ this._stableHttpServerDurationHistogram = this.meter.createHistogram(semantic_conventions_1.METRIC_HTTP_SERVER_REQUEST_DURATION, {
62
+ description: 'Duration of HTTP server requests.',
63
+ unit: 's',
64
+ valueType: api_1.ValueType.DOUBLE,
65
+ advice: {
66
+ explicitBucketBoundaries: [
67
+ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5,
68
+ 7.5, 10,
69
+ ],
70
+ },
71
+ });
72
+ this._stableHttpClientDurationHistogram = this.meter.createHistogram(semantic_conventions_1.METRIC_HTTP_CLIENT_REQUEST_DURATION, {
73
+ description: 'Duration of HTTP client requests.',
74
+ unit: 's',
75
+ valueType: api_1.ValueType.DOUBLE,
76
+ advice: {
77
+ explicitBucketBoundaries: [
78
+ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5,
79
+ 7.5, 10,
80
+ ],
81
+ },
82
+ });
83
+ }
84
+ _recordServerDuration(durationMs, oldAttributes, stableAttributes) {
85
+ if ((this._semconvStability & 2 /* OLD */) ===
86
+ 2 /* OLD */) {
87
+ // old histogram is counted in MS
88
+ this._oldHttpServerDurationHistogram.record(durationMs, oldAttributes);
89
+ }
90
+ if ((this._semconvStability & 1 /* STABLE */) ===
91
+ 1 /* STABLE */) {
92
+ // stable histogram is counted in S
93
+ this._stableHttpServerDurationHistogram.record(durationMs / 1000, stableAttributes);
94
+ }
95
+ }
96
+ _recordClientDuration(durationMs, oldAttributes, stableAttributes) {
97
+ if ((this._semconvStability & 2 /* OLD */) ===
98
+ 2 /* OLD */) {
99
+ // old histogram is counted in MS
100
+ this._oldHttpClientDurationHistogram.record(durationMs, oldAttributes);
101
+ }
102
+ if ((this._semconvStability & 1 /* STABLE */) ===
103
+ 1 /* STABLE */) {
104
+ // stable histogram is counted in S
105
+ this._stableHttpClientDurationHistogram.record(durationMs / 1000, stableAttributes);
106
+ }
50
107
  }
51
108
  setConfig(config = {}) {
52
109
  super.setConfig(config);
@@ -57,9 +114,16 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
57
114
  }
58
115
  _getHttpInstrumentation() {
59
116
  return new instrumentation_1.InstrumentationNodeModuleDefinition('http', ['*'], (moduleExports) => {
117
+ const isESM = moduleExports[Symbol.toStringTag] === 'Module';
60
118
  if (!this.getConfig().disableOutgoingRequestInstrumentation) {
61
119
  const patchedRequest = this._wrap(moduleExports, 'request', this._getPatchOutgoingRequestFunction('http'));
62
- this._wrap(moduleExports, 'get', this._getPatchOutgoingGetFunction(patchedRequest));
120
+ const patchedGet = this._wrap(moduleExports, 'get', this._getPatchOutgoingGetFunction(patchedRequest));
121
+ if (isESM) {
122
+ // To handle `import http from 'http'`, which returns the default
123
+ // export, we need to set `module.default.*`.
124
+ moduleExports.default.request = patchedRequest;
125
+ moduleExports.default.get = patchedGet;
126
+ }
63
127
  }
64
128
  if (!this.getConfig().disableIncomingRequestInstrumentation) {
65
129
  this._wrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction('http'));
@@ -79,9 +143,16 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
79
143
  }
80
144
  _getHttpsInstrumentation() {
81
145
  return new instrumentation_1.InstrumentationNodeModuleDefinition('https', ['*'], (moduleExports) => {
146
+ const isESM = moduleExports[Symbol.toStringTag] === 'Module';
82
147
  if (!this.getConfig().disableOutgoingRequestInstrumentation) {
83
148
  const patchedRequest = this._wrap(moduleExports, 'request', this._getPatchHttpsOutgoingRequestFunction('https'));
84
- this._wrap(moduleExports, 'get', this._getPatchHttpsOutgoingGetFunction(patchedRequest));
149
+ const patchedGet = this._wrap(moduleExports, 'get', this._getPatchHttpsOutgoingGetFunction(patchedRequest));
150
+ if (isESM) {
151
+ // To handle `import https from 'https'`, which returns the default
152
+ // export, we need to set `module.default.*`.
153
+ moduleExports.default.request = patchedRequest;
154
+ moduleExports.default.get = patchedGet;
155
+ }
85
156
  }
86
157
  if (!this.getConfig().disableIncomingRequestInstrumentation) {
87
158
  this._wrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction('https'));
@@ -175,9 +246,10 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
175
246
  * @param request The original request object.
176
247
  * @param span representing the current operation
177
248
  * @param startTime representing the start time of the request to calculate duration in Metric
178
- * @param metricAttributes metric attributes
249
+ * @param oldMetricAttributes metric attributes for old semantic conventions
250
+ * @param stableMetricAttributes metric attributes for new semantic conventions
179
251
  */
180
- _traceClientRequest(request, span, startTime, metricAttributes) {
252
+ _traceClientRequest(request, span, startTime, oldMetricAttributes, stableMetricAttributes) {
181
253
  if (this.getConfig().requestHook) {
182
254
  this._callRequestHook(span, request);
183
255
  }
@@ -195,9 +267,9 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
195
267
  if (request.listenerCount('response') <= 1) {
196
268
  response.resume();
197
269
  }
198
- const responseAttributes = utils.getOutgoingRequestAttributesOnResponse(response);
270
+ const responseAttributes = (0, utils_1.getOutgoingRequestAttributesOnResponse)(response, this._semconvStability);
199
271
  span.setAttributes(responseAttributes);
200
- metricAttributes = Object.assign(metricAttributes, utils.getOutgoingRequestMetricAttributesOnResponse(responseAttributes));
272
+ oldMetricAttributes = Object.assign(oldMetricAttributes, (0, utils_1.getOutgoingRequestMetricAttributesOnResponse)(responseAttributes));
201
273
  if (this.getConfig().responseHook) {
202
274
  this._callResponseHook(span, response);
203
275
  }
@@ -215,15 +287,16 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
215
287
  status = { code: api_1.SpanStatusCode.ERROR };
216
288
  }
217
289
  else {
290
+ // behaves same for new and old semconv
218
291
  status = {
219
- code: utils.parseResponseStatus(api_1.SpanKind.CLIENT, response.statusCode),
292
+ code: (0, utils_1.parseResponseStatus)(api_1.SpanKind.CLIENT, response.statusCode),
220
293
  };
221
294
  }
222
295
  span.setStatus(status);
223
296
  if (this.getConfig().applyCustomAttributesOnSpan) {
224
297
  (0, instrumentation_1.safeExecuteInTheMiddle)(() => this.getConfig().applyCustomAttributesOnSpan(span, request, response), () => { }, true);
225
298
  }
226
- this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, metricAttributes);
299
+ this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
227
300
  };
228
301
  response.on('end', endHandler);
229
302
  // See https://github.com/open-telemetry/opentelemetry-js/pull/3625#issuecomment-1475673533
@@ -236,12 +309,12 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
236
309
  return;
237
310
  }
238
311
  responseFinished = true;
239
- utils.setSpanWithError(span, error);
312
+ (0, utils_1.setSpanWithError)(span, error, this._semconvStability);
240
313
  span.setStatus({
241
314
  code: api_1.SpanStatusCode.ERROR,
242
315
  message: error.message,
243
316
  });
244
- this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, metricAttributes);
317
+ this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
245
318
  });
246
319
  });
247
320
  request.on('close', () => {
@@ -250,7 +323,7 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
250
323
  return;
251
324
  }
252
325
  responseFinished = true;
253
- this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, metricAttributes);
326
+ this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
254
327
  });
255
328
  request.on(events_1.errorMonitor, (error) => {
256
329
  this._diag.debug('outgoingRequest on request error()', error);
@@ -258,8 +331,8 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
258
331
  return;
259
332
  }
260
333
  responseFinished = true;
261
- utils.setSpanWithError(span, error);
262
- this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, metricAttributes);
334
+ (0, utils_1.setSpanWithError)(span, error, this._semconvStability);
335
+ this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
263
336
  });
264
337
  this._diag.debug('http.ClientRequest return request');
265
338
  return request;
@@ -273,17 +346,13 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
273
346
  }
274
347
  const request = args[0];
275
348
  const response = args[1];
276
- const pathname = request.url
277
- ? url.parse(request.url).pathname || '/'
278
- : '/';
279
349
  const method = request.method || 'GET';
280
350
  instrumentation._diag.debug(`${component} instrumentation incomingRequest`);
281
- if (utils.isIgnored(pathname, instrumentation.getConfig().ignoreIncomingPaths, (e) => instrumentation._diag.error('caught ignoreIncomingPaths error: ', e)) ||
282
- (0, instrumentation_1.safeExecuteInTheMiddle)(() => { var _a, _b; return (_b = (_a = instrumentation.getConfig()).ignoreIncomingRequestHook) === null || _b === void 0 ? void 0 : _b.call(_a, request); }, (e) => {
283
- if (e != null) {
284
- instrumentation._diag.error('caught ignoreIncomingRequestHook error: ', e);
285
- }
286
- }, true)) {
351
+ if ((0, instrumentation_1.safeExecuteInTheMiddle)(() => { var _a, _b; return (_b = (_a = instrumentation.getConfig()).ignoreIncomingRequestHook) === null || _b === void 0 ? void 0 : _b.call(_a, request); }, (e) => {
352
+ if (e != null) {
353
+ instrumentation._diag.error('caught ignoreIncomingRequestHook error: ', e);
354
+ }
355
+ }, true)) {
287
356
  return api_1.context.with((0, core_1.suppressTracing)(api_1.context.active()), () => {
288
357
  api_1.context.bind(api_1.context.active(), request);
289
358
  api_1.context.bind(api_1.context.active(), response);
@@ -291,17 +360,28 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
291
360
  });
292
361
  }
293
362
  const headers = request.headers;
294
- const spanAttributes = utils.getIncomingRequestAttributes(request, {
363
+ const spanAttributes = (0, utils_1.getIncomingRequestAttributes)(request, {
295
364
  component: component,
296
365
  serverName: instrumentation.getConfig().serverName,
297
366
  hookAttributes: instrumentation._callStartSpanHook(request, instrumentation.getConfig().startIncomingSpanHook),
367
+ semconvStability: instrumentation._semconvStability,
298
368
  });
299
369
  const spanOptions = {
300
370
  kind: api_1.SpanKind.SERVER,
301
371
  attributes: spanAttributes,
302
372
  };
303
373
  const startTime = (0, core_1.hrTime)();
304
- const metricAttributes = utils.getIncomingRequestMetricAttributes(spanAttributes);
374
+ const oldMetricAttributes = (0, utils_1.getIncomingRequestMetricAttributes)(spanAttributes);
375
+ // request method and url.scheme are both required span attributes
376
+ const stableMetricAttributes = {
377
+ [semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD]: spanAttributes[semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD],
378
+ [semantic_conventions_1.ATTR_URL_SCHEME]: spanAttributes[semantic_conventions_1.ATTR_URL_SCHEME],
379
+ };
380
+ // recommended if and only if one was sent, same as span recommendation
381
+ if (spanAttributes[semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION]) {
382
+ stableMetricAttributes[semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION] =
383
+ spanAttributes[semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION];
384
+ }
305
385
  const ctx = api_1.propagation.extract(api_1.ROOT_CONTEXT, headers);
306
386
  const span = instrumentation._startHttpSpan(method, spanOptions, ctx);
307
387
  const rpcMetadata = {
@@ -324,16 +404,16 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
324
404
  if (hasError) {
325
405
  return;
326
406
  }
327
- instrumentation._onServerResponseFinish(request, response, span, metricAttributes, startTime);
407
+ instrumentation._onServerResponseFinish(request, response, span, oldMetricAttributes, stableMetricAttributes, startTime);
328
408
  });
329
409
  response.on(events_1.errorMonitor, (err) => {
330
410
  hasError = true;
331
- instrumentation._onServerResponseError(span, metricAttributes, startTime, err);
411
+ instrumentation._onServerResponseError(span, oldMetricAttributes, stableMetricAttributes, startTime, err);
332
412
  });
333
413
  return (0, instrumentation_1.safeExecuteInTheMiddle)(() => original.apply(this, [event, ...args]), error => {
334
414
  if (error) {
335
- utils.setSpanWithError(span, error);
336
- instrumentation._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, metricAttributes);
415
+ (0, utils_1.setSpanWithError)(span, error, instrumentation._semconvStability);
416
+ instrumentation._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, oldMetricAttributes, stableMetricAttributes);
337
417
  throw error;
338
418
  }
339
419
  });
@@ -343,14 +423,14 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
343
423
  _outgoingRequestFunction(component, original) {
344
424
  const instrumentation = this;
345
425
  return function outgoingRequest(options, ...args) {
346
- if (!utils.isValidOptionsType(options)) {
426
+ if (!(0, utils_1.isValidOptionsType)(options)) {
347
427
  return original.apply(this, [options, ...args]);
348
428
  }
349
429
  const extraOptions = typeof args[0] === 'object' &&
350
430
  (typeof options === 'string' || options instanceof url.URL)
351
431
  ? args.shift()
352
432
  : undefined;
353
- const { origin, pathname, method, optionsParsed } = utils.getRequestInfo(options, extraOptions);
433
+ const { method, optionsParsed } = (0, utils_1.getRequestInfo)(options, extraOptions);
354
434
  /**
355
435
  * Node 8's https module directly call the http one so to avoid creating
356
436
  * 2 span for the same request we need to check that the protocol is correct
@@ -361,27 +441,42 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
361
441
  optionsParsed.protocol === 'https:') {
362
442
  return original.apply(this, [optionsParsed, ...args]);
363
443
  }
364
- if (utils.isIgnored(origin + pathname, instrumentation.getConfig().ignoreOutgoingUrls, (e) => instrumentation._diag.error('caught ignoreOutgoingUrls error: ', e)) ||
365
- (0, instrumentation_1.safeExecuteInTheMiddle)(() => {
366
- var _a, _b;
367
- return (_b = (_a = instrumentation
368
- .getConfig()).ignoreOutgoingRequestHook) === null || _b === void 0 ? void 0 : _b.call(_a, optionsParsed);
369
- }, (e) => {
370
- if (e != null) {
371
- instrumentation._diag.error('caught ignoreOutgoingRequestHook error: ', e);
372
- }
373
- }, true)) {
444
+ if ((0, instrumentation_1.safeExecuteInTheMiddle)(() => {
445
+ var _a, _b;
446
+ return (_b = (_a = instrumentation
447
+ .getConfig()).ignoreOutgoingRequestHook) === null || _b === void 0 ? void 0 : _b.call(_a, optionsParsed);
448
+ }, (e) => {
449
+ if (e != null) {
450
+ instrumentation._diag.error('caught ignoreOutgoingRequestHook error: ', e);
451
+ }
452
+ }, true)) {
374
453
  return original.apply(this, [optionsParsed, ...args]);
375
454
  }
376
- const { hostname, port } = utils.extractHostnameAndPort(optionsParsed);
377
- const attributes = utils.getOutgoingRequestAttributes(optionsParsed, {
455
+ const { hostname, port } = (0, utils_1.extractHostnameAndPort)(optionsParsed);
456
+ const attributes = (0, utils_1.getOutgoingRequestAttributes)(optionsParsed, {
378
457
  component,
379
458
  port,
380
459
  hostname,
381
460
  hookAttributes: instrumentation._callStartSpanHook(optionsParsed, instrumentation.getConfig().startOutgoingSpanHook),
382
- });
461
+ }, instrumentation._semconvStability);
383
462
  const startTime = (0, core_1.hrTime)();
384
- const metricAttributes = utils.getOutgoingRequestMetricAttributes(attributes);
463
+ const oldMetricAttributes = (0, utils_1.getOutgoingRequestMetricAttributes)(attributes);
464
+ // request method, server address, and server port are both required span attributes
465
+ const stableMetricAttributes = {
466
+ [semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD]: attributes[semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD],
467
+ [semantic_conventions_1.ATTR_SERVER_ADDRESS]: attributes[semantic_conventions_1.ATTR_SERVER_ADDRESS],
468
+ [semantic_conventions_1.ATTR_SERVER_PORT]: attributes[semantic_conventions_1.ATTR_SERVER_PORT],
469
+ };
470
+ // required if and only if one was sent, same as span requirement
471
+ if (attributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE]) {
472
+ stableMetricAttributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE] =
473
+ attributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE];
474
+ }
475
+ // recommended if and only if one was sent, same as span recommendation
476
+ if (attributes[semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION]) {
477
+ stableMetricAttributes[semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION] =
478
+ attributes[semantic_conventions_1.ATTR_NETWORK_PROTOCOL_VERSION];
479
+ }
385
480
  const spanOptions = {
386
481
  kind: api_1.SpanKind.CLIENT,
387
482
  attributes,
@@ -409,23 +504,24 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
409
504
  }
410
505
  const request = (0, instrumentation_1.safeExecuteInTheMiddle)(() => original.apply(this, [optionsParsed, ...args]), error => {
411
506
  if (error) {
412
- utils.setSpanWithError(span, error);
413
- instrumentation._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, metricAttributes);
507
+ (0, utils_1.setSpanWithError)(span, error, instrumentation._semconvStability);
508
+ instrumentation._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
414
509
  throw error;
415
510
  }
416
511
  });
417
512
  instrumentation._diag.debug(`${component} instrumentation outgoingRequest`);
418
513
  api_1.context.bind(parentContext, request);
419
- return instrumentation._traceClientRequest(request, span, startTime, metricAttributes);
514
+ return instrumentation._traceClientRequest(request, span, startTime, oldMetricAttributes, stableMetricAttributes);
420
515
  });
421
516
  };
422
517
  }
423
- _onServerResponseFinish(request, response, span, metricAttributes, startTime) {
424
- const attributes = utils.getIncomingRequestAttributesOnResponse(request, response);
425
- metricAttributes = Object.assign(metricAttributes, utils.getIncomingRequestMetricAttributesOnResponse(attributes));
518
+ _onServerResponseFinish(request, response, span, oldMetricAttributes, stableMetricAttributes, startTime) {
519
+ const attributes = (0, utils_1.getIncomingRequestAttributesOnResponse)(request, response, this._semconvStability);
520
+ oldMetricAttributes = Object.assign(oldMetricAttributes, (0, utils_1.getIncomingRequestMetricAttributesOnResponse)(attributes));
521
+ stableMetricAttributes = Object.assign(stableMetricAttributes, (0, utils_1.getIncomingStableRequestMetricAttributesOnResponse)(attributes));
426
522
  this._headerCapture.server.captureResponseHeaders(span, header => response.getHeader(header));
427
523
  span.setAttributes(attributes).setStatus({
428
- code: utils.parseResponseStatus(api_1.SpanKind.SERVER, response.statusCode),
524
+ code: (0, utils_1.parseResponseStatus)(api_1.SpanKind.SERVER, response.statusCode),
429
525
  });
430
526
  const route = attributes[semantic_conventions_1.SEMATTRS_HTTP_ROUTE];
431
527
  if (route) {
@@ -434,11 +530,12 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
434
530
  if (this.getConfig().applyCustomAttributesOnSpan) {
435
531
  (0, instrumentation_1.safeExecuteInTheMiddle)(() => this.getConfig().applyCustomAttributesOnSpan(span, request, response), () => { }, true);
436
532
  }
437
- this._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, metricAttributes);
533
+ this._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, oldMetricAttributes, stableMetricAttributes);
438
534
  }
439
- _onServerResponseError(span, metricAttributes, startTime, error) {
440
- utils.setSpanWithError(span, error);
441
- this._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, metricAttributes);
535
+ _onServerResponseError(span, oldMetricAttributes, stableMetricAttributes, startTime, error) {
536
+ (0, utils_1.setSpanWithError)(span, error, this._semconvStability);
537
+ // TODO get error attributes for metrics
538
+ this._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, oldMetricAttributes, stableMetricAttributes);
442
539
  }
443
540
  _startHttpSpan(name, options, ctx = api_1.context.active()) {
444
541
  /*
@@ -462,7 +559,7 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
462
559
  this._spanNotEnded.add(span);
463
560
  return span;
464
561
  }
465
- _closeHttpSpan(span, spanKind, startTime, metricAttributes) {
562
+ _closeHttpSpan(span, spanKind, startTime, oldMetricAttributes, stableMetricAttributes) {
466
563
  if (!this._spanNotEnded.has(span)) {
467
564
  return;
468
565
  }
@@ -471,10 +568,10 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
471
568
  // Record metrics
472
569
  const duration = (0, core_1.hrTimeToMilliseconds)((0, core_1.hrTimeDuration)(startTime, (0, core_1.hrTime)()));
473
570
  if (spanKind === api_1.SpanKind.SERVER) {
474
- this._httpServerDurationHistogram.record(duration, metricAttributes);
571
+ this._recordServerDuration(duration, oldMetricAttributes, stableMetricAttributes);
475
572
  }
476
573
  else if (spanKind === api_1.SpanKind.CLIENT) {
477
- this._httpClientDurationHistogram.record(duration, metricAttributes);
574
+ this._recordClientDuration(duration, oldMetricAttributes, stableMetricAttributes);
478
575
  }
479
576
  }
480
577
  _callResponseHook(span, response) {
@@ -493,12 +590,12 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
493
590
  const config = this.getConfig();
494
591
  return {
495
592
  client: {
496
- captureRequestHeaders: utils.headerCapture('request', (_c = (_b = (_a = config.headersToSpanAttributes) === null || _a === void 0 ? void 0 : _a.client) === null || _b === void 0 ? void 0 : _b.requestHeaders) !== null && _c !== void 0 ? _c : []),
497
- captureResponseHeaders: utils.headerCapture('response', (_f = (_e = (_d = config.headersToSpanAttributes) === null || _d === void 0 ? void 0 : _d.client) === null || _e === void 0 ? void 0 : _e.responseHeaders) !== null && _f !== void 0 ? _f : []),
593
+ captureRequestHeaders: (0, utils_1.headerCapture)('request', (_c = (_b = (_a = config.headersToSpanAttributes) === null || _a === void 0 ? void 0 : _a.client) === null || _b === void 0 ? void 0 : _b.requestHeaders) !== null && _c !== void 0 ? _c : []),
594
+ captureResponseHeaders: (0, utils_1.headerCapture)('response', (_f = (_e = (_d = config.headersToSpanAttributes) === null || _d === void 0 ? void 0 : _d.client) === null || _e === void 0 ? void 0 : _e.responseHeaders) !== null && _f !== void 0 ? _f : []),
498
595
  },
499
596
  server: {
500
- captureRequestHeaders: utils.headerCapture('request', (_j = (_h = (_g = config.headersToSpanAttributes) === null || _g === void 0 ? void 0 : _g.server) === null || _h === void 0 ? void 0 : _h.requestHeaders) !== null && _j !== void 0 ? _j : []),
501
- captureResponseHeaders: utils.headerCapture('response', (_m = (_l = (_k = config.headersToSpanAttributes) === null || _k === void 0 ? void 0 : _k.server) === null || _l === void 0 ? void 0 : _l.responseHeaders) !== null && _m !== void 0 ? _m : []),
597
+ captureRequestHeaders: (0, utils_1.headerCapture)('request', (_j = (_h = (_g = config.headersToSpanAttributes) === null || _g === void 0 ? void 0 : _g.server) === null || _h === void 0 ? void 0 : _h.requestHeaders) !== null && _j !== void 0 ? _j : []),
598
+ captureResponseHeaders: (0, utils_1.headerCapture)('response', (_m = (_l = (_k = config.headersToSpanAttributes) === null || _k === void 0 ? void 0 : _k.server) === null || _l === void 0 ? void 0 : _l.responseHeaders) !== null && _m !== void 0 ? _m : []),
502
599
  },
503
600
  };
504
601
  }