diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 205dd3123c3..4d57f6d2143 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to experimental packages in this project will be documented * feat(opentelemetry-instrumentation-fetch): optionally ignore network events #3028 @gregolsen * feat(http-instrumentation): record exceptions in http instrumentation #3008 @luismiramirez +* feat(node-sdk): add serviceName config option #2867 @naseemkullah ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md index 841bff28247..a21de3c7411 100644 --- a/experimental/packages/opentelemetry-sdk-node/README.md +++ b/experimental/packages/opentelemetry-sdk-node/README.md @@ -128,6 +128,10 @@ Configure a trace exporter. If an exporter OR span processor is not configured, Configure tracing parameters. These are the same trace parameters used to [configure a tracer](../../../packages/opentelemetry-sdk-trace-base/src/types.ts#L71). +### serviceName + +Configure the [service name](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md#service). + ## Useful links - For more information on OpenTelemetry, visit: diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 6f9ddb1d23c..126d0437dc2 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -14,23 +14,26 @@ * limitations under the License. */ -import { TextMapPropagator } from '@opentelemetry/api'; +import { ContextManager, TextMapPropagator } from '@opentelemetry/api'; import { metrics } from '@opentelemetry/api-metrics'; -import { ContextManager } from '@opentelemetry/api'; -import { MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; import { InstrumentationOption, - registerInstrumentations, + registerInstrumentations } from '@opentelemetry/instrumentation'; -import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { detectResources, envDetector, processDetector, Resource, - ResourceDetectionConfig, + ResourceDetectionConfig } from '@opentelemetry/resources'; -import { BatchSpanProcessor, SpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; +import { + BatchSpanProcessor, + SpanProcessor +} from '@opentelemetry/sdk-trace-base'; +import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { NodeSDKConfiguration } from './types'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ @@ -50,6 +53,7 @@ export class NodeSDK { private _tracerProvider?: NodeTracerProvider; private _meterProvider?: MeterProvider; + private _serviceName?: string; /** * Create a new NodeJS SDK instance @@ -57,6 +61,9 @@ export class NodeSDK { public constructor(configuration: Partial = {}) { this._resource = configuration.resource ?? new Resource({}); + + this._serviceName = configuration.serviceName; + this._autoDetectResources = configuration.autoDetectResources ?? true; if (configuration.spanProcessor || configuration.traceExporter) { @@ -113,7 +120,9 @@ export class NodeSDK { } /** Detect resource attributes */ - public async detectResources(config?: ResourceDetectionConfig): Promise { + public async detectResources( + config?: ResourceDetectionConfig + ): Promise { const internalConfig: ResourceDetectionConfig = { detectors: [ envDetector, processDetector], ...config, @@ -135,6 +144,12 @@ export class NodeSDK { await this.detectResources(); } + this._resource = this._serviceName === undefined + ? this._resource + : this._resource.merge(new Resource( + {[SemanticResourceAttributes.SERVICE_NAME]: this._serviceName} + )); + if (this._tracerProviderConfig) { const tracerProvider = new NodeTracerProvider({ ...this._tracerProviderConfig.tracerConfig, diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index ea93ddae167..90c1e08401d 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { SpanAttributes, TextMapPropagator, Sampler } from '@opentelemetry/api'; -import type { ContextManager } from '@opentelemetry/api'; +import type { ContextManager, SpanAttributes } from '@opentelemetry/api'; +import { Sampler, TextMapPropagator } from '@opentelemetry/api'; import { InstrumentationOption } from '@opentelemetry/instrumentation'; -import { MetricReader } from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; +import { MetricReader } from '@opentelemetry/sdk-metrics-base'; import { SpanExporter, - SpanProcessor, SpanLimits, + SpanProcessor } from '@opentelemetry/sdk-trace-base'; export interface NodeSDKConfiguration { @@ -34,6 +34,7 @@ export interface NodeSDKConfiguration { instrumentations: InstrumentationOption[]; resource: Resource; sampler: Sampler; + serviceName?: string; spanProcessor: SpanProcessor; traceExporter: SpanExporter; spanLimits: SpanLimits; diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 14b362209c0..217800c9c05 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -261,4 +261,76 @@ describe('Node SDK', () => { }); }); }); + + describe('configureServiceName', async () => { + it('should configure service name via config', async () => { + const sdk = new NodeSDK({ + serviceName: 'config-set-name', + }); + + await sdk.start(); + const resource = sdk['_resource']; + + assertServiceResource(resource, { + name: 'config-set-name', + }); + }); + + it('should configure service name via OTEL_SERVICE_NAME env var', async () => { + process.env.OTEL_SERVICE_NAME='env-set-name'; + const sdk = new NodeSDK(); + + await sdk.start(); + const resource = sdk['_resource']; + + assertServiceResource(resource, { + name: 'env-set-name', + }); + delete process.env.OTEL_SERVICE_NAME; + }); + + it('should favor config set service name over OTEL_SERVICE_NAME env set service name', async () => { + process.env.OTEL_SERVICE_NAME='env-set-name'; + const sdk = new NodeSDK({ + serviceName: 'config-set-name', + }); + + await sdk.start(); + const resource = sdk['_resource']; + + assertServiceResource(resource, { + name: 'config-set-name', + }); + delete process.env.OTEL_SERVICE_NAME; + }); + + + it('should configure service name via OTEL_RESOURCE_ATTRIBUTES env var', async () => { + process.env.OTEL_RESOURCE_ATTRIBUTES = 'service.name=resource-env-set-name'; + const sdk = new NodeSDK(); + + await sdk.start(); + const resource = sdk['_resource']; + + assertServiceResource(resource, { + name: 'resource-env-set-name', + }); + delete process.env.OTEL_RESOURCE_ATTRIBUTES; + }); + + it('should favor config set service name over OTEL_RESOURCE_ATTRIBUTES env set service name', async () => { + process.env.OTEL_RESOURCE_ATTRIBUTES = 'service.name=resource-env-set-name'; + const sdk = new NodeSDK({ + serviceName: 'config-set-name', + }); + + await sdk.start(); + const resource = sdk['_resource']; + + assertServiceResource(resource, { + name: 'config-set-name', + }); + delete process.env.OTEL_RESOURCE_ATTRIBUTES; + }); + }); }); diff --git a/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts b/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts index 206c9bcf86a..331fed6e8ec 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts @@ -232,7 +232,7 @@ export const assertServiceResource = ( resource: Resource, validations: { name: string; - instanceId: string; + instanceId?: string; namespace?: string; version?: string; }