diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 4d2903e0f7..df038b1ed8 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) * feat(api-logs): Add delegating no-op logger provider [#4861](https://github.com/open-telemetry/opentelemetry-js/pull/4861) @hectorhdzg +* feat(instrumentation-http): Add support for client span semantic conventions 1.27 [#4940](https://github.com/open-telemetry/opentelemetry-js/pull/4940) @dyladan ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-instrumentation-http/README.md b/experimental/packages/opentelemetry-instrumentation-http/README.md index 530205ac77..cdd2f2337f 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/README.md +++ b/experimental/packages/opentelemetry-instrumentation-http/README.md @@ -76,7 +76,44 @@ The following options are deprecated: ## Semantic Conventions -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) +### Client Spans + +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). + +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). +It is controlled using the environment variable `OTEL_SEMCONV_STABILITY_OPT_IN`, which is a comma separated list of values. +The values `http` and `http/dup` control this instrumentation. +See details for the behavior of each of these values below. +If neither `http` or `http/dup` is included in `OTEL_SEMCONV_STABILITY_OPT_IN`, the old experimental semantic conventions will be used by default. + +#### Stable Semantic Conventions 1.27 + +Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http` OR `http/dup`. +This is the recommended configuration, and will soon become the default behavior. + +Follow all requirements and recommendations of HTTP Client Span Semantic Conventions [Version 1.27.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md), including all required and recommended attributes. + +#### Legacy Behavior (default) + +Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http/dup` or DOES NOT CONTAIN `http`. +This is the current default behavior. + +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). + +#### Upgrading Semantic Conventions + +When upgrading to the new semantic conventions, it is recommended to do so in the following order: + +1. Upgrade `@opentelemetry/instrumentation-http` to the latest version +2. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http/dup` to emit both old and new semantic conventions +3. Modify alerts, dashboards, metrics, and other processes to expect the new semantic conventions +4. Set `OTEL_SEMCONV_STABILITY_OPT_IN=http` to emit only the new semantic conventions + +This will cause both the old and new semantic conventions to be emitted during the transition period. + +### Server Spans + +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). Attributes collected: diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 42a4332718..06bc3cac50 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -47,17 +47,39 @@ import { HttpInstrumentationConfig, HttpRequestArgs, Https, + SemconvStability, } from './types'; -import * as utils from './utils'; import { VERSION } from './version'; import { InstrumentationBase, InstrumentationNodeModuleDefinition, safeExecuteInTheMiddle, } from '@opentelemetry/instrumentation'; -import { RPCMetadata, RPCType, setRPCMetadata } from '@opentelemetry/core'; +import { + RPCMetadata, + RPCType, + setRPCMetadata, + getEnv, +} from '@opentelemetry/core'; import { errorMonitor } from 'events'; import { SEMATTRS_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; +import { + extractHostnameAndPort, + getIncomingRequestAttributes, + getIncomingRequestAttributesOnResponse, + getIncomingRequestMetricAttributes, + getIncomingRequestMetricAttributesOnResponse, + getOutgoingRequestAttributes, + getOutgoingRequestAttributesOnResponse, + getOutgoingRequestMetricAttributes, + getOutgoingRequestMetricAttributesOnResponse, + getRequestInfo, + headerCapture, + isIgnored, + isValidOptionsType, + parseResponseStatus, + setSpanWithError, +} from './utils'; /** * Http instrumentation instrumentation for Opentelemetry @@ -69,9 +91,21 @@ export class HttpInstrumentation extends InstrumentationBase @@ -487,7 +521,7 @@ export class HttpInstrumentation extends InstrumentationBase original.apply(this, [event, ...args]), error => { if (error) { - utils.setSpanWithError(span, error); + setSpanWithError( + span, + error, + instrumentation._semconvStability + ); instrumentation._closeHttpSpan( span, SpanKind.SERVER, @@ -584,7 +622,7 @@ export class HttpInstrumentation extends InstrumentationBase @@ -635,21 +673,25 @@ export class HttpInstrumentation extends InstrumentationBase original.apply(this, [optionsParsed, ...args]), error => { if (error) { - utils.setSpanWithError(span, error); + setSpanWithError(span, error, instrumentation._semconvStability); instrumentation._closeHttpSpan( span, SpanKind.CLIENT, @@ -716,13 +758,13 @@ export class HttpInstrumentation extends InstrumentationBase @@ -730,7 +772,7 @@ export class HttpInstrumentation extends InstrumentationBase { +export const setSpanWithError = ( + span: Span, + error: Err, + semconvStability: SemconvStability +): void => { const message = error.message; - span.setAttribute(AttributeNames.HTTP_ERROR_NAME, error.name); - span.setAttribute(AttributeNames.HTTP_ERROR_MESSAGE, message); + if ((semconvStability & SemconvStability.OLD) === SemconvStability.OLD) { + span.setAttribute(AttributeNames.HTTP_ERROR_NAME, error.name); + span.setAttribute(AttributeNames.HTTP_ERROR_MESSAGE, message); + } + + if ( + (semconvStability & SemconvStability.STABLE) === + SemconvStability.STABLE + ) { + span.setAttribute(ATTR_ERROR_TYPE, error.name); + } + span.setStatus({ code: SpanStatusCode.ERROR, message }); span.recordException(error); }; @@ -196,6 +227,8 @@ export const setRequestContentLengthAttribute = ( * Adds attributes for response content-length and content-encoding HTTP headers * @param { IncomingMessage } Response object whose headers will be analyzed * @param { SpanAttributes } SpanAttributes object to be modified + * + * @deprecated this is for an older version of semconv. It is retained for compatibility using OTEL_SEMCONV_STABILITY_OPT_IN */ export const setResponseContentLengthAttribute = ( response: IncomingMessage, @@ -343,6 +376,7 @@ export const extractHostnameAndPort = ( * Returns outgoing request attributes scoped to the options passed to the request * @param {ParsedRequestOptions} requestOptions the same options used to make the request * @param {{ component: string, hostname: string, hookAttributes?: SpanAttributes }} options used to pass data needed to create attributes + * @param {SemconvStability} semconvStability determines which semconv version to use */ export const getOutgoingRequestAttributes = ( requestOptions: ParsedRequestOptions, @@ -351,7 +385,8 @@ export const getOutgoingRequestAttributes = ( hostname: string; port: string | number; hookAttributes?: SpanAttributes; - } + }, + semconvStability: SemconvStability ): SpanAttributes => { const hostname = options.hostname; const port = options.port; @@ -359,22 +394,49 @@ export const getOutgoingRequestAttributes = ( const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; const headers = requestOptions.headers || {}; const userAgent = headers['user-agent']; - const attributes: SpanAttributes = { - [SEMATTRS_HTTP_URL]: getAbsoluteUrl( - requestOptions, - headers, - `${options.component}:` - ), + const urlFull = getAbsoluteUrl( + requestOptions, + headers, + `${options.component}:` + ); + const oldAttributes: SpanAttributes = { + [SEMATTRS_HTTP_URL]: urlFull, [SEMATTRS_HTTP_METHOD]: method, [SEMATTRS_HTTP_TARGET]: requestOptions.path || '/', [SEMATTRS_NET_PEER_NAME]: hostname, [SEMATTRS_HTTP_HOST]: headers.host ?? `${hostname}:${port}`, }; + const newAttributes: Attributes = { + // Required attributes + [ATTR_HTTP_REQUEST_METHOD]: method, + [ATTR_SERVER_ADDRESS]: hostname, + [ATTR_SERVER_PORT]: Number(port), + [ATTR_URL_FULL]: urlFull, + // leaving out protocol version, it is not yet negotiated + // leaving out protocol name, it is only required when protocol version is set + // retries and redirects not supported + + // Opt-in attributes left off for now + }; + + // conditionally required if request method required case normalization + if (requestMethod && method !== requestMethod) { + newAttributes[ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = requestMethod; + } + if (userAgent !== undefined) { - attributes[SEMATTRS_HTTP_USER_AGENT] = userAgent; + oldAttributes[SEMATTRS_HTTP_USER_AGENT] = userAgent; } - return Object.assign(attributes, options.hookAttributes); + + switch (semconvStability) { + case SemconvStability.STABLE: + return Object.assign(newAttributes, options.hookAttributes); + case SemconvStability.OLD: + return Object.assign(oldAttributes, options.hookAttributes); + } + + return Object.assign(oldAttributes, newAttributes, options.hookAttributes); }; /** @@ -413,29 +475,49 @@ export const setAttributesFromHttpKind = ( /** * Returns outgoing request attributes scoped to the response data * @param {IncomingMessage} response the response object - * @param {{ hostname: string }} options used to pass data needed to create attributes + * @param {SemconvStability} semconvStability determines which semconv version to use */ export const getOutgoingRequestAttributesOnResponse = ( - response: IncomingMessage + response: IncomingMessage, + semconvStability: SemconvStability ): SpanAttributes => { const { statusCode, statusMessage, httpVersion, socket } = response; - const attributes: SpanAttributes = {}; + const oldAttributes: SpanAttributes = {}; + const stableAttributes: Attributes = {}; + + if (statusCode != null) { + stableAttributes[ATTR_HTTP_RESPONSE_STATUS_CODE] = statusCode; + } + if (socket) { const { remoteAddress, remotePort } = socket; - attributes[SEMATTRS_NET_PEER_IP] = remoteAddress; - attributes[SEMATTRS_NET_PEER_PORT] = remotePort; + oldAttributes[SEMATTRS_NET_PEER_IP] = remoteAddress; + oldAttributes[SEMATTRS_NET_PEER_PORT] = remotePort; + + // Recommended + stableAttributes[ATTR_NETWORK_PEER_ADDRESS] = remoteAddress; + stableAttributes[ATTR_NETWORK_PEER_PORT] = remotePort; + stableAttributes[ATTR_NETWORK_PROTOCOL_VERSION] = response.httpVersion; } - setResponseContentLengthAttribute(response, attributes); + setResponseContentLengthAttribute(response, oldAttributes); if (statusCode) { - attributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode; - attributes[AttributeNames.HTTP_STATUS_TEXT] = ( + oldAttributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode; + oldAttributes[AttributeNames.HTTP_STATUS_TEXT] = ( statusMessage || '' ).toUpperCase(); } - setAttributesFromHttpKind(httpVersion, attributes); - return attributes; + setAttributesFromHttpKind(httpVersion, oldAttributes); + + switch (semconvStability) { + case SemconvStability.STABLE: + return stableAttributes; + case SemconvStability.OLD: + return oldAttributes; + } + + return Object.assign(oldAttributes, stableAttributes); }; /** diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts index 77d90b4fa3..047089c9d8 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts @@ -30,12 +30,28 @@ import { SimpleSpanProcessor, } from '@opentelemetry/sdk-trace-base'; import { + ATTR_HTTP_REQUEST_METHOD, + ATTR_HTTP_RESPONSE_STATUS_CODE, + ATTR_NETWORK_PEER_ADDRESS, + ATTR_NETWORK_PEER_PORT, + ATTR_NETWORK_PROTOCOL_VERSION, + ATTR_SERVER_ADDRESS, + ATTR_SERVER_PORT, + ATTR_URL_FULL, + HTTP_REQUEST_METHOD_VALUE_GET, NETTRANSPORTVALUES_IP_TCP, SEMATTRS_HTTP_CLIENT_IP, SEMATTRS_HTTP_FLAVOR, + SEMATTRS_HTTP_HOST, + SEMATTRS_HTTP_METHOD, + SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, SEMATTRS_HTTP_ROUTE, SEMATTRS_HTTP_STATUS_CODE, + SEMATTRS_HTTP_TARGET, + SEMATTRS_HTTP_URL, SEMATTRS_NET_HOST_PORT, + SEMATTRS_NET_PEER_IP, + SEMATTRS_NET_PEER_NAME, SEMATTRS_NET_PEER_PORT, SEMATTRS_NET_TRANSPORT, } from '@opentelemetry/semantic-conventions'; @@ -43,7 +59,7 @@ import * as assert from 'assert'; import * as nock from 'nock'; import * as path from 'path'; import { HttpInstrumentation } from '../../src/http'; -import { HttpInstrumentationConfig } from '../../src/types'; +import { HttpInstrumentationConfig, SemconvStability } from '../../src/types'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; import { httpRequest } from '../utils/httpRequest'; @@ -63,6 +79,7 @@ instrumentation.enable(); instrumentation.disable(); import * as http from 'http'; +import { AttributeNames } from '../../src/enums/AttributeNames'; const applyCustomAttributesOnSpanErrorMessage = 'bad applyCustomAttributesOnSpan function'; @@ -156,7 +173,7 @@ describe('HttpInstrumentation', () => { memoryExporter.reset(); }); - before(() => { + before(async () => { const config: HttpInstrumentationConfig = { ignoreIncomingPaths: [ (url: string) => { @@ -184,7 +201,7 @@ describe('HttpInstrumentation', () => { response.end('Test Server Response'); }); - server.listen(serverPort); + await new Promise(resolve => server.listen(serverPort, resolve)); }); after(() => { @@ -237,7 +254,7 @@ describe('HttpInstrumentation', () => { instrumentation.disable(); }); - it('allows to disable outgoing request instrumentation', () => { + it('allows to disable outgoing request instrumentation', async () => { server.close(); instrumentation.disable(); @@ -249,14 +266,14 @@ describe('HttpInstrumentation', () => { response.end('Test Server Response'); }); - server.listen(serverPort); + await new Promise(resolve => server.listen(serverPort, resolve)); assert.strictEqual(isWrapped(http.Server.prototype.emit), true); assert.strictEqual(isWrapped(http.get), false); assert.strictEqual(isWrapped(http.request), false); }); - it('allows to disable incoming request instrumentation', () => { + it('allows to disable incoming request instrumentation', async () => { server.close(); instrumentation.disable(); @@ -268,7 +285,7 @@ describe('HttpInstrumentation', () => { response.end('Test Server Response'); }); - server.listen(serverPort); + await new Promise(resolve => server.listen(serverPort, resolve)); assert.strictEqual(isWrapped(http.Server.prototype.emit), false); assert.strictEqual(isWrapped(http.get), true); @@ -281,7 +298,7 @@ describe('HttpInstrumentation', () => { memoryExporter.reset(); }); - before(() => { + before(async () => { instrumentation.setConfig({ ignoreIncomingPaths: [ '/ignored/string', @@ -347,7 +364,7 @@ describe('HttpInstrumentation', () => { response.end('Test Server Response'); }); - server.listen(serverPort); + await new Promise(resolve => server.listen(serverPort, resolve)); }); after(() => { @@ -1013,6 +1030,137 @@ describe('HttpInstrumentation', () => { }); }); + describe('with semconv stability set to http', () => { + beforeEach(() => { + memoryExporter.reset(); + }); + + before(async () => { + instrumentation.setConfig({}); + instrumentation['_semconvStability'] = SemconvStability.STABLE; + instrumentation.enable(); + server = http.createServer((request, response) => { + if (request.url?.includes('/premature-close')) { + response.destroy(); + return; + } + if (request.url?.includes('/hang')) { + // write response headers. + response.write(''); + // hang the request. + return; + } + if (request.url?.includes('/destroy-request')) { + // force flush http response header to trigger client response callback + response.write(''); + setTimeout(() => { + request.socket.destroy(); + }, 100); + return; + } + if (request.url?.includes('/ignored')) { + provider.getTracer('test').startSpan('some-span').end(); + } + if (request.url?.includes('/setroute')) { + const rpcData = getRPCMetadata(context.active()); + assert.ok(rpcData != null); + assert.strictEqual(rpcData.type, RPCType.HTTP); + assert.strictEqual(rpcData.route, undefined); + rpcData.route = 'TheRoute'; + } + response.end('Test Server Response'); + }); + + await new Promise(resolve => server.listen(serverPort, resolve)); + }); + + after(() => { + server.close(); + instrumentation.disable(); + }); + + it('should generate semconv 1.27 spans', async () => { + const response = await httpRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + const spans = memoryExporter.getFinishedSpans(); + const [_, outgoingSpan] = spans; + assert.strictEqual(spans.length, 2); + + // should have only required and recommended attributes for semconv 1.27 + assert.deepStrictEqual(outgoingSpan.attributes, { + [ATTR_HTTP_REQUEST_METHOD]: HTTP_REQUEST_METHOD_VALUE_GET, + [ATTR_SERVER_ADDRESS]: hostname, + [ATTR_SERVER_PORT]: serverPort, + [ATTR_URL_FULL]: `${protocol}://${hostname}:${serverPort}${pathname}`, + [ATTR_HTTP_RESPONSE_STATUS_CODE]: 200, + [ATTR_NETWORK_PEER_ADDRESS]: response.address, + [ATTR_NETWORK_PEER_PORT]: serverPort, + [ATTR_NETWORK_PROTOCOL_VERSION]: '1.1', + }); + }); + }); + + describe('with semconv stability set to http/dup', () => { + beforeEach(() => { + memoryExporter.reset(); + instrumentation.setConfig({}); + }); + + before(async () => { + instrumentation['_semconvStability'] = SemconvStability.DUPLICATE; + instrumentation.enable(); + server = http.createServer((_, response) => { + response.end('Test Server Response'); + }); + + await new Promise(resolve => server.listen(serverPort, resolve)); + }); + + after(() => { + server.close(); + instrumentation.disable(); + }); + + it('should create client spans with semconv 1.27 and old 1.7', async () => { + const response = await httpRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 2); + const outgoingSpan = spans[1]; + + // should have only required and recommended attributes for semconv 1.27 + assert.deepStrictEqual(outgoingSpan.attributes, { + // 1.27 attributes + [ATTR_HTTP_REQUEST_METHOD]: HTTP_REQUEST_METHOD_VALUE_GET, + [ATTR_SERVER_ADDRESS]: hostname, + [ATTR_SERVER_PORT]: serverPort, + [ATTR_URL_FULL]: `http://${hostname}:${serverPort}${pathname}`, + [ATTR_HTTP_RESPONSE_STATUS_CODE]: 200, + [ATTR_NETWORK_PEER_ADDRESS]: response.address, + [ATTR_NETWORK_PEER_PORT]: serverPort, + [ATTR_NETWORK_PROTOCOL_VERSION]: '1.1', + + // 1.7 attributes + [SEMATTRS_HTTP_FLAVOR]: '1.1', + [SEMATTRS_HTTP_HOST]: `${hostname}:${serverPort}`, + [SEMATTRS_HTTP_METHOD]: 'GET', + [SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED]: 20, + [SEMATTRS_HTTP_STATUS_CODE]: 200, + [SEMATTRS_HTTP_TARGET]: '/test', + [SEMATTRS_HTTP_URL]: `http://${hostname}:${serverPort}${pathname}`, + [SEMATTRS_NET_PEER_IP]: response.address, + [SEMATTRS_NET_PEER_NAME]: hostname, + [SEMATTRS_NET_PEER_PORT]: serverPort, + [SEMATTRS_NET_TRANSPORT]: 'ip_tcp', + + // unspecified old names + [AttributeNames.HTTP_STATUS_TEXT]: 'OK', + }); + }); + }); + describe('with require parent span', () => { beforeEach(done => { memoryExporter.reset(); @@ -1167,7 +1315,7 @@ describe('HttpInstrumentation', () => { memoryExporter.reset(); }); - before(() => { + before(async () => { instrumentation.setConfig({ headersToSpanAttributes: { client: { @@ -1187,7 +1335,7 @@ describe('HttpInstrumentation', () => { response.end('Test Server Response'); }); - server.listen(serverPort); + await new Promise(resolve => server.listen(serverPort, resolve)); }); after(() => { diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts index 39260fd7cc..4b64e93681 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -36,7 +36,11 @@ import { IncomingMessage, ServerResponse } from 'http'; import { Socket } from 'net'; import * as sinon from 'sinon'; import * as url from 'url'; -import { IgnoreMatcher, ParsedRequestOptions } from '../../src/types'; +import { + IgnoreMatcher, + ParsedRequestOptions, + SemconvStability, +} from '../../src/types'; import * as utils from '../../src/utils'; import { AttributeNames } from '../../src/enums/AttributeNames'; import { RPCType, setRPCMetadata } from '@opentelemetry/core'; @@ -261,8 +265,11 @@ describe('Utility', () => { { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, SpanKind.INTERNAL ); - /* tslint:disable-next-line:no-any */ - utils.setSpanWithError(span, new Error(errorMessage)); + utils.setSpanWithError( + span, + new Error(errorMessage), + SemconvStability.OLD + ); const attributes = span.attributes; assert.strictEqual( attributes[AttributeNames.HTTP_ERROR_MESSAGE], diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts b/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts index 57ca54574a..14be1f8b11 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts @@ -22,6 +22,7 @@ type GetResult = Promise<{ resHeaders: http.IncomingHttpHeaders; reqHeaders: http.OutgoingHttpHeaders; method: string | undefined; + address?: string; }>; function get(input: string | URL, options?: http.RequestOptions): GetResult; @@ -46,6 +47,7 @@ function get(input: any, options?: any): GetResult { reqHeaders: req.getHeaders ? req.getHeaders() : (req as any)._headers, resHeaders: res.headers, method: res.req.method, + address: req.socket?.remoteAddress, }); }); resp.on('error', err => { diff --git a/packages/opentelemetry-core/src/utils/environment.ts b/packages/opentelemetry-core/src/utils/environment.ts index 3540d0813d..addb6b18ce 100644 --- a/packages/opentelemetry-core/src/utils/environment.ts +++ b/packages/opentelemetry-core/src/utils/environment.ts @@ -74,6 +74,7 @@ function isEnvVarANumber(key: unknown): key is keyof ENVIRONMENT_NUMBERS { const ENVIRONMENT_LISTS_KEYS = [ 'OTEL_NO_PATCH_MODULES', 'OTEL_PROPAGATORS', + 'OTEL_SEMCONV_STABILITY_OPT_IN', ] as const; type ENVIRONMENT_LISTS = { @@ -235,6 +236,7 @@ export const DEFAULT_ENVIRONMENT: Required = { OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: 'http/protobuf', OTEL_EXPORTER_OTLP_LOGS_PROTOCOL: 'http/protobuf', OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: 'cumulative', + OTEL_SEMCONV_STABILITY_OPT_IN: [], }; /**