From 76f2e2af5af98f38d849ab2cbb313fb8647ce224 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:54:27 -0700 Subject: [PATCH 01/11] WIP --- .../package.json | 1 + .../src/http.ts | 95 ++++++++++++++----- .../src/utils.ts | 64 +++++++++++++ .../tsconfig.json | 3 + .../scripts/protos.js | 9 +- 5 files changed, 142 insertions(+), 30 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index f88f8f2a28..712998f56e 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -74,6 +74,7 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { + "@opentelemetry/api-metrics": "0.31.0", "@opentelemetry/core": "1.5.0", "@opentelemetry/instrumentation": "0.31.0", "@opentelemetry/semantic-conventions": "1.5.0", diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index c0c01b762c..70ad9f0152 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -15,6 +15,7 @@ */ import { context, + HrTime, INVALID_SPAN_CONTEXT, propagation, ROOT_CONTEXT, @@ -25,7 +26,8 @@ import { SpanStatusCode, trace, } from '@opentelemetry/api'; -import { suppressTracing } from '@opentelemetry/core'; +import { Histogram, MetricAttributes, MetricOptions, ValueType } from '@opentelemetry/api-metrics'; +import { hrTime, hrTimeDuration, hrTimeToMilliseconds, suppressTracing } from '@opentelemetry/core'; import type * as http from 'http'; import type * as https from 'https'; import { Socket } from 'net'; @@ -58,6 +60,8 @@ export class HttpInstrumentation extends InstrumentationBase { private readonly _spanNotEnded: WeakSet = new WeakSet(); private readonly _version = process.versions.node; private _headerCapture; + private _httpServerDurationHistogram: Histogram; + private _httpClientDurationHistogram: Histogram; constructor(config?: HttpInstrumentationConfig) { super( @@ -67,6 +71,20 @@ export class HttpInstrumentation extends InstrumentationBase { ); this._headerCapture = this._createHeaderCapture(); + const serverDurationMetricOptions: MetricOptions = { + description: 'measures the duration of the inbound HTTP requests', + unit: 'ms', + valueType: ValueType.DOUBLE + }; + // TODO: Metrics Semantic Conventions are not available yet + this._httpServerDurationHistogram = this.meter.createHistogram('http.server.duration', serverDurationMetricOptions); + const clientDurationMetricOptions: MetricOptions = { + description: 'measures the duration of the outbound HTTP requests', + unit: 'ms', + valueType: ValueType.DOUBLE + }; + // TODO: Metrics Semantic Conventions are not available yet + this._httpClientDurationHistogram = this.meter.createHistogram('http.client.duration', clientDurationMetricOptions); } private _getConfig(): HttpInstrumentationConfig { @@ -272,11 +290,15 @@ export class HttpInstrumentation extends InstrumentationBase { * @param request The original request object. * @param options The arguments to the original function. * @param span representing the current operation + * @param startTime representing the start time of the request to calculate duration in Metric + * @param metricAttributes metric attributes */ private _traceClientRequest( request: http.ClientRequest, hostname: string, - span: Span + span: Span, + startTime: HrTime, + metricAttributes: MetricAttributes ): http.ClientRequest { if (this._getConfig().requestHook) { this._callRequestHook(span, request); @@ -295,6 +317,8 @@ export class HttpInstrumentation extends InstrumentationBase { { hostname } ); span.setAttributes(responseAttributes); + metricAttributes = Object.assign(metricAttributes, utils.getOutgoingRequestMetricAttributesOnResponse(responseAttributes)); + if (this._getConfig().responseHook) { this._callResponseHook(span, response); } @@ -324,32 +348,32 @@ export class HttpInstrumentation extends InstrumentationBase { request, response ), - () => {}, + () => { }, true ); } - this._closeHttpSpan(span); + this._closeHttpSpan(span, SpanKind.CLIENT, startTime, metricAttributes); }); response.on('error', (error: Err) => { this._diag.debug('outgoingRequest on error()', error); utils.setSpanWithError(span, error); const code = utils.parseResponseStatus(SpanKind.CLIENT, response.statusCode); span.setStatus({ code, message: error.message }); - this._closeHttpSpan(span); + this._closeHttpSpan(span, SpanKind.CLIENT, startTime, metricAttributes); }); } ); request.on('close', () => { this._diag.debug('outgoingRequest on request close()'); if (!request.aborted) { - this._closeHttpSpan(span); + this._closeHttpSpan(span, SpanKind.CLIENT, startTime, metricAttributes); } }); request.on('error', (error: Err) => { this._diag.debug('outgoingRequest on request error()', error); utils.setSpanWithError(span, error); - this._closeHttpSpan(span); + this._closeHttpSpan(span, SpanKind.CLIENT, startTime, metricAttributes); }); this._diag.debug('http.ClientRequest return request'); @@ -405,18 +429,23 @@ export class HttpInstrumentation extends InstrumentationBase { const headers = request.headers; + const spanAttributes = utils.getIncomingRequestAttributes(request, { + component: component, + serverName: instrumentation._getConfig().serverName, + hookAttributes: instrumentation._callStartSpanHook( + request, + instrumentation._getConfig().startIncomingSpanHook + ), + }); + const spanOptions: SpanOptions = { kind: SpanKind.SERVER, - attributes: utils.getIncomingRequestAttributes(request, { - component: component, - serverName: instrumentation._getConfig().serverName, - hookAttributes: instrumentation._callStartSpanHook( - request, - instrumentation._getConfig().startIncomingSpanHook - ), - }), + attributes: spanAttributes, }; + const startTime = hrTime(); + let metricAttributes: MetricAttributes = utils.getIncomingRequestMetricAttributes(spanAttributes, { component: component }); + const ctx = propagation.extract(ROOT_CONTEXT, headers); const span = instrumentation._startHttpSpan( `${component.toLocaleUpperCase()} ${method}`, @@ -457,7 +486,7 @@ export class HttpInstrumentation extends InstrumentationBase { error => { if (error) { utils.setSpanWithError(span, error); - instrumentation._closeHttpSpan(span); + instrumentation._closeHttpSpan(span, SpanKind.SERVER, startTime, metricAttributes); throw error; } } @@ -467,6 +496,7 @@ export class HttpInstrumentation extends InstrumentationBase { request, response ); + metricAttributes = Object.assign(metricAttributes, utils.getIncomingRequestMetricAttributesOnResponse(attributes)); instrumentation._headerCapture.server.captureResponseHeaders(span, header => response.getHeader(header)); @@ -482,12 +512,12 @@ export class HttpInstrumentation extends InstrumentationBase { request, response ), - () => {}, + () => { }, true ); } - instrumentation._closeHttpSpan(span); + instrumentation._closeHttpSpan(span, SpanKind.SERVER, startTime, metricAttributes); return returned; }; @@ -496,7 +526,7 @@ export class HttpInstrumentation extends InstrumentationBase { error => { if (error) { utils.setSpanWithError(span, error); - instrumentation._closeHttpSpan(span); + instrumentation._closeHttpSpan(span, SpanKind.SERVER, startTime, metricAttributes); throw error; } } @@ -521,7 +551,7 @@ export class HttpInstrumentation extends InstrumentationBase { } const extraOptions = typeof args[0] === 'object' && - (typeof options === 'string' || options instanceof url.URL) + (typeof options === 'string' || options instanceof url.URL) ? (args.shift() as http.RequestOptions) : undefined; const { origin, pathname, method, optionsParsed } = utils.getRequestInfo( @@ -575,6 +605,9 @@ export class HttpInstrumentation extends InstrumentationBase { ), }); + const startTime = hrTime(); + const metricAttributes: MetricAttributes = utils.getOutgoingRequestMetricAttributes(attributes); + const spanOptions: SpanOptions = { kind: SpanKind.CLIENT, attributes, @@ -604,7 +637,7 @@ export class HttpInstrumentation extends InstrumentationBase { error => { if (error) { utils.setSpanWithError(span, error); - instrumentation._closeHttpSpan(span); + instrumentation._closeHttpSpan(span, SpanKind.CLIENT, startTime, metricAttributes); throw error; } } @@ -615,7 +648,9 @@ export class HttpInstrumentation extends InstrumentationBase { return instrumentation._traceClientRequest( request, hostname, - span + span, + startTime, + metricAttributes ); }); }; @@ -649,13 +684,21 @@ export class HttpInstrumentation extends InstrumentationBase { return span; } - private _closeHttpSpan(span: Span) { + private _closeHttpSpan(span: Span, spanKind: SpanKind, startTime: HrTime, metricAttributes: MetricAttributes) { if (!this._spanNotEnded.has(span)) { return; } span.end(); this._spanNotEnded.delete(span); + + // Record metrics + const duration = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())); + if (spanKind == SpanKind.SERVER) { + this._httpServerDurationHistogram.record(duration, metricAttributes); + } else if (spanKind == SpanKind.CLIENT) { + this._httpClientDurationHistogram.record(duration, metricAttributes); + } } private _callResponseHook( @@ -664,7 +707,7 @@ export class HttpInstrumentation extends InstrumentationBase { ) { safeExecuteInTheMiddle( () => this._getConfig().responseHook!(span, response), - () => {}, + () => { }, true ); } @@ -675,7 +718,7 @@ export class HttpInstrumentation extends InstrumentationBase { ) { safeExecuteInTheMiddle( () => this._getConfig().requestHook!(span, request), - () => {}, + () => { }, true ); } @@ -684,7 +727,7 @@ export class HttpInstrumentation extends InstrumentationBase { request: http.IncomingMessage | http.RequestOptions, hookFunc: Function | undefined, ) { - if(typeof hookFunc === 'function'){ + if (typeof hookFunc === 'function') { return safeExecuteInTheMiddle( () => hookFunc(request), () => { }, diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts index d21dd105a9..5a4f4d6f93 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -35,6 +35,7 @@ import { getRPCMetadata, RPCType } from '@opentelemetry/core'; import * as url from 'url'; import { AttributeNames } from './enums/AttributeNames'; import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; +import { MetricAttributes } from '@opentelemetry/api-metrics'; /** * Get an absolute url @@ -330,6 +331,22 @@ export const getOutgoingRequestAttributes = ( return Object.assign(attributes, options.hookAttributes); }; +/** + * Returns outgoing request Metric attributes scoped to the request data + * @param {SpanAttributes} spanAttributes the span attributes + */ +export const getOutgoingRequestMetricAttributes = ( + spanAttributes: SpanAttributes +): MetricAttributes => { + const metricAttributes: MetricAttributes = {}; + metricAttributes[SemanticAttributes.HTTP_METHOD] = spanAttributes[SemanticAttributes.HTTP_METHOD]; + metricAttributes[SemanticAttributes.NET_PEER_NAME] = spanAttributes[SemanticAttributes.NET_PEER_NAME]; + // TODO: If configured on + metricAttributes[SemanticAttributes.HTTP_TARGET] = spanAttributes[SemanticAttributes.HTTP_TARGET]; + metricAttributes[SemanticAttributes.HTTP_URL] = spanAttributes[SemanticAttributes.HTTP_URL]; + return metricAttributes; +}; + /** * Returns attributes related to the kind of HTTP protocol used * @param {string} [kind] Kind of HTTP protocol used: "1.0", "1.1", "2", "SPDY" or "QUIC". @@ -376,6 +393,20 @@ export const getOutgoingRequestAttributesOnResponse = ( return Object.assign(attributes, httpKindAttributes); }; +/** + * Returns outgoing request Metric attributes scoped to the response data + * @param {SpanAttributes} spanAttributes the span attributes + */ +export const getOutgoingRequestMetricAttributesOnResponse = ( + spanAttributes: SpanAttributes +): MetricAttributes => { + const metricAttributes: MetricAttributes = {}; + metricAttributes[SemanticAttributes.NET_PEER_PORT] = spanAttributes[SemanticAttributes.NET_PEER_PORT]; + metricAttributes[SemanticAttributes.HTTP_STATUS_CODE] = spanAttributes[SemanticAttributes.HTTP_STATUS_CODE]; + metricAttributes[SemanticAttributes.HTTP_FLAVOR] = spanAttributes[SemanticAttributes.HTTP_FLAVOR]; + return metricAttributes; +}; + /** * Returns incoming request attributes scoped to the request data * @param {IncomingMessage} request the request object @@ -429,6 +460,26 @@ export const getIncomingRequestAttributes = ( return Object.assign(attributes, httpKindAttributes, options.hookAttributes); }; +/** + * Returns incoming request Metric attributes scoped to the request data + * @param {SpanAttributes} spanAttributes the span attributes + * @param {{ component: string }} options used to pass data needed to create attributes + */ +export const getIncomingRequestMetricAttributes = ( + spanAttributes: SpanAttributes, + options: { component: string } +): MetricAttributes => { + const metricAttributes: MetricAttributes = {}; + metricAttributes[SemanticAttributes.HTTP_SCHEME] = options.component; + metricAttributes[SemanticAttributes.HTTP_METHOD] = spanAttributes[SemanticAttributes.HTTP_METHOD]; + metricAttributes[SemanticAttributes.NET_HOST_NAME] = spanAttributes[SemanticAttributes.NET_HOST_NAME]; + metricAttributes[SemanticAttributes.HTTP_FLAVOR] = spanAttributes[SemanticAttributes.HTTP_FLAVOR]; + // TODO: If configured on + metricAttributes[SemanticAttributes.HTTP_TARGET] = spanAttributes[SemanticAttributes.HTTP_TARGET]; + metricAttributes[SemanticAttributes.HTTP_URL] = spanAttributes[SemanticAttributes.HTTP_URL]; + return metricAttributes; +}; + /** * Returns incoming request attributes scoped to the response data * @param {(ServerResponse & { socket: Socket; })} response the response object @@ -459,6 +510,19 @@ export const getIncomingRequestAttributesOnResponse = ( return attributes; }; +/** + * Returns incoming request Metric attributes scoped to the request data + * @param {SpanAttributes} spanAttributes the span attributes + */ +export const getIncomingRequestMetricAttributesOnResponse = ( + spanAttributes: SpanAttributes +): MetricAttributes => { + const metricAttributes: MetricAttributes = {}; + metricAttributes[SemanticAttributes.HTTP_STATUS_CODE] = spanAttributes[SemanticAttributes.HTTP_STATUS_CODE]; + metricAttributes[SemanticAttributes.NET_HOST_PORT] = spanAttributes[SemanticAttributes.NET_HOST_PORT]; + return metricAttributes; +}; + export function headerCapture(type: 'request' | 'response', headers: string[]) { const normalizedHeaders = new Map(headers.map(header => [header.toLowerCase(), header.toLowerCase().replace(/-/g, '_')])); diff --git a/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json b/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json index b883031b0f..8b4ba562f1 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json +++ b/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json @@ -24,6 +24,9 @@ { "path": "../../../packages/opentelemetry-semantic-conventions" }, + { + "path": "../opentelemetry-api-metrics" + }, { "path": "../opentelemetry-instrumentation" } diff --git a/experimental/packages/otlp-proto-exporter-base/scripts/protos.js b/experimental/packages/otlp-proto-exporter-base/scripts/protos.js index c5ab118f18..c3c8aa316b 100644 --- a/experimental/packages/otlp-proto-exporter-base/scripts/protos.js +++ b/experimental/packages/otlp-proto-exporter-base/scripts/protos.js @@ -18,7 +18,8 @@ const protos = [ function exec(command, argv) { return new Promise((resolve, reject) => { - const child = cp.spawn(process.execPath, [command, ...argv], { + const child = cp.spawn(command, argv, { + shell: true, stdio: ['ignore', 'inherit', 'inherit'], }); child.on('exit', (code, signal) => { @@ -27,7 +28,7 @@ function exec(command, argv) { return; } resolve(); - }) + }); }); } @@ -35,7 +36,7 @@ function pbts(pbjsOutFile) { const pbtsPath = path.resolve(__dirname, '../node_modules/.bin/pbts'); const pbtsOptions = [ '-o', path.join(generatedPath, 'root.d.ts'), - ] + ]; return exec(pbtsPath, [...pbtsOptions, pbjsOutFile]); } @@ -55,4 +56,4 @@ async function pbjs(files) { (async function main() { const pbjsOut = await pbjs(protos); await pbts(pbjsOut); -})(); +})(); \ No newline at end of file From ef264fda472c47ee5d96084d14054d7f82ebc2a3 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:23:51 -0700 Subject: [PATCH 02/11] WIP --- .../package.json | 4 +- .../src/http.ts | 28 +++-- .../src/utils.ts | 4 - .../test/functionals/http-metrics.test.ts | 106 ++++++++++++++++++ 4 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index 712998f56e..e68642f321 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -9,14 +9,13 @@ "prepublishOnly": "npm run compile", "compile": "tsc --build", "clean": "tsc --build --clean", - "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", + "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts ", "tdd": "npm run test -- --watch-extensions ts --watch", "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../../", "version": "node ../../../scripts/version-update.js", "watch": "tsc --build --watch", - "precompile": "lerna run version --scope $(npm pkg get name) --include-dependencies", "prewatch": "node ../../../scripts/version-update.js", "peer-api-check": "node ../../../scripts/peer-api-check.js" }, @@ -47,6 +46,7 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/context-async-hooks": "1.5.0", + "@opentelemetry/sdk-metrics-base": "0.31.0", "@opentelemetry/sdk-trace-base": "1.5.0", "@opentelemetry/sdk-trace-node": "1.5.0", "@types/got": "9.6.12", diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 70ad9f0152..86abee2698 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -26,7 +26,7 @@ import { SpanStatusCode, trace, } from '@opentelemetry/api'; -import { Histogram, MetricAttributes, MetricOptions, ValueType } from '@opentelemetry/api-metrics'; +import { Histogram, MeterProvider, MetricAttributes, ValueType } from '@opentelemetry/api-metrics'; import { hrTime, hrTimeDuration, hrTimeToMilliseconds, suppressTracing } from '@opentelemetry/core'; import type * as http from 'http'; import type * as https from 'https'; @@ -60,8 +60,8 @@ export class HttpInstrumentation extends InstrumentationBase { private readonly _spanNotEnded: WeakSet = new WeakSet(); private readonly _version = process.versions.node; private _headerCapture; - private _httpServerDurationHistogram: Histogram; - private _httpClientDurationHistogram: Histogram; + private _httpServerDurationHistogram!: Histogram; + private _httpClientDurationHistogram!: Histogram; constructor(config?: HttpInstrumentationConfig) { super( @@ -69,22 +69,26 @@ export class HttpInstrumentation extends InstrumentationBase { VERSION, config ); - this._headerCapture = this._createHeaderCapture(); - const serverDurationMetricOptions: MetricOptions = { + this._updateMetricInstruments(); + } + + override setMeterProvider(meterProvider: MeterProvider) { + super.setMeterProvider(meterProvider); + this._updateMetricInstruments(); + } + + private _updateMetricInstruments() { + this._httpServerDurationHistogram = this.meter.createHistogram('http.server.duration', { description: 'measures the duration of the inbound HTTP requests', unit: 'ms', valueType: ValueType.DOUBLE - }; - // TODO: Metrics Semantic Conventions are not available yet - this._httpServerDurationHistogram = this.meter.createHistogram('http.server.duration', serverDurationMetricOptions); - const clientDurationMetricOptions: MetricOptions = { + }); + this._httpClientDurationHistogram = this.meter.createHistogram('http.client.duration', { description: 'measures the duration of the outbound HTTP requests', unit: 'ms', valueType: ValueType.DOUBLE - }; - // TODO: Metrics Semantic Conventions are not available yet - this._httpClientDurationHistogram = this.meter.createHistogram('http.client.duration', clientDurationMetricOptions); + }); } private _getConfig(): HttpInstrumentationConfig { diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts index 5a4f4d6f93..f3140e016b 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -341,8 +341,6 @@ export const getOutgoingRequestMetricAttributes = ( const metricAttributes: MetricAttributes = {}; metricAttributes[SemanticAttributes.HTTP_METHOD] = spanAttributes[SemanticAttributes.HTTP_METHOD]; metricAttributes[SemanticAttributes.NET_PEER_NAME] = spanAttributes[SemanticAttributes.NET_PEER_NAME]; - // TODO: If configured on - metricAttributes[SemanticAttributes.HTTP_TARGET] = spanAttributes[SemanticAttributes.HTTP_TARGET]; metricAttributes[SemanticAttributes.HTTP_URL] = spanAttributes[SemanticAttributes.HTTP_URL]; return metricAttributes; }; @@ -474,9 +472,7 @@ export const getIncomingRequestMetricAttributes = ( metricAttributes[SemanticAttributes.HTTP_METHOD] = spanAttributes[SemanticAttributes.HTTP_METHOD]; metricAttributes[SemanticAttributes.NET_HOST_NAME] = spanAttributes[SemanticAttributes.NET_HOST_NAME]; metricAttributes[SemanticAttributes.HTTP_FLAVOR] = spanAttributes[SemanticAttributes.HTTP_FLAVOR]; - // TODO: If configured on metricAttributes[SemanticAttributes.HTTP_TARGET] = spanAttributes[SemanticAttributes.HTTP_TARGET]; - metricAttributes[SemanticAttributes.HTTP_URL] = spanAttributes[SemanticAttributes.HTTP_URL]; return metricAttributes; }; diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts new file mode 100644 index 0000000000..a7a52aa9b2 --- /dev/null +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts @@ -0,0 +1,106 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + AggregationTemporality, + DataPointType, + InMemoryMetricExporter, + MeterProvider, + PeriodicExportingMetricReader, +} from "@opentelemetry/sdk-metrics-base"; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import { HttpInstrumentation } from '../../src/http'; +import { httpRequest } from '../utils/httpRequest'; + +const instrumentation = new HttpInstrumentation(); +instrumentation.enable(); +instrumentation.disable(); + +import * as http from 'http'; + +let server: http.Server; +const serverPort = 22346; +const protocol = 'http'; +const hostname = 'localhost'; +const pathname = '/test'; +const tracerProvider = new NodeTracerProvider(); +const meterProvider = new MeterProvider(); +const metricsMemoryExporter = new InMemoryMetricExporter(AggregationTemporality.DELTA); +const metricReader = new PeriodicExportingMetricReader({ exporter: metricsMemoryExporter, exportIntervalMillis: 100 }); +meterProvider.addMetricReader(metricReader); +instrumentation.setTracerProvider(tracerProvider); +instrumentation.setMeterProvider(meterProvider); + + +describe('metrics', () => { + beforeEach(() => { + metricsMemoryExporter.reset(); + }); + + before(() => { + instrumentation.enable(); + server = http.createServer((request, response) => { + response.end('Test Server Response'); + }); + server.listen(serverPort); + }); + + after(() => { + server.close(); + instrumentation.disable(); + }); + + it('should add server/client duration metrics', async () => { + let requestCount = 3; + for (let i = 0; i < requestCount; i++) { + await httpRequest.get(`${protocol}://${hostname}:${serverPort}${pathname}`); + } + await new Promise(resolve => setTimeout(resolve, 120)); + const resourceMetrics = metricsMemoryExporter.getMetrics(); + assert.strictEqual(resourceMetrics.length, 1, "resourceMetrics count"); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count"); + const metrics = scopeMetrics[0].metrics; + assert.strictEqual(metrics.length, 2, "metrics count"); + assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual(metrics[0].descriptor.description, "measures the duration of the inbound HTTP requests"); + assert.strictEqual(metrics[0].descriptor.name, "http.server.duration"); + assert.strictEqual(metrics[0].descriptor.unit, "ms"); + assert.strictEqual(metrics[0].dataPoints.length, 1); + assert.strictEqual((metrics[0].dataPoints[0].value as any).count, requestCount); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_SCHEME], "http"); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], "GET"); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], "1.1"); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_NAME], "localhost"); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_TARGET], "/test"); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE], 200); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_PORT], 22346); + + assert.strictEqual(metrics[1].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual(metrics[1].descriptor.description, "measures the duration of the outbound HTTP requests"); + assert.strictEqual(metrics[1].descriptor.name, "http.client.duration"); + assert.strictEqual(metrics[1].descriptor.unit, "ms"); + assert.strictEqual(metrics[1].dataPoints.length, 1); + assert.strictEqual((metrics[1].dataPoints[0].value as any).count, requestCount); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], "GET"); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_NAME], "localhost"); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_URL], "http://localhost:22346/test"); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_PORT], 22346); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE], 200); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], "1.1"); + }); +}); From cdcc93ce31daf8db7326190f54e8f963b9fc0679 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:29:25 -0700 Subject: [PATCH 03/11] Update --- .../packages/opentelemetry-instrumentation-http/package.json | 3 ++- .../packages/otlp-proto-exporter-base/scripts/protos.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index e68642f321..21904269d0 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -9,13 +9,14 @@ "prepublishOnly": "npm run compile", "compile": "tsc --build", "clean": "tsc --build --clean", - "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts ", + "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", "tdd": "npm run test -- --watch-extensions ts --watch", "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../../", "version": "node ../../../scripts/version-update.js", "watch": "tsc --build --watch", + "precompile": "lerna run version --scope $(npm pkg get name) --include-dependencies", "prewatch": "node ../../../scripts/version-update.js", "peer-api-check": "node ../../../scripts/peer-api-check.js" }, diff --git a/experimental/packages/otlp-proto-exporter-base/scripts/protos.js b/experimental/packages/otlp-proto-exporter-base/scripts/protos.js index c3c8aa316b..e06cc9db44 100644 --- a/experimental/packages/otlp-proto-exporter-base/scripts/protos.js +++ b/experimental/packages/otlp-proto-exporter-base/scripts/protos.js @@ -56,4 +56,4 @@ async function pbjs(files) { (async function main() { const pbjsOut = await pbjs(protos); await pbts(pbjsOut); -})(); \ No newline at end of file +})(); From 87e198a932954b579de12d6f77ef24aea25fa3d4 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:42:45 -0700 Subject: [PATCH 04/11] Lint --- experimental/CHANGELOG.md | 1 + .../src/http.ts | 4 +- .../test/functionals/http-metrics.test.ts | 40 +++++++++---------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 95be6b4e3d..21f285dc0e 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) +* feature(instrumentation-http): Add HTTP Server and Client duration Metrics in HTTP Node.js Instrumentation [#](https://github.com/open-telemetry/opentelemetry-js/pull/) @hectorhdzg * feature(prometheus-serialiser): export the unit block when unit is set in metric descriptor [#3066](https://github.com/open-telemetry/opentelemetry-js/pull/3041) @weyert ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index a3e865bb36..66bcaa12c1 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -695,9 +695,9 @@ export class HttpInstrumentation extends InstrumentationBase { // Record metrics const duration = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())); - if (spanKind == SpanKind.SERVER) { + if (spanKind === SpanKind.SERVER) { this._httpServerDurationHistogram.record(duration, metricAttributes); - } else if (spanKind == SpanKind.CLIENT) { + } else if (spanKind === SpanKind.CLIENT) { this._httpClientDurationHistogram.record(duration, metricAttributes); } } diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts index a7a52aa9b2..e62843b146 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts @@ -19,7 +19,7 @@ import { InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader, -} from "@opentelemetry/sdk-metrics-base"; +} from '@opentelemetry/sdk-metrics-base'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; @@ -65,42 +65,42 @@ describe('metrics', () => { }); it('should add server/client duration metrics', async () => { - let requestCount = 3; + const requestCount = 3; for (let i = 0; i < requestCount; i++) { await httpRequest.get(`${protocol}://${hostname}:${serverPort}${pathname}`); } await new Promise(resolve => setTimeout(resolve, 120)); const resourceMetrics = metricsMemoryExporter.getMetrics(); - assert.strictEqual(resourceMetrics.length, 1, "resourceMetrics count"); + assert.strictEqual(resourceMetrics.length, 1, 'resourceMetrics count'); const scopeMetrics = resourceMetrics[0].scopeMetrics; - assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count"); + assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); const metrics = scopeMetrics[0].metrics; - assert.strictEqual(metrics.length, 2, "metrics count"); + assert.strictEqual(metrics.length, 2, 'metrics count'); assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); - assert.strictEqual(metrics[0].descriptor.description, "measures the duration of the inbound HTTP requests"); - assert.strictEqual(metrics[0].descriptor.name, "http.server.duration"); - assert.strictEqual(metrics[0].descriptor.unit, "ms"); + assert.strictEqual(metrics[0].descriptor.description, 'measures the duration of the inbound HTTP requests'); + assert.strictEqual(metrics[0].descriptor.name, 'http.server.duration'); + assert.strictEqual(metrics[0].descriptor.unit, 'ms'); assert.strictEqual(metrics[0].dataPoints.length, 1); assert.strictEqual((metrics[0].dataPoints[0].value as any).count, requestCount); - assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_SCHEME], "http"); - assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], "GET"); - assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], "1.1"); - assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_NAME], "localhost"); - assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_TARGET], "/test"); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_SCHEME], 'http'); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], 'GET'); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], '1.1'); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_NAME], 'localhost'); + assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_TARGET], '/test'); assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE], 200); assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_PORT], 22346); assert.strictEqual(metrics[1].dataPointType, DataPointType.HISTOGRAM); - assert.strictEqual(metrics[1].descriptor.description, "measures the duration of the outbound HTTP requests"); - assert.strictEqual(metrics[1].descriptor.name, "http.client.duration"); - assert.strictEqual(metrics[1].descriptor.unit, "ms"); + assert.strictEqual(metrics[1].descriptor.description, 'measures the duration of the outbound HTTP requests'); + assert.strictEqual(metrics[1].descriptor.name, 'http.client.duration'); + assert.strictEqual(metrics[1].descriptor.unit, 'ms'); assert.strictEqual(metrics[1].dataPoints.length, 1); assert.strictEqual((metrics[1].dataPoints[0].value as any).count, requestCount); - assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], "GET"); - assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_NAME], "localhost"); - assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_URL], "http://localhost:22346/test"); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], 'GET'); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_NAME], 'localhost'); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_URL], 'http://localhost:22346/test'); assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_PORT], 22346); assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE], 200); - assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], "1.1"); + assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], '1.1'); }); }); From 61dadc42479c9bee2d5303df828d66005a2c8e03 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:47:31 -0700 Subject: [PATCH 05/11] Updating changelog --- experimental/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 21f285dc0e..df83cb2849 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) -* feature(instrumentation-http): Add HTTP Server and Client duration Metrics in HTTP Node.js Instrumentation [#](https://github.com/open-telemetry/opentelemetry-js/pull/) @hectorhdzg +* feature(instrumentation-http): Add HTTP Server and Client duration Metrics in HTTP Node.js Instrumentation [#3149](https://github.com/open-telemetry/opentelemetry-js/pull/3149) @hectorhdzg * feature(prometheus-serialiser): export the unit block when unit is set in metric descriptor [#3066](https://github.com/open-telemetry/opentelemetry-js/pull/3041) @weyert ### :bug: (Bug Fix) From 948346585b30bf21daa71bfc07aa9f73f295daf4 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:18:38 -0700 Subject: [PATCH 06/11] Test --- .../test/functionals/http-metrics.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts index e62843b146..47d7953398 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts @@ -71,7 +71,6 @@ describe('metrics', () => { } await new Promise(resolve => setTimeout(resolve, 120)); const resourceMetrics = metricsMemoryExporter.getMetrics(); - assert.strictEqual(resourceMetrics.length, 1, 'resourceMetrics count'); const scopeMetrics = resourceMetrics[0].scopeMetrics; assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); const metrics = scopeMetrics[0].metrics; From bbf08caba36466edfa40247f261efb6183b90baa Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Mon, 15 Aug 2022 13:52:33 -0700 Subject: [PATCH 07/11] Adding HTTP SCHEME in Server Span --- .../opentelemetry-instrumentation-http/src/http.ts | 2 +- .../src/utils.ts | 14 +++++++------- .../test/functionals/http-metrics.test.ts | 2 -- .../test/utils/assertSpan.ts | 11 ++++++++--- .../tsconfig.json | 3 +++ 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 66bcaa12c1..a0e07a1e9f 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -447,7 +447,7 @@ export class HttpInstrumentation extends InstrumentationBase { }; const startTime = hrTime(); - let metricAttributes: MetricAttributes = utils.getIncomingRequestMetricAttributes(spanAttributes, { component: component }); + let metricAttributes: MetricAttributes = utils.getIncomingRequestMetricAttributes(spanAttributes); const ctx = propagation.extract(ROOT_CONTEXT, headers); const span = instrumentation._startHttpSpan( diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts index 5b39b5a400..e9e9b45468 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -300,7 +300,7 @@ export const extractHostnameAndPort = ( requestOptions: Pick ): { hostname: string, port: number | string } => { if (requestOptions.hostname && requestOptions.port) { - return {hostname: requestOptions.hostname, port: requestOptions.port}; + return { hostname: requestOptions.hostname, port: requestOptions.port }; } const matches = requestOptions.host?.match(/^([^:/ ]+)(:\d{1,5})?/) || null; const hostname = requestOptions.hostname || (matches === null ? 'localhost' : matches[1]); @@ -313,7 +313,7 @@ export const extractHostnameAndPort = ( port = requestOptions.protocol === 'https:' ? '443' : '80'; } } - return {hostname, port}; + return { hostname, port }; }; /** @@ -359,7 +359,7 @@ export const getOutgoingRequestMetricAttributes = ( const metricAttributes: MetricAttributes = {}; metricAttributes[SemanticAttributes.HTTP_METHOD] = spanAttributes[SemanticAttributes.HTTP_METHOD]; metricAttributes[SemanticAttributes.NET_PEER_NAME] = spanAttributes[SemanticAttributes.NET_PEER_NAME]; - metricAttributes[SemanticAttributes.HTTP_URL] = spanAttributes[SemanticAttributes.HTTP_URL]; + //TODO: http.url attribute, it should susbtitute any parameters to avoid high cardinality. return metricAttributes; }; @@ -451,6 +451,7 @@ export const getIncomingRequestAttributes = ( [SemanticAttributes.HTTP_HOST]: host, [SemanticAttributes.NET_HOST_NAME]: hostname, [SemanticAttributes.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_SCHEME]: options.component, }; if (typeof ips === 'string') { @@ -480,15 +481,14 @@ export const getIncomingRequestAttributes = ( * @param {{ component: string }} options used to pass data needed to create attributes */ export const getIncomingRequestMetricAttributes = ( - spanAttributes: SpanAttributes, - options: { component: string } + spanAttributes: SpanAttributes ): MetricAttributes => { const metricAttributes: MetricAttributes = {}; - metricAttributes[SemanticAttributes.HTTP_SCHEME] = options.component; + metricAttributes[SemanticAttributes.HTTP_SCHEME] = spanAttributes[SemanticAttributes.HTTP_SCHEME]; metricAttributes[SemanticAttributes.HTTP_METHOD] = spanAttributes[SemanticAttributes.HTTP_METHOD]; metricAttributes[SemanticAttributes.NET_HOST_NAME] = spanAttributes[SemanticAttributes.NET_HOST_NAME]; metricAttributes[SemanticAttributes.HTTP_FLAVOR] = spanAttributes[SemanticAttributes.HTTP_FLAVOR]; - metricAttributes[SemanticAttributes.HTTP_TARGET] = spanAttributes[SemanticAttributes.HTTP_TARGET]; + //TODO: http.target attribute, it should susbtitute any parameters to avoid high cardinality. return metricAttributes; }; diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts index 47d7953398..dedf9ae4ff 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts @@ -85,7 +85,6 @@ describe('metrics', () => { assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], 'GET'); assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], '1.1'); assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_NAME], 'localhost'); - assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_TARGET], '/test'); assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE], 200); assert.strictEqual(metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_PORT], 22346); @@ -97,7 +96,6 @@ describe('metrics', () => { assert.strictEqual((metrics[1].dataPoints[0].value as any).count, requestCount); assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD], 'GET'); assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_NAME], 'localhost'); - assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_URL], 'http://localhost:22346/test'); assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_PORT], 22346); assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE], 200); assert.strictEqual(metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR], '1.1'); diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts b/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts index db0d0d1ea0..3d8ee843c8 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts @@ -114,7 +114,7 @@ export const assertSpan = ( } else { assert.strictEqual( span.attributes[ - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -125,7 +125,7 @@ export const assertSpan = ( validations.hostname, 'must be consistent (PEER_NAME and hostname)' ); - if(!validations.noNetPeer) { + if (!validations.noNetPeer) { assert.ok( span.attributes[SemanticAttributes.NET_PEER_IP], 'must have PEER_IP' @@ -157,7 +157,7 @@ export const assertSpan = ( } else { assert.strictEqual( span.attributes[ - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -178,6 +178,11 @@ export const assertSpan = ( 'must have HOST_IP' ); } + assert.strictEqual( + span.attributes[SemanticAttributes.HTTP_SCHEME], + validations.component, + ' must have http.scheme attribute' + ); assert.ok(typeof span.parentSpanId === 'string'); assert.ok(isValidSpanId(span.parentSpanId)); } else if (validations.reqHeaders) { diff --git a/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json b/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json index 8b4ba562f1..56df6dd1b8 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json +++ b/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json @@ -29,6 +29,9 @@ }, { "path": "../opentelemetry-instrumentation" + }, + { + "path": "../opentelemetry-sdk-metrics-base" } ] } From fdd6f583d37db8412c55c2e85208fa6c07100239 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:01:41 -0700 Subject: [PATCH 08/11] Lint --- .../test/utils/assertSpan.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts b/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts index 3d8ee843c8..3b9b1a402d 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts @@ -114,7 +114,7 @@ export const assertSpan = ( } else { assert.strictEqual( span.attributes[ - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -157,7 +157,7 @@ export const assertSpan = ( } else { assert.strictEqual( span.attributes[ - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); From 393371f1c19e203afb44186f72b0de9e0d5aefc7 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 17 Aug 2022 09:30:36 -0700 Subject: [PATCH 09/11] Update metrics sdk package name --- .../packages/opentelemetry-instrumentation-http/package.json | 2 +- .../test/functionals/http-metrics.test.ts | 2 +- .../packages/opentelemetry-instrumentation-http/tsconfig.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index d4a6f577a9..d39ddf0832 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -47,7 +47,7 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/context-async-hooks": "1.5.0", - "@opentelemetry/sdk-metrics-base": "0.31.0", + "@opentelemetry/sdk-metrics": "0.31.0", "@opentelemetry/sdk-trace-base": "1.5.0", "@opentelemetry/sdk-trace-node": "1.5.0", "@types/got": "9.6.12", diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts index dedf9ae4ff..2b8630e399 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts @@ -19,7 +19,7 @@ import { InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader, -} from '@opentelemetry/sdk-metrics-base'; +} from '@opentelemetry/sdk-metrics'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; diff --git a/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json b/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json index 56df6dd1b8..034c613b3e 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json +++ b/experimental/packages/opentelemetry-instrumentation-http/tsconfig.json @@ -31,7 +31,7 @@ "path": "../opentelemetry-instrumentation" }, { - "path": "../opentelemetry-sdk-metrics-base" + "path": "../opentelemetry-sdk-metrics" } ] } From cfe93ae00974510e82a9bfba8492638143de656a Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 17 Aug 2022 09:52:31 -0700 Subject: [PATCH 10/11] Test --- .../test/functionals/http-metrics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts index 2b8630e399..5ab046b45f 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts @@ -69,7 +69,7 @@ describe('metrics', () => { for (let i = 0; i < requestCount; i++) { await httpRequest.get(`${protocol}://${hostname}:${serverPort}${pathname}`); } - await new Promise(resolve => setTimeout(resolve, 120)); + await new Promise(resolve => setTimeout(resolve, 300)); const resourceMetrics = metricsMemoryExporter.getMetrics(); const scopeMetrics = resourceMetrics[0].scopeMetrics; assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); From 8f622db2f6b057b0fe0400716281776a9fe566d6 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:29:26 -0700 Subject: [PATCH 11/11] Add dependencies --- .../packages/opentelemetry-instrumentation-http/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index a248b031ea..754414cf9f 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -74,8 +74,10 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { + "@opentelemetry/api-metrics": "0.32.0", "@opentelemetry/core": "1.6.0", "@opentelemetry/instrumentation": "0.32.0", + "@opentelemetry/sdk-metrics": "0.32.0", "@opentelemetry/semantic-conventions": "1.6.0", "semver": "^7.3.5" },