@opentelemetry/instrumentation-http 0.52.1 → 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 +42 -6
- package/build/src/http.d.ts +10 -4
- package/build/src/http.js +189 -76
- package/build/src/http.js.map +1 -1
- package/build/src/index.d.ts +3 -3
- package/build/src/index.js +24 -13
- package/build/src/index.js.map +1 -1
- package/build/src/types.d.ts +21 -13
- package/build/src/types.js.map +1 -1
- package/build/src/utils.d.ts +39 -35
- package/build/src/utils.js +287 -79
- package/build/src/utils.js.map +1 -1
- package/build/src/version.d.ts +1 -1
- package/build/src/version.js +1 -1
- package/build/src/version.js.map +1 -1
- package/package.json +18 -18
package/README.md
CHANGED
|
@@ -61,20 +61,56 @@ Http instrumentation has few options available to choose from. You can set the f
|
|
|
61
61
|
| [`startOutgoingSpanHook`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-http/src/types.ts#L99) | `StartOutgoingSpanCustomAttributeFunction` | Function for adding custom attributes before a span is started in outgoingRequest |
|
|
62
62
|
| `ignoreIncomingRequestHook` | `IgnoreIncomingRequestFunction` | Http instrumentation will not trace all incoming requests that matched with custom function |
|
|
63
63
|
| `ignoreOutgoingRequestHook` | `IgnoreOutgoingRequestFunction` | Http instrumentation will not trace all outgoing requests that matched with custom function |
|
|
64
|
+
| `disableOutgoingRequestInstrumentation` | `boolean` | Set to true to avoid instrumenting outgoing requests at all. This can be helpful when another instrumentation handles outgoing requests. |
|
|
65
|
+
| `disableIncomingRequestInstrumentation` | `boolean` | Set to true to avoid instrumenting incoming requests at all. This can be helpful when another instrumentation handles incoming requests. |
|
|
64
66
|
| [`serverName`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-http/src/types.ts#L101) | `string` | The primary server name of the matched virtual host. |
|
|
65
67
|
| [`requireParentforOutgoingSpans`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-http/src/types.ts#L103) | Boolean | Require that is a parent span to create new span for outgoing requests. |
|
|
66
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. |
|
|
67
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` |
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
## Semantic Conventions
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
| ------- | ---- | ----------- |
|
|
73
|
-
| `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).
|
|
74
74
|
|
|
75
|
-
|
|
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)
|
|
76
112
|
|
|
77
|
-
|
|
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).
|
|
78
114
|
|
|
79
115
|
Attributes collected:
|
|
80
116
|
|
package/build/src/http.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
14
|
-
private
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
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.
|
|
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,30 +114,60 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
57
114
|
}
|
|
58
115
|
_getHttpInstrumentation() {
|
|
59
116
|
return new instrumentation_1.InstrumentationNodeModuleDefinition('http', ['*'], (moduleExports) => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
117
|
+
const isESM = moduleExports[Symbol.toStringTag] === 'Module';
|
|
118
|
+
if (!this.getConfig().disableOutgoingRequestInstrumentation) {
|
|
119
|
+
const patchedRequest = this._wrap(moduleExports, 'request', this._getPatchOutgoingRequestFunction('http'));
|
|
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
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!this.getConfig().disableIncomingRequestInstrumentation) {
|
|
129
|
+
this._wrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction('http'));
|
|
130
|
+
}
|
|
63
131
|
return moduleExports;
|
|
64
132
|
}, (moduleExports) => {
|
|
65
133
|
if (moduleExports === undefined)
|
|
66
134
|
return;
|
|
67
|
-
this.
|
|
68
|
-
|
|
69
|
-
|
|
135
|
+
if (!this.getConfig().disableOutgoingRequestInstrumentation) {
|
|
136
|
+
this._unwrap(moduleExports, 'request');
|
|
137
|
+
this._unwrap(moduleExports, 'get');
|
|
138
|
+
}
|
|
139
|
+
if (!this.getConfig().disableIncomingRequestInstrumentation) {
|
|
140
|
+
this._unwrap(moduleExports.Server.prototype, 'emit');
|
|
141
|
+
}
|
|
70
142
|
});
|
|
71
143
|
}
|
|
72
144
|
_getHttpsInstrumentation() {
|
|
73
145
|
return new instrumentation_1.InstrumentationNodeModuleDefinition('https', ['*'], (moduleExports) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
146
|
+
const isESM = moduleExports[Symbol.toStringTag] === 'Module';
|
|
147
|
+
if (!this.getConfig().disableOutgoingRequestInstrumentation) {
|
|
148
|
+
const patchedRequest = this._wrap(moduleExports, 'request', this._getPatchHttpsOutgoingRequestFunction('https'));
|
|
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
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!this.getConfig().disableIncomingRequestInstrumentation) {
|
|
158
|
+
this._wrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction('https'));
|
|
159
|
+
}
|
|
77
160
|
return moduleExports;
|
|
78
161
|
}, (moduleExports) => {
|
|
79
162
|
if (moduleExports === undefined)
|
|
80
163
|
return;
|
|
81
|
-
this.
|
|
82
|
-
|
|
83
|
-
|
|
164
|
+
if (!this.getConfig().disableOutgoingRequestInstrumentation) {
|
|
165
|
+
this._unwrap(moduleExports, 'request');
|
|
166
|
+
this._unwrap(moduleExports, 'get');
|
|
167
|
+
}
|
|
168
|
+
if (!this.getConfig().disableIncomingRequestInstrumentation) {
|
|
169
|
+
this._unwrap(moduleExports.Server.prototype, 'emit');
|
|
170
|
+
}
|
|
84
171
|
});
|
|
85
172
|
}
|
|
86
173
|
/**
|
|
@@ -159,9 +246,10 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
159
246
|
* @param request The original request object.
|
|
160
247
|
* @param span representing the current operation
|
|
161
248
|
* @param startTime representing the start time of the request to calculate duration in Metric
|
|
162
|
-
* @param
|
|
249
|
+
* @param oldMetricAttributes metric attributes for old semantic conventions
|
|
250
|
+
* @param stableMetricAttributes metric attributes for new semantic conventions
|
|
163
251
|
*/
|
|
164
|
-
_traceClientRequest(request, span, startTime,
|
|
252
|
+
_traceClientRequest(request, span, startTime, oldMetricAttributes, stableMetricAttributes) {
|
|
165
253
|
if (this.getConfig().requestHook) {
|
|
166
254
|
this._callRequestHook(span, request);
|
|
167
255
|
}
|
|
@@ -179,9 +267,9 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
179
267
|
if (request.listenerCount('response') <= 1) {
|
|
180
268
|
response.resume();
|
|
181
269
|
}
|
|
182
|
-
const responseAttributes =
|
|
270
|
+
const responseAttributes = (0, utils_1.getOutgoingRequestAttributesOnResponse)(response, this._semconvStability);
|
|
183
271
|
span.setAttributes(responseAttributes);
|
|
184
|
-
|
|
272
|
+
oldMetricAttributes = Object.assign(oldMetricAttributes, (0, utils_1.getOutgoingRequestMetricAttributesOnResponse)(responseAttributes));
|
|
185
273
|
if (this.getConfig().responseHook) {
|
|
186
274
|
this._callResponseHook(span, response);
|
|
187
275
|
}
|
|
@@ -199,15 +287,16 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
199
287
|
status = { code: api_1.SpanStatusCode.ERROR };
|
|
200
288
|
}
|
|
201
289
|
else {
|
|
290
|
+
// behaves same for new and old semconv
|
|
202
291
|
status = {
|
|
203
|
-
code:
|
|
292
|
+
code: (0, utils_1.parseResponseStatus)(api_1.SpanKind.CLIENT, response.statusCode),
|
|
204
293
|
};
|
|
205
294
|
}
|
|
206
295
|
span.setStatus(status);
|
|
207
296
|
if (this.getConfig().applyCustomAttributesOnSpan) {
|
|
208
297
|
(0, instrumentation_1.safeExecuteInTheMiddle)(() => this.getConfig().applyCustomAttributesOnSpan(span, request, response), () => { }, true);
|
|
209
298
|
}
|
|
210
|
-
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime,
|
|
299
|
+
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
211
300
|
};
|
|
212
301
|
response.on('end', endHandler);
|
|
213
302
|
// See https://github.com/open-telemetry/opentelemetry-js/pull/3625#issuecomment-1475673533
|
|
@@ -220,12 +309,12 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
220
309
|
return;
|
|
221
310
|
}
|
|
222
311
|
responseFinished = true;
|
|
223
|
-
|
|
312
|
+
(0, utils_1.setSpanWithError)(span, error, this._semconvStability);
|
|
224
313
|
span.setStatus({
|
|
225
314
|
code: api_1.SpanStatusCode.ERROR,
|
|
226
315
|
message: error.message,
|
|
227
316
|
});
|
|
228
|
-
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime,
|
|
317
|
+
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
229
318
|
});
|
|
230
319
|
});
|
|
231
320
|
request.on('close', () => {
|
|
@@ -234,7 +323,7 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
234
323
|
return;
|
|
235
324
|
}
|
|
236
325
|
responseFinished = true;
|
|
237
|
-
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime,
|
|
326
|
+
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
238
327
|
});
|
|
239
328
|
request.on(events_1.errorMonitor, (error) => {
|
|
240
329
|
this._diag.debug('outgoingRequest on request error()', error);
|
|
@@ -242,8 +331,8 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
242
331
|
return;
|
|
243
332
|
}
|
|
244
333
|
responseFinished = true;
|
|
245
|
-
|
|
246
|
-
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime,
|
|
334
|
+
(0, utils_1.setSpanWithError)(span, error, this._semconvStability);
|
|
335
|
+
this._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
247
336
|
});
|
|
248
337
|
this._diag.debug('http.ClientRequest return request');
|
|
249
338
|
return request;
|
|
@@ -257,17 +346,13 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
257
346
|
}
|
|
258
347
|
const request = args[0];
|
|
259
348
|
const response = args[1];
|
|
260
|
-
const pathname = request.url
|
|
261
|
-
? url.parse(request.url).pathname || '/'
|
|
262
|
-
: '/';
|
|
263
349
|
const method = request.method || 'GET';
|
|
264
350
|
instrumentation._diag.debug(`${component} instrumentation incomingRequest`);
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}, 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)) {
|
|
271
356
|
return api_1.context.with((0, core_1.suppressTracing)(api_1.context.active()), () => {
|
|
272
357
|
api_1.context.bind(api_1.context.active(), request);
|
|
273
358
|
api_1.context.bind(api_1.context.active(), response);
|
|
@@ -275,17 +360,28 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
275
360
|
});
|
|
276
361
|
}
|
|
277
362
|
const headers = request.headers;
|
|
278
|
-
const spanAttributes =
|
|
363
|
+
const spanAttributes = (0, utils_1.getIncomingRequestAttributes)(request, {
|
|
279
364
|
component: component,
|
|
280
365
|
serverName: instrumentation.getConfig().serverName,
|
|
281
366
|
hookAttributes: instrumentation._callStartSpanHook(request, instrumentation.getConfig().startIncomingSpanHook),
|
|
367
|
+
semconvStability: instrumentation._semconvStability,
|
|
282
368
|
});
|
|
283
369
|
const spanOptions = {
|
|
284
370
|
kind: api_1.SpanKind.SERVER,
|
|
285
371
|
attributes: spanAttributes,
|
|
286
372
|
};
|
|
287
373
|
const startTime = (0, core_1.hrTime)();
|
|
288
|
-
const
|
|
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
|
+
}
|
|
289
385
|
const ctx = api_1.propagation.extract(api_1.ROOT_CONTEXT, headers);
|
|
290
386
|
const span = instrumentation._startHttpSpan(method, spanOptions, ctx);
|
|
291
387
|
const rpcMetadata = {
|
|
@@ -308,16 +404,16 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
308
404
|
if (hasError) {
|
|
309
405
|
return;
|
|
310
406
|
}
|
|
311
|
-
instrumentation._onServerResponseFinish(request, response, span,
|
|
407
|
+
instrumentation._onServerResponseFinish(request, response, span, oldMetricAttributes, stableMetricAttributes, startTime);
|
|
312
408
|
});
|
|
313
409
|
response.on(events_1.errorMonitor, (err) => {
|
|
314
410
|
hasError = true;
|
|
315
|
-
instrumentation._onServerResponseError(span,
|
|
411
|
+
instrumentation._onServerResponseError(span, oldMetricAttributes, stableMetricAttributes, startTime, err);
|
|
316
412
|
});
|
|
317
413
|
return (0, instrumentation_1.safeExecuteInTheMiddle)(() => original.apply(this, [event, ...args]), error => {
|
|
318
414
|
if (error) {
|
|
319
|
-
|
|
320
|
-
instrumentation._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime,
|
|
415
|
+
(0, utils_1.setSpanWithError)(span, error, instrumentation._semconvStability);
|
|
416
|
+
instrumentation._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
321
417
|
throw error;
|
|
322
418
|
}
|
|
323
419
|
});
|
|
@@ -327,14 +423,14 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
327
423
|
_outgoingRequestFunction(component, original) {
|
|
328
424
|
const instrumentation = this;
|
|
329
425
|
return function outgoingRequest(options, ...args) {
|
|
330
|
-
if (!
|
|
426
|
+
if (!(0, utils_1.isValidOptionsType)(options)) {
|
|
331
427
|
return original.apply(this, [options, ...args]);
|
|
332
428
|
}
|
|
333
429
|
const extraOptions = typeof args[0] === 'object' &&
|
|
334
430
|
(typeof options === 'string' || options instanceof url.URL)
|
|
335
431
|
? args.shift()
|
|
336
432
|
: undefined;
|
|
337
|
-
const {
|
|
433
|
+
const { method, optionsParsed } = (0, utils_1.getRequestInfo)(options, extraOptions);
|
|
338
434
|
/**
|
|
339
435
|
* Node 8's https module directly call the http one so to avoid creating
|
|
340
436
|
* 2 span for the same request we need to check that the protocol is correct
|
|
@@ -345,27 +441,42 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
345
441
|
optionsParsed.protocol === 'https:') {
|
|
346
442
|
return original.apply(this, [optionsParsed, ...args]);
|
|
347
443
|
}
|
|
348
|
-
if (
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}, 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)) {
|
|
358
453
|
return original.apply(this, [optionsParsed, ...args]);
|
|
359
454
|
}
|
|
360
|
-
const { hostname, port } =
|
|
361
|
-
const attributes =
|
|
455
|
+
const { hostname, port } = (0, utils_1.extractHostnameAndPort)(optionsParsed);
|
|
456
|
+
const attributes = (0, utils_1.getOutgoingRequestAttributes)(optionsParsed, {
|
|
362
457
|
component,
|
|
363
458
|
port,
|
|
364
459
|
hostname,
|
|
365
460
|
hookAttributes: instrumentation._callStartSpanHook(optionsParsed, instrumentation.getConfig().startOutgoingSpanHook),
|
|
366
|
-
});
|
|
461
|
+
}, instrumentation._semconvStability);
|
|
367
462
|
const startTime = (0, core_1.hrTime)();
|
|
368
|
-
const
|
|
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
|
+
}
|
|
369
480
|
const spanOptions = {
|
|
370
481
|
kind: api_1.SpanKind.CLIENT,
|
|
371
482
|
attributes,
|
|
@@ -393,23 +504,24 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
393
504
|
}
|
|
394
505
|
const request = (0, instrumentation_1.safeExecuteInTheMiddle)(() => original.apply(this, [optionsParsed, ...args]), error => {
|
|
395
506
|
if (error) {
|
|
396
|
-
|
|
397
|
-
instrumentation._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime,
|
|
507
|
+
(0, utils_1.setSpanWithError)(span, error, instrumentation._semconvStability);
|
|
508
|
+
instrumentation._closeHttpSpan(span, api_1.SpanKind.CLIENT, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
398
509
|
throw error;
|
|
399
510
|
}
|
|
400
511
|
});
|
|
401
512
|
instrumentation._diag.debug(`${component} instrumentation outgoingRequest`);
|
|
402
513
|
api_1.context.bind(parentContext, request);
|
|
403
|
-
return instrumentation._traceClientRequest(request, span, startTime,
|
|
514
|
+
return instrumentation._traceClientRequest(request, span, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
404
515
|
});
|
|
405
516
|
};
|
|
406
517
|
}
|
|
407
|
-
_onServerResponseFinish(request, response, span,
|
|
408
|
-
const attributes =
|
|
409
|
-
|
|
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));
|
|
410
522
|
this._headerCapture.server.captureResponseHeaders(span, header => response.getHeader(header));
|
|
411
523
|
span.setAttributes(attributes).setStatus({
|
|
412
|
-
code:
|
|
524
|
+
code: (0, utils_1.parseResponseStatus)(api_1.SpanKind.SERVER, response.statusCode),
|
|
413
525
|
});
|
|
414
526
|
const route = attributes[semantic_conventions_1.SEMATTRS_HTTP_ROUTE];
|
|
415
527
|
if (route) {
|
|
@@ -418,11 +530,12 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
418
530
|
if (this.getConfig().applyCustomAttributesOnSpan) {
|
|
419
531
|
(0, instrumentation_1.safeExecuteInTheMiddle)(() => this.getConfig().applyCustomAttributesOnSpan(span, request, response), () => { }, true);
|
|
420
532
|
}
|
|
421
|
-
this._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime,
|
|
533
|
+
this._closeHttpSpan(span, api_1.SpanKind.SERVER, startTime, oldMetricAttributes, stableMetricAttributes);
|
|
422
534
|
}
|
|
423
|
-
_onServerResponseError(span,
|
|
424
|
-
|
|
425
|
-
|
|
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);
|
|
426
539
|
}
|
|
427
540
|
_startHttpSpan(name, options, ctx = api_1.context.active()) {
|
|
428
541
|
/*
|
|
@@ -446,7 +559,7 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
446
559
|
this._spanNotEnded.add(span);
|
|
447
560
|
return span;
|
|
448
561
|
}
|
|
449
|
-
_closeHttpSpan(span, spanKind, startTime,
|
|
562
|
+
_closeHttpSpan(span, spanKind, startTime, oldMetricAttributes, stableMetricAttributes) {
|
|
450
563
|
if (!this._spanNotEnded.has(span)) {
|
|
451
564
|
return;
|
|
452
565
|
}
|
|
@@ -455,10 +568,10 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
455
568
|
// Record metrics
|
|
456
569
|
const duration = (0, core_1.hrTimeToMilliseconds)((0, core_1.hrTimeDuration)(startTime, (0, core_1.hrTime)()));
|
|
457
570
|
if (spanKind === api_1.SpanKind.SERVER) {
|
|
458
|
-
this.
|
|
571
|
+
this._recordServerDuration(duration, oldMetricAttributes, stableMetricAttributes);
|
|
459
572
|
}
|
|
460
573
|
else if (spanKind === api_1.SpanKind.CLIENT) {
|
|
461
|
-
this.
|
|
574
|
+
this._recordClientDuration(duration, oldMetricAttributes, stableMetricAttributes);
|
|
462
575
|
}
|
|
463
576
|
}
|
|
464
577
|
_callResponseHook(span, response) {
|
|
@@ -477,12 +590,12 @@ class HttpInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
|
477
590
|
const config = this.getConfig();
|
|
478
591
|
return {
|
|
479
592
|
client: {
|
|
480
|
-
captureRequestHeaders:
|
|
481
|
-
captureResponseHeaders:
|
|
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 : []),
|
|
482
595
|
},
|
|
483
596
|
server: {
|
|
484
|
-
captureRequestHeaders:
|
|
485
|
-
captureResponseHeaders:
|
|
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 : []),
|
|
486
599
|
},
|
|
487
600
|
};
|
|
488
601
|
}
|