diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ae14870199..65903a9f2d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,9 +24,12 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :bug: (Bug Fix) * fix(core): added falsy check to make otel core work with browser where webpack config had process as false or null [#3613](/~https://github.com/open-telemetry/opentelemetry-js/issues/3613) @ravindra-dyte +* fix(instrumentation-http): include query params in http.target [#3646](/~https://github.com/open-telemetry/opentelemetry-js/pull/3646) @kobi-co ### :books: (Refine Doc) +* chore: update http example [#3651](/~https://github.com/open-telemetry/opentelemetry-js/pull/3651) @JamieDanielson + ### :house: (Internal) * fix(sdk-metrics): fix flaky LastValueAggregator test by using fake timer [#3587](/~https://github.com/open-telemetry/opentelemetry-js/pull/3587) @pichlermarc diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index cc96483c766..31697a308ca 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -7,6 +7,8 @@ All notable changes to this project will be documented in this file. ### :bug: (Bug Fix) * fix(metrics): export `MetricsAPI` type [#3535](/~https://github.com/open-telemetry/opentelemetry-js/pull/3535) +* fix(api): rename `LoggerOptions` to `DiagLoggerOptions` [#3641](/~https://github.com/open-telemetry/opentelemetry-js/pull/3641) +* fix(api): export `DiagLoggerOptions` type [#3639](/~https://github.com/open-telemetry/opentelemetry-js/pull/3639) ## 1.4.0 diff --git a/api/src/diag/types.ts b/api/src/diag/types.ts index e861d430c1f..588b525ec65 100644 --- a/api/src/diag/types.ts +++ b/api/src/diag/types.ts @@ -97,7 +97,7 @@ export interface ComponentLoggerOptions { namespace: string; } -export interface LoggerOptions { +export interface DiagLoggerOptions { /** * The {@link DiagLogLevel} used to filter logs sent to the logger. * @@ -117,10 +117,10 @@ export interface DiagLoggerApi { * If a global diag logger is already set, this will override it. * * @param logger - The {@link DiagLogger} instance to set as the default logger. - * @param options - A {@link LoggerOptions} object. If not provided, default values will be set. + * @param options - A {@link DiagLoggerOptions} object. If not provided, default values will be set. * @returns `true` if the logger was successfully registered, else `false` */ - setLogger(logger: DiagLogger, options?: LoggerOptions): boolean; + setLogger(logger: DiagLogger, options?: DiagLoggerOptions): boolean; /** * diff --git a/api/src/index.ts b/api/src/index.ts index 2326aba9dcc..69920058747 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -32,6 +32,7 @@ export { DiagLogger, DiagLogLevel, ComponentLoggerOptions, + DiagLoggerOptions, } from './diag/types'; export type { DiagAPI } from './api/diag'; diff --git a/examples/http/client.js b/examples/http/client.js index 40babbda895..168d4339223 100644 --- a/examples/http/client.js +++ b/examples/http/client.js @@ -9,8 +9,7 @@ function makeRequest() { // span corresponds to outgoing requests. Here, we have manually created // the span, which is created to track work that happens outside of the // request lifecycle entirely. - const span = tracer.startSpan('makeRequest'); - api.context.with(api.trace.setSpan(api.context.active(), span), () => { + tracer.startActiveSpan('makeRequest', (span) => { http.get({ host: 'localhost', port: 8080, diff --git a/examples/http/package.json b/examples/http/package.json index 490f9dba75a..f10a5992dc2 100644 --- a/examples/http/package.json +++ b/examples/http/package.json @@ -1,7 +1,7 @@ { "name": "http-example", "private": true, - "version": "0.25.0", + "version": "0.35.1", "description": "Example of HTTP integration with OpenTelemetry", "main": "index.js", "scripts": { @@ -28,15 +28,15 @@ "url": "/~https://github.com/open-telemetry/opentelemetry-js/issues" }, "dependencies": { - "@opentelemetry/api": "^1.0.2", - "@opentelemetry/exporter-jaeger": "0.25.0", - "@opentelemetry/exporter-zipkin": "0.25.0", - "@opentelemetry/instrumentation": "0.25.0", - "@opentelemetry/instrumentation-http": "0.25.0", - "@opentelemetry/resources": "0.25.0", - "@opentelemetry/semantic-conventions": "0.25.0", - "@opentelemetry/sdk-trace-node": "0.25.0", - "@opentelemetry/sdk-trace-base": "0.25.0" + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/exporter-jaeger": "1.9.1", + "@opentelemetry/exporter-zipkin": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/instrumentation-http": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1", + "@opentelemetry/sdk-trace-node": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "homepage": "/~https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/http", "devDependencies": { diff --git a/examples/http/server.js b/examples/http/server.js index 1c11f60323a..ea65858b520 100644 --- a/examples/http/server.js +++ b/examples/http/server.js @@ -19,9 +19,10 @@ function startServer(port) { /** A function which handles requests and send response. */ function handleRequest(request, response) { - const currentSpan = api.trace.getSpan(api.context.active()); + const currentSpan = api.trace.getActiveSpan(); // display traceid in the terminal - console.log(`traceid: ${currentSpan.spanContext().traceId}`); + const traceId = currentSpan.spanContext().traceId; + console.log(`traceId: ${traceId}`); const span = tracer.startSpan('handleRequest', { kind: 1, // server attributes: { key: 'value' }, diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index b85f4c1d5f5..7d5a24082aa 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -12,11 +12,15 @@ All notable changes to experimental packages in this project will be documented * feat: use HTTP_ROUTE in span name [#3603](/~https://github.com/open-telemetry/opentelemetry-js/pull/3603) @Flarna * feat: add HTTP_ROUTE attribute to http incoming metrics if present [#3581](/~https://github.com/open-telemetry/opentelemetry-js/pull/3581) @hermogenes +* feat(opentelemetry-instrumentation-grpc): allow to add attributes from grpc metadata in the patched server [#3589](/~https://github.com/open-telemetry/opentelemetry-js/pull/3589) @zombispormedio * feat(sdk-node): install diag logger with OTEL_LOG_LEVEL [#3627](/~https://github.com/open-telemetry/opentelemetry-js/pull/3627) @legendecas +* feat(otlp-exporter-base): add retries [#3207](/~https://github.com/open-telemetry/opentelemetry-js/pull/3207) @svetlanabrennan +* feat(sdk-node): override IdGenerator when using NodeSDK [#3645](/~https://github.com/open-telemetry/opentelemetry-js/pull/3645) @haddasbronfman ### :bug: (Bug Fix) * fix(prometheus-exporter): add possibility to respond to errors returned by `server.listen()` [#3552](/~https://github.com/open-telemetry/opentelemetry-js/pull/3402) @pichlermarc + fix(sdk-node): update instrumentations once MeterProvider is initialized [#3624](/~https://github.com/open-telemetry/opentelemetry-js/pull/3624) @pichlermarc ### :books: (Refine Doc) @@ -75,7 +79,6 @@ All notable changes to experimental packages in this project will be documented * deps: remove unused proto-loader dependencies and update grpc-js and proto-loader versions [#3337](/~https://github.com/open-telemetry/opentelemetry-js/pull/3337) @seemk * feat(metrics-exporters): configure temporality via environment variable [#3305](/~https://github.com/open-telemetry/opentelemetry-js/pull/3305) @pichlermarc * feat(console-metric-exporter): add temporality configuration [#3387](/~https://github.com/open-telemetry/opentelemetry-js/pull/3387) @pichlermarc -* feat(otlp-exporter-base): add retries [#3207](/~https://github.com/open-telemetry/opentelemetry-js/pull/3207) @svetlanabrennan ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/README.md b/experimental/packages/opentelemetry-instrumentation-grpc/README.md index 0ea83304386..2aa6038c03e 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/README.md +++ b/experimental/packages/opentelemetry-instrumentation-grpc/README.md @@ -47,7 +47,7 @@ gRPC instrumentation accepts the following configuration: | Options | Type | Description | |----------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`ignoreGrpcMethods`](/~https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts#L25) | `IgnoreMatcher[]` | gRPC instrumentation will not trace any methods that match anything in this list. You may pass a string (case-insensitive match), a `RegExp` object, or a filter function. | -| [`metadataToSpanAttributes`](/~https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts#L27) | `object` | List of case insensitive metadata to convert to span attributes. Client (outgoing requests, incoming responses) metadata attributes will be converted to span attributes in the form of `rpc.{request\response}.metadata.metadata_key`, e.g. `rpc.response.metadata.date` | +| [`metadataToSpanAttributes`](/~https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts#L27) | `object` | List of case insensitive metadata to convert to span attributes. Client and server (outgoing requests, incoming responses) metadata attributes will be converted to span attributes in the form of `rpc.{request\response}.metadata.metadata_key`, e.g. `rpc.response.metadata.date` | ## Useful links diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts index 2e5c833b193..6608521bd6b 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts @@ -212,6 +212,24 @@ export class GrpcJsInstrumentation extends InstrumentationBase { [SemanticAttributes.RPC_SERVICE]: service, }); + instrumentation._metadataCapture.server.captureRequestMetadata( + span, + call.metadata + ); + + instrumentation._wrap( + call, + 'sendMetadata', + originalSendMetadata => + (responseMetadata: grpcJs.Metadata) => { + instrumentation._metadataCapture.server.captureResponseMetadata( + span, + responseMetadata + ); + originalSendMetadata.call(call, responseMetadata); + } + ); + context.with(trace.setSpan(context.active(), span), () => { handleServerFunction.call( self, @@ -385,6 +403,16 @@ export class GrpcJsInstrumentation extends InstrumentationBase { config.metadataToSpanAttributes?.client?.responseMetadata ?? [] ), }, + server: { + captureRequestMetadata: metadataCapture( + 'request', + config.metadataToSpanAttributes?.server?.requestMetadata ?? [] + ), + captureResponseMetadata: metadataCapture( + 'response', + config.metadataToSpanAttributes?.server?.responseMetadata ?? [] + ), + }, }; } } diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index 4df381eb668..fbeac0395ee 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -223,6 +223,24 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< [SemanticAttributes.RPC_SERVICE]: service, }); + instrumentation._metadataCapture.server.captureRequestMetadata( + span, + call.metadata + ); + + instrumentation._wrap( + call as any, + 'sendMetadata', + originalSendMetadata => + (responseMetadata: grpcTypes.Metadata) => { + instrumentation._metadataCapture.server.captureResponseMetadata( + span, + responseMetadata + ); + originalSendMetadata.call(call, responseMetadata); + } + ); + context.with(trace.setSpan(context.active(), span), () => { switch (type) { case 'unary': @@ -370,6 +388,16 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< config.metadataToSpanAttributes?.client?.responseMetadata ?? [] ), }, + server: { + captureRequestMetadata: metadataCapture( + 'request', + config.metadataToSpanAttributes?.server?.requestMetadata ?? [] + ), + captureResponseMetadata: metadataCapture( + 'response', + config.metadataToSpanAttributes?.server?.responseMetadata ?? [] + ), + }, }; } } diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts index d0052f7643e..ebc17ae1d4a 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts @@ -29,4 +29,14 @@ export type metadataCaptureType = { metadata: grpcJsTypes.Metadata | grpcTypes.Metadata ) => void; }; + server: { + captureRequestMetadata: ( + span: Span, + metadata: grpcJsTypes.Metadata | grpcTypes.Metadata + ) => void; + captureResponseMetadata: ( + span: Span, + metadata: grpcJsTypes.Metadata | grpcTypes.Metadata + ) => void; + }; }; diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts index dd08d8224b8..430476b7a47 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts @@ -29,5 +29,9 @@ export interface GrpcInstrumentationConfig extends InstrumentationConfig { responseMetadata?: string[]; requestMetadata?: string[]; }; + server?: { + responseMetadata?: string[]; + requestMetadata?: string[]; + }; }; } diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts b/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts index 85b6a1c64e2..58855a6a5ff 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts @@ -978,6 +978,10 @@ export const runTests = ( requestMetadata: ['client_metadata_key'], responseMetadata: ['server_metadata_key'], }, + server: { + requestMetadata: ['client_metadata_key'], + responseMetadata: ['server_metadata_key'], + }, }, }); @@ -999,13 +1003,18 @@ export const runTests = ( }); }); - describe('Capture request/response metadata in client span', () => { + describe('Capture request/response metadata in client and server spans', () => { const attributeValidation = { clientAttributes: { 'rpc.request.metadata.client_metadata_key': 'client_metadata_value', 'rpc.response.metadata.server_metadata_key': 'server_metadata_value', }, + serverAttributes: { + 'rpc.request.metadata.client_metadata_key': 'client_metadata_value', + 'rpc.response.metadata.server_metadata_key': + 'server_metadata_value', + }, }; runTestWithAttributeValidation( diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts index 7801a7626c8..a563e294715 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -484,7 +484,7 @@ export const getIncomingRequestAttributes = ( } if (requestUrl) { - attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.path || '/'; } if (userAgent !== undefined) { 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 700498cfb66..35b3c1ab5bc 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -503,6 +503,23 @@ describe('Utility', () => { }); assert.strictEqual(attributes[SemanticAttributes.HTTP_ROUTE], undefined); }); + + it('should set http.target as path in http span attributes', () => { + const request = { + url: 'http://hostname/user/?q=val', + method: 'GET', + } as IncomingMessage; + request.headers = { + 'user-agent': 'chrome', + }; + const attributes = utils.getIncomingRequestAttributes(request, { + component: 'http', + }); + assert.strictEqual( + attributes[SemanticAttributes.HTTP_TARGET], + '/user/?q=val' + ); + }); }); describe('headers to span attributes capture', () => { diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index faae12dc7d5..dc9244f3e4f 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -48,6 +48,7 @@ import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' import { NodeSDKConfiguration } from './types'; import { TracerProviderWithEnvExporters } from './TracerProviderWithEnvExporter'; import { getEnv } from '@opentelemetry/core'; +import { parseInstrumentationOptions } from './utils'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ @@ -61,6 +62,7 @@ export type MeterProviderConfig = { */ views?: View[]; }; + export class NodeSDK { private _tracerProviderConfig?: { tracerConfig: NodeTracerConfig; @@ -117,6 +119,9 @@ export class NodeSDK { if (configuration.spanLimits) { tracerProviderConfig.spanLimits = configuration.spanLimits; } + if (configuration.idGenerator) { + tracerProviderConfig.idGenerator = configuration.idGenerator; + } const spanProcessor = configuration.spanProcessor ?? @@ -272,6 +277,15 @@ export class NodeSDK { this._meterProvider = meterProvider; metrics.setGlobalMeterProvider(meterProvider); + + // TODO: This is a workaround to fix /~https://github.com/open-telemetry/opentelemetry-js/issues/3609 + // If the MeterProvider is not yet registered when instrumentations are registered, all metrics are dropped. + // This code is obsolete once /~https://github.com/open-telemetry/opentelemetry-js/issues/3622 is implemented. + for (const instrumentation of parseInstrumentationOptions( + this._instrumentations + )) { + instrumentation.setMeterProvider(metrics.getMeterProvider()); + } } } diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index 49dfae8f7c9..38178e58c56 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -24,6 +24,7 @@ import { SpanExporter, SpanLimits, SpanProcessor, + IdGenerator, } from '@opentelemetry/sdk-trace-base'; export interface NodeSDKConfiguration { @@ -41,4 +42,5 @@ export interface NodeSDKConfiguration { spanProcessor: SpanProcessor; traceExporter: SpanExporter; spanLimits: SpanLimits; + idGenerator: IdGenerator; } diff --git a/experimental/packages/opentelemetry-sdk-node/src/utils.ts b/experimental/packages/opentelemetry-sdk-node/src/utils.ts new file mode 100644 index 00000000000..a3d83147477 --- /dev/null +++ b/experimental/packages/opentelemetry-sdk-node/src/utils.ts @@ -0,0 +1,43 @@ +/* + * 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 { + Instrumentation, + InstrumentationOption, +} from '@opentelemetry/instrumentation'; + +// TODO: This part of a workaround to fix /~https://github.com/open-telemetry/opentelemetry-js/issues/3609 +// If the MeterProvider is not yet registered when instrumentations are registered, all metrics are dropped. +// This code is obsolete once /~https://github.com/open-telemetry/opentelemetry-js/issues/3622 is implemented. +export function parseInstrumentationOptions( + options: InstrumentationOption[] = [] +): Instrumentation[] { + let instrumentations: Instrumentation[] = []; + for (let i = 0, j = options.length; i < j; i++) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const option = options[i] as any; + if (Array.isArray(option)) { + const results = parseInstrumentationOptions(option); + instrumentations = instrumentations.concat(results); + } else if (typeof option === 'function') { + instrumentations.push(new option()); + } else if ((option as Instrumentation).instrumentationName) { + instrumentations.push(option); + } + } + + return instrumentations; +} diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index fb439017105..5fcdb7f94d5 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -45,6 +45,7 @@ import { SimpleSpanProcessor, BatchSpanProcessor, NoopSpanProcessor, + IdGenerator, } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as semver from 'semver'; @@ -691,6 +692,33 @@ describe('Node SDK', () => { }); }); }); + + describe('configure IdGenerator', async () => { + class CustomIdGenerator implements IdGenerator { + generateTraceId(): string { + return 'constant-test-trace-id'; + } + generateSpanId(): string { + return 'constant-test-span-id'; + } + } + + it('should configure IdGenerator via config', async () => { + const idGenerator = new CustomIdGenerator(); + const spanProcessor = new SimpleSpanProcessor(new ConsoleSpanExporter()); + const sdk = new NodeSDK({ + idGenerator, + spanProcessor, + }); + sdk.start(); + + const span = trace.getTracer('test').startSpan('testName'); + span.end(); + + assert.strictEqual(span.spanContext().spanId, 'constant-test-span-id'); + assert.strictEqual(span.spanContext().traceId, 'constant-test-trace-id'); + }); + }); }); describe('setup exporter from env', () => { diff --git a/lerna.json b/lerna.json index d97ab02a62b..6eba3f4874b 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,7 @@ "selenium-tests", "examples/otlp-exporter-node", "examples/opentelemetry-web", + "examples/http", "examples/https" ] }