From 758c7af0c183b36bbce0b0dfbda3dd43bdc0ca8d Mon Sep 17 00:00:00 2001 From: Jamie Danielson Date: Mon, 15 May 2023 05:14:12 -0400 Subject: [PATCH] feat(otlp-trace-exporters): Add User-Agent header to OTLP trace exporters (#3790) Co-authored-by: Marc Pichler --- experimental/CHANGELOG.md | 4 ++++ .../src/OTLPTraceExporter.ts | 14 +++++++++++--- .../test/OTLPTraceExporter.test.ts | 7 +++++++ .../src/platform/node/OTLPTraceExporter.ts | 15 ++++++++++----- .../test/node/CollectorTraceExporter.test.ts | 8 ++++++++ .../src/platform/node/OTLPTraceExporter.ts | 15 ++++++++++----- .../test/node/OTLPTraceExporter.test.ts | 11 +++++++++++ 7 files changed, 61 insertions(+), 13 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 398fa659e8..f9adec9c9a 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to experimental packages in this project will be documented ## Unreleased +### :rocket: (Enhancement) + +* feat(otlp-trace-exporters): Add User-Agent header to OTLP trace exporters. [#3790](https://github.com/open-telemetry/opentelemetry-js/pull/3790) @JamieDanielson + ### :boom: Breaking Change ### :rocket: (Enhancement) diff --git a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts index 2cc5abcb2e..c99826a176 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts @@ -28,6 +28,11 @@ import { createExportTraceServiceRequest, IExportTraceServiceRequest, } from '@opentelemetry/otlp-transformer'; +import { VERSION } from './version'; + +const USER_AGENT = { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, +}; /** * OTLP Trace Exporter for Node @@ -38,9 +43,12 @@ export class OTLPTraceExporter { constructor(config: OTLPGRPCExporterConfigNode = {}) { super(config); - const headers = baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS - ); + const headers = { + ...USER_AGENT, + ...baggageUtils.parseKeyPairsIntoRecord( + getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS + ), + }; this.metadata ||= new Metadata(); for (const [k, v] of Object.entries(headers)) { this.metadata.set(k, v); diff --git a/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts index 29c080b966..17e5511010 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts @@ -27,6 +27,7 @@ import * as grpc from '@grpc/grpc-js'; import * as path from 'path'; import * as sinon from 'sinon'; import { OTLPTraceExporter } from '../src'; +import { VERSION } from '../src/version'; import { ensureExportedSpanIsCorrect, @@ -336,6 +337,12 @@ describe('when configuring via environment', () => { assert.deepStrictEqual(collectorExporter.metadata?.get('foo'), ['bar']); envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); + it('should include user agent in header', () => { + const collectorExporter = new OTLPTraceExporter(); + assert.deepStrictEqual(collectorExporter.metadata?.get('User-Agent'), [ + `OTel-OTLP-Exporter-JavaScript/${VERSION}`, + ]); + }); it('should override global headers config with signal headers defined via env', () => { const metadata = new grpc.Metadata(); metadata.set('foo', 'bar'); diff --git a/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts index 8704baf576..f10fbd0ec0 100644 --- a/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts @@ -26,9 +26,13 @@ import { createExportTraceServiceRequest, IExportTraceServiceRequest, } from '@opentelemetry/otlp-transformer'; +import { VERSION } from '../../version'; const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; +const USER_AGENT = { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, +}; /** * Collector Trace Exporter for Node @@ -39,12 +43,13 @@ export class OTLPTraceExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { super(config); - this.headers = Object.assign( - this.headers, - baggageUtils.parseKeyPairsIntoRecord( + this.headers = { + ...this.headers, + ...USER_AGENT, + ...baggageUtils.parseKeyPairsIntoRecord( getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS - ) - ); + ), + }; } convert(spans: ReadableSpan[]): IExportTraceServiceRequest { diff --git a/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts index 426aed4431..9238260d30 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts @@ -36,6 +36,7 @@ import { import { nextTick } from 'process'; import { MockedResponse } from './nodeHelpers'; import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; +import { VERSION } from '../../src/version'; let fakeRequest: PassThrough; @@ -160,6 +161,13 @@ describe('OTLPTraceExporter - node with json over http', () => { assert.strictEqual(collectorExporter.headers.foo, 'bar'); envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); + it('should include user agent in header', () => { + const collectorExporter = new OTLPTraceExporter(); + assert.strictEqual( + collectorExporter.headers['User-Agent'], + `OTel-OTLP-Exporter-JavaScript/${VERSION}` + ); + }); it('should override global headers config with signal headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; envSource.OTEL_EXPORTER_OTLP_TRACES_HEADERS = 'foo=boo'; diff --git a/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts index 0634013311..210a16145a 100644 --- a/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts @@ -29,9 +29,13 @@ import { createExportTraceServiceRequest, IExportTraceServiceRequest, } from '@opentelemetry/otlp-transformer'; +import { VERSION } from '../../version'; const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; +const USER_AGENT = { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, +}; /** * Collector Trace Exporter for Node with protobuf @@ -42,12 +46,13 @@ export class OTLPTraceExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { super(config); - this.headers = Object.assign( - this.headers, - baggageUtils.parseKeyPairsIntoRecord( + this.headers = { + ...this.headers, + ...USER_AGENT, + ...baggageUtils.parseKeyPairsIntoRecord( getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS - ) - ); + ), + }; } convert(spans: ReadableSpan[]): IExportTraceServiceRequest { diff --git a/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts index 64f0e40ea0..c0a604ce90 100644 --- a/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts @@ -39,6 +39,7 @@ import { ServiceClientType, } from '@opentelemetry/otlp-proto-exporter-base'; import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; +import { VERSION } from '../../src/version'; let fakeRequest: PassThrough; @@ -52,6 +53,16 @@ describe('OTLPTraceExporter - node with proto over http', () => { sinon.restore(); }); + describe('default behavior for headers', () => { + const collectorExporter = new OTLPTraceExporter(); + it('should include user agent in header', () => { + assert.strictEqual( + collectorExporter.headers['User-Agent'], + `OTel-OTLP-Exporter-JavaScript/${VERSION}` + ); + }); + }); + describe('when configuring via environment', () => { const envSource = process.env; it('should use url defined in env that ends with root path and append version and signal path', () => {