diff --git a/test/loginjection.bunyan.test.ts b/test/loginjection.bunyan.test.ts new file mode 100644 index 00000000..3518d4a8 --- /dev/null +++ b/test/loginjection.bunyan.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright Splunk Inc. + * + * 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 + * + * http://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 { TestLogStream, assertInjection } from './utils'; +import { startTracing, stopTracing } from '../src/tracing'; +import type * as bunyan from 'bunyan'; + +describe('log injection', () => { + let logStream: TestLogStream; + + beforeEach(() => { + logStream = new TestLogStream(); + }); + + describe('injecting version and environment', () => { + before(() => { + process.env.OTEL_RESOURCE_ATTRIBUTES = + 'service.version=1,deployment.environment=test'; + }); + + after(() => { + delete process.env.OTEL_RESOURCE_ATTRIBUTES; + }); + + it('injects service version and service environment if available', () => { + startTracing({ serviceName: 'test-service' }); + + const logger: bunyan = require('bunyan').createLogger({ + name: 'test', + stream: logStream.stream, + }); + + assertInjection(logStream, logger, [ + ['service.name', 'test-service'], + ['service.version', '1'], + ['service.environment', 'test'], + ]); + + stopTracing(); + }); + }); +}); diff --git a/test/loginjection.pino.test.ts b/test/loginjection.pino.test.ts new file mode 100644 index 00000000..8e5dccaf --- /dev/null +++ b/test/loginjection.pino.test.ts @@ -0,0 +1,77 @@ +/* + * Copyright Splunk Inc. + * + * 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 + * + * http://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 type * as pino from 'pino'; +import { startTracing, stopTracing } from '../src/tracing'; +import { TestLogStream, assertInjection } from './utils'; +import { defaultLogHook } from '../src/instrumentations/logging'; +import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino'; + +describe('pino with with custom hooks', () => { + let logStream: TestLogStream; + + beforeEach(() => { + logStream = new TestLogStream(); + }); + afterEach(() => { + stopTracing(); + }); + + it('is possible to opt out from injecting resource attributes', () => { + const MY_VALUE = 'myValue'; + const MY_ATTRIBUTE = 'myAttribute'; + startTracing({ + serviceName: 'test-service', + instrumentations: [ + new PinoInstrumentation({ + logHook: (span, logRecord) => { + logRecord[MY_ATTRIBUTE] = MY_VALUE; + }, + }), + ], + }); + + const logger: pino.Logger = require('pino')(logStream.stream); + + assertInjection(logStream, logger, [ + ['service.name', undefined], + [MY_ATTRIBUTE, MY_VALUE], + ]); + }); + + it('is easy enough do do both', () => { + const MY_VALUE = 'myValueBoth'; + const MY_ATTRIBUTE = 'myAttributeBoth'; + startTracing({ + serviceName: 'test-service', + instrumentations: [ + new PinoInstrumentation({ + logHook: (span, logRecord) => { + defaultLogHook(span, logRecord); + logRecord[MY_ATTRIBUTE] = MY_VALUE; + }, + }), + ], + }); + + const logger: pino.Logger = require('pino')(logStream.stream); + + assertInjection(logStream, logger, [ + ['service.name', 'test-service'], + [MY_ATTRIBUTE, MY_VALUE], + ]); + }); +}); diff --git a/test/loginjection.test.ts b/test/loginjection.test.ts deleted file mode 100644 index 9fd83a9a..00000000 --- a/test/loginjection.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * 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 - * - * http://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 * as assert from 'assert'; -import * as util from 'util'; -import { Writable } from 'stream'; -import { context, trace } from '@opentelemetry/api'; -import { startTracing, stopTracing } from '../src/tracing'; -import { defaultLogHook } from '../src/instrumentations/logging'; -import type * as pino from 'pino'; -import type * as bunyan from 'bunyan'; -import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino'; - -describe('log injection', () => { - let stream: Writable; - let record: any; - - function assertInjection(logger, extra = [['service.name', 'test-service']]) { - const span = trace.getTracer('test').startSpan('main'); - let traceId; - let spanId; - context.with(trace.setSpan(context.active(), span), () => { - traceId = span.spanContext().traceId; - spanId = span.spanContext().spanId; - logger.info('my-log-message'); - }); - - assert.strictEqual(record['trace_id'], traceId); - assert.strictEqual(record['span_id'], spanId); - - for (const [key, value] of extra || []) { - assert.strictEqual( - record[key], - value, - `Invalid value for "${key}": ${util.inspect(record[key])}` - ); - } - } - - beforeEach(() => { - stream = new Writable({ - write: (chunk) => { - record = JSON.parse(chunk); - }, - }); - record = {}; - }); - - describe('injecting version and environment', () => { - before(() => { - process.env.OTEL_RESOURCE_ATTRIBUTES = - 'service.version=1,deployment.environment=test'; - }); - - after(() => { - delete process.env.OTEL_RESOURCE_ATTRIBUTES; - }); - - it('injects service version and service environment if available', () => { - startTracing({ serviceName: 'test-service' }); - - const logger: bunyan = require('bunyan').createLogger({ - name: 'test', - stream, - }); - - assertInjection(logger, [ - ['service.name', 'test-service'], - ['service.version', '1'], - ['service.environment', 'test'], - ]); - - stopTracing(); - }); - }); - - it('injects context to winston records', () => { - startTracing({ serviceName: 'test-service' }); - const winston: winston = require('winston'); - const logger = winston.createLogger({ - transports: [new winston.transports.Stream({ stream })], - }); - assertInjection(logger); - stopTracing(); - }); - - describe('injecting with custom hook', () => { - afterEach(() => { - stopTracing(); - }); - - it('is possible to opt out from injecting resource attributes', () => { - const MY_VALUE = 'myValue'; - const MY_ATTRIBUTE = 'myAttribute'; - startTracing({ - serviceName: 'test-service', - instrumentations: [ - new PinoInstrumentation({ - logHook: (span, logRecord) => { - logRecord[MY_ATTRIBUTE] = MY_VALUE; - }, - }), - ], - }); - - const logger: pino.Logger = require('pino')(stream); - - assertInjection(logger, [ - ['service.name', undefined], - [MY_ATTRIBUTE, MY_VALUE], - ]); - }); - - it('is easy enough do do both', () => { - const MY_VALUE = 'myValueBoth'; - const MY_ATTRIBUTE = 'myAttributeBoth'; - startTracing({ - serviceName: 'test-service', - instrumentations: [ - new PinoInstrumentation({ - logHook: (span, logRecord) => { - defaultLogHook(span, logRecord); - logRecord[MY_ATTRIBUTE] = MY_VALUE; - }, - }), - ], - }); - - const logger: pino.Logger = require('pino')(stream); - - assertInjection(logger, [ - ['service.name', 'test-service'], - [MY_ATTRIBUTE, MY_VALUE], - ]); - }); - }); -}); diff --git a/test/loginjection.winston.test.ts b/test/loginjection.winston.test.ts new file mode 100644 index 00000000..e2e13910 --- /dev/null +++ b/test/loginjection.winston.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Splunk Inc. + * + * 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 + * + * http://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 { startTracing, stopTracing } from '../src/tracing'; +import { TestLogStream, assertInjection } from './utils'; + +describe('winston log injection', () => { + let logStream: TestLogStream; + + beforeEach(() => { + logStream = new TestLogStream(); + }); + + it('injects context to winston records', () => { + startTracing({ serviceName: 'test-service' }); + const winston = require('winston'); + const logger = winston.createLogger({ + transports: [new winston.transports.Stream({ stream: logStream.stream })], + }); + assertInjection(logStream, logger); + stopTracing(); + }); +}); diff --git a/test/utils.ts b/test/utils.ts index 7513ab24..fb661829 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -19,6 +19,10 @@ import { InstrumentType, MetricReader, } from '@opentelemetry/sdk-metrics'; +import * as assert from 'assert'; +import * as util from 'util'; +import { Writable } from 'stream'; +import { context, trace } from '@opentelemetry/api'; const isConfigVarEntry = (key) => { const lowercased = key.toLowerCase(); @@ -63,3 +67,42 @@ export class TestMetricReader extends MetricReader { protected async onForceFlush() {} protected async onShutdown() {} } + +export class TestLogStream { + public stream: Writable; + public record = {}; + + constructor() { + this.stream = new Writable({ + write: (chunk) => { + this.record = JSON.parse(chunk); + }, + }); + } +} + +export function assertInjection( + stream: TestLogStream, + logger: any, + extra = [['service.name', 'test-service']] +) { + const span = trace.getTracer('test').startSpan('main'); + let traceId; + let spanId; + context.with(trace.setSpan(context.active(), span), () => { + traceId = span.spanContext().traceId; + spanId = span.spanContext().spanId; + logger.info('my-log-message'); + }); + + assert.strictEqual(stream.record['trace_id'], traceId); + assert.strictEqual(stream.record['span_id'], spanId); + + for (const [key, value] of extra || []) { + assert.strictEqual( + stream.record[key], + value, + `Invalid value for "${key}": ${util.inspect(stream.record[key])}` + ); + } +}