From 622955a95de2625b5fe711f3bc09cdfddaff8121 Mon Sep 17 00:00:00 2001 From: Aaron Abbott Date: Wed, 31 May 2023 01:54:13 -0400 Subject: [PATCH] feat(opencensus-shim) add require-in-the-middle hook to patch @opencensus/core (#3809) --- experimental/CHANGELOG.md | 1 + .../packages/shim-opencensus/README.md | 4 -- .../packages/shim-opencensus/package.json | 3 - .../packages/shim-opencensus/src/register.ts | 19 +++++ .../packages/shim-opencensus/src/shim.ts | 62 ++++++++++++++++ .../shim-opencensus/test/shim.test.ts | 72 +++++++++++++++++++ .../packages/shim-opencensus/test/util.ts | 18 +++-- 7 files changed, 167 insertions(+), 12 deletions(-) create mode 100644 experimental/packages/shim-opencensus/src/register.ts create mode 100644 experimental/packages/shim-opencensus/src/shim.ts create mode 100644 experimental/packages/shim-opencensus/test/shim.test.ts diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 7bdbe404cb..60a3bb2664 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to experimental packages in this project will be documented * feat(exporter-logs-otlp-http): otlp-http exporter for logs. [#3764](https://github.com/open-telemetry/opentelemetry-js/pull/3764/) @fuaiyi * feat(otlp-trace-exporters): Add User-Agent header to OTLP trace exporters. [#3790](https://github.com/open-telemetry/opentelemetry-js/pull/3790) @JamieDanielson * feat(otlp-metric-exporters): Add User-Agent header to OTLP metric exporters. [#3806](https://github.com/open-telemetry/opentelemetry-js/pull/3806) @JamieDanielson +* feat(opencensus-shim): add OpenCensus trace shim [#3809](https://github.com/open-telemetry/opentelemetry-js/pull/3809) @aabmass ### :bug: (Bug Fix) diff --git a/experimental/packages/shim-opencensus/README.md b/experimental/packages/shim-opencensus/README.md index 63bea4df42..e614fccf38 100644 --- a/experimental/packages/shim-opencensus/README.md +++ b/experimental/packages/shim-opencensus/README.md @@ -3,10 +3,6 @@ [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] -> **Note** -> This package is in active development and has not yet been released. You cannot install this -> package from NPM yet. - OpenCensus shim allows existing OpenCensus instrumentation to report to OpenTelemetry. This allows you to incrementally migrate your existing OpenCensus instrumentation to OpenTelemetry. More details are available in the [OpenCensus Compatibility Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/compatibility/opencensus.md). diff --git a/experimental/packages/shim-opencensus/package.json b/experimental/packages/shim-opencensus/package.json index 1dae7c52a5..202f8e2d67 100644 --- a/experimental/packages/shim-opencensus/package.json +++ b/experimental/packages/shim-opencensus/package.json @@ -2,7 +2,6 @@ "name": "@opentelemetry/shim-opencensus", "version": "0.39.1", "description": "OpenCensus to OpenTelemetry shim", - "private": true, "main": "build/src/index.js", "types": "build/src/index.d.ts", "repository": "open-telemetry/opentelemetry-js", @@ -45,7 +44,6 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/core": "1.13.0", "@opentelemetry/context-async-hooks": "1.13.0", "@opentelemetry/sdk-trace-base": "1.13.0", "@opencensus/core": "0.1.0", @@ -65,7 +63,6 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/context-async-hooks": "1.13.0", "@opentelemetry/core": "1.13.0", "require-in-the-middle": "^7.0.0", "semver": "^7.3.5" diff --git a/experimental/packages/shim-opencensus/src/register.ts b/experimental/packages/shim-opencensus/src/register.ts new file mode 100644 index 0000000000..c07f54c2b2 --- /dev/null +++ b/experimental/packages/shim-opencensus/src/register.ts @@ -0,0 +1,19 @@ +/* + * 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 { installShim } from './shim'; + +installShim(); diff --git a/experimental/packages/shim-opencensus/src/shim.ts b/experimental/packages/shim-opencensus/src/shim.ts new file mode 100644 index 0000000000..0426561c76 --- /dev/null +++ b/experimental/packages/shim-opencensus/src/shim.ts @@ -0,0 +1,62 @@ +/* + * 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 { diag, trace, Tracer } from '@opentelemetry/api'; +import { Hook } from 'require-in-the-middle'; +import * as oc from '@opencensus/core'; + +import { ShimTracer } from './ShimTracer'; +import { VERSION } from './version'; + +type CoreTracerConstructor = new ( + ...args: ConstructorParameters +) => oc.Tracer; + +let hook: Hook | null = null; + +interface OpenCensusShimConfig { + /** + * An optional OpenTelemetry tracer to send OpenCensus spans to. If not provided, one will be + * created for you. + */ + tracer?: Tracer | undefined; +} + +/** + * Patches OpenCensus to redirect all instrumentation to OpenTelemetry. Uses + * require-in-the-middle to override the implementation of OpenCensus's CoreTracer. + * + * Use {@link uninstallShim} to undo the effects of this function. + * + * @param config + */ +export function installShim({ + tracer = trace.getTracer('@opentelemetry/shim-opencensus', VERSION), +}: OpenCensusShimConfig = {}): void { + diag.info('Installing OpenCensus shim require-in-the-middle hook'); + + hook = new Hook(['@opencensus/core'], exports => { + const CoreTracer: CoreTracerConstructor = ShimTracer.bind(null, tracer); + return { + ...exports, + CoreTracer, + }; + }); +} + +export function uninstallShim(): void { + hook?.unhook(); +} diff --git a/experimental/packages/shim-opencensus/test/shim.test.ts b/experimental/packages/shim-opencensus/test/shim.test.ts new file mode 100644 index 0000000000..525a8026a7 --- /dev/null +++ b/experimental/packages/shim-opencensus/test/shim.test.ts @@ -0,0 +1,72 @@ +/* + * 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 * as assert from 'assert'; +import { installShim, uninstallShim } from '../src/shim'; +import { ShimTracer } from '../src'; +import { CoreTracer as OrigCoreTracer } from '@opencensus/core'; +import { withTestTracerProvider } from './util'; +import { trace } from '@opentelemetry/api'; + +describe('shim', () => { + beforeEach(uninstallShim); + afterEach(uninstallShim); + afterEach(() => { + trace.disable(); + }); + + describe('installShim', () => { + it('should patch the @opencensus/core CoreTracer to create instances of the ShimTracer', () => { + installShim(); + const { CoreTracer } = require('@opencensus/core'); + assert.notStrictEqual(CoreTracer, OrigCoreTracer); + assert(new CoreTracer() instanceof ShimTracer); + }); + + it('should use the provided Tracer', async () => { + const spans = await withTestTracerProvider(tracerProvider => { + const tracer = tracerProvider.getTracer('test'); + installShim({ tracer }); + const CoreTracer: typeof OrigCoreTracer = + require('@opencensus/core').CoreTracer; + const coreTracer = new CoreTracer(); + coreTracer.startChildSpan().end(); + }); + assert.strictEqual(spans.length, 1); + }); + + it('should use the global OpenTelemetry TracerProvider if none provided', async () => { + installShim(); + const spans = await withTestTracerProvider(tracerProvider => { + trace.setGlobalTracerProvider(tracerProvider); + const CoreTracer: typeof OrigCoreTracer = + require('@opencensus/core').CoreTracer; + const coreTracer = new CoreTracer(); + coreTracer.startChildSpan().end(); + }); + assert.strictEqual(spans.length, 1); + }); + }); + + describe('uninstallShim', () => { + it('should restore the original CoreTracer', () => { + installShim(); + uninstallShim(); + const { CoreTracer } = require('@opencensus/core'); + assert.strictEqual(CoreTracer, OrigCoreTracer); + }); + }); +}); diff --git a/experimental/packages/shim-opencensus/test/util.ts b/experimental/packages/shim-opencensus/test/util.ts index b28baca4d2..842525ea0c 100644 --- a/experimental/packages/shim-opencensus/test/util.ts +++ b/experimental/packages/shim-opencensus/test/util.ts @@ -27,10 +27,21 @@ import { AsyncHooksContextManager, AsyncLocalStorageContextManager, } from '@opentelemetry/context-async-hooks'; -import { Tracer, context } from '@opentelemetry/api'; +import { Tracer, TracerProvider, context } from '@opentelemetry/api'; export async function withTestTracer( func: (shimTracer: ShimTracer, otelTracer: Tracer) => void | Promise +): Promise { + return await withTestTracerProvider(tracerProvider => + func( + new ShimTracer(tracerProvider.getTracer('test-shim')), + tracerProvider.getTracer('test-otel') + ) + ); +} + +export async function withTestTracerProvider( + func: (otelTracerProvider: TracerProvider) => void | Promise ): Promise { const tracerProvider = new BasicTracerProvider({ sampler: new AlwaysOnSampler(), @@ -38,10 +49,7 @@ export async function withTestTracer( const inMemExporter = new InMemorySpanExporter(); tracerProvider.addSpanProcessor(new SimpleSpanProcessor(inMemExporter)); - await func( - new ShimTracer(tracerProvider.getTracer('test-shim')), - tracerProvider.getTracer('test-otel') - ); + await func(tracerProvider); await tracerProvider.forceFlush(); const spans = inMemExporter.getFinishedSpans();