diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 9410607e4e08..526011676d25 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1612,6 +1612,15 @@ packages: '@opentelemetry/api': 1.0.4 dev: false + /@opentelemetry/context-async-hooks/1.0.1_@opentelemetry+api@1.0.4: + resolution: {integrity: sha512-oGCPjDlZ03gXPAdLxw5iqaQJIpL8BZFaiZhAPgF7Vj6XYmrmrA/FXVIsjfNECQTa2D+lt5p8vf0xYIkFufgceQ==} + engines: {node: '>=8.1.0'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.1.0' + dependencies: + '@opentelemetry/api': 1.0.4 + dev: false + /@opentelemetry/context-base/0.10.2: resolution: {integrity: sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw==} engines: {node: '>=8.0.0'} @@ -1704,6 +1713,16 @@ packages: '@opentelemetry/core': 0.24.0_@opentelemetry+api@1.0.4 dev: false + /@opentelemetry/propagator-b3/1.0.1_@opentelemetry+api@1.0.4: + resolution: {integrity: sha512-UQQiOpR/WXyoqIRQEkn6RuXtGfpjhBDMq/1HrVxRCRPMVn7f4e+zxZWoQSsCOvSGQTu9S+W8eAutm00sRJN7fg==} + engines: {node: '>=8.0.0'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.1.0' + dependencies: + '@opentelemetry/api': 1.0.4 + '@opentelemetry/core': 1.0.1_@opentelemetry+api@1.0.4 + dev: false + /@opentelemetry/propagator-jaeger/0.24.0_@opentelemetry+api@1.0.4: resolution: {integrity: sha512-QXCxBwuSka+vXbBZdumtF7YKO84gwTyKy3GelZV5BPlgWoge0AbLR3DfsO9Beu13pmD+4PyuwMw3LfYsgG1+3g==} engines: {node: '>=8.5.0'} @@ -1714,6 +1733,16 @@ packages: '@opentelemetry/core': 0.24.0_@opentelemetry+api@1.0.4 dev: false + /@opentelemetry/propagator-jaeger/1.0.1_@opentelemetry+api@1.0.4: + resolution: {integrity: sha512-bzvXksBn3j0GyiFXQbx87CUSGC1UDyp4hjL+CCOrQfzIEdp6usKCLHv40Ib5WU6739hPMkyr59CPfKwzlviTtA==} + engines: {node: '>=8.5.0'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.1.0' + dependencies: + '@opentelemetry/api': 1.0.4 + '@opentelemetry/core': 1.0.1_@opentelemetry+api@1.0.4 + dev: false + /@opentelemetry/resources/0.22.0_@opentelemetry+api@1.0.4: resolution: {integrity: sha512-LiX6/JyuD2eHi7Ewrq/PUP79azDqshd0r2oksNTJ+VwgbGfMlq79ykd4FhiEEk23fFbajGt+9ginadXoRk17dg==} engines: {node: '>=8.0.0'} @@ -1747,6 +1776,33 @@ packages: '@opentelemetry/semantic-conventions': 1.0.1 dev: false + /@opentelemetry/sdk-trace-base/1.0.1_@opentelemetry+api@1.0.4: + resolution: {integrity: sha512-JVSAepTpW7dnqfV7XFN0zHj1jXGNd5OcvIGQl76buogqffdgJdgJWQNrOuUJaus56zrOtlzqFH+YtMA9RGEg8w==} + engines: {node: '>=8.0.0'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.1.0' + dependencies: + '@opentelemetry/api': 1.0.4 + '@opentelemetry/core': 1.0.1_@opentelemetry+api@1.0.4 + '@opentelemetry/resources': 1.0.1_@opentelemetry+api@1.0.4 + '@opentelemetry/semantic-conventions': 1.0.1 + dev: false + + /@opentelemetry/sdk-trace-node/1.0.1_@opentelemetry+api@1.0.4: + resolution: {integrity: sha512-0ifT2pEI5aXy14zDDUQkl3ODzY6jjaC1plbxyAuz5BdPxGJzav9vzIJ2BhEwfXDn1LKqpQ5D1Yy+XnNRQpOo3w==} + engines: {node: '>=8.0.0'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.1.0' + dependencies: + '@opentelemetry/api': 1.0.4 + '@opentelemetry/context-async-hooks': 1.0.1_@opentelemetry+api@1.0.4 + '@opentelemetry/core': 1.0.1_@opentelemetry+api@1.0.4 + '@opentelemetry/propagator-b3': 1.0.1_@opentelemetry+api@1.0.4 + '@opentelemetry/propagator-jaeger': 1.0.1_@opentelemetry+api@1.0.4 + '@opentelemetry/sdk-trace-base': 1.0.1_@opentelemetry+api@1.0.4 + semver: 7.3.5 + dev: false + /@opentelemetry/semantic-conventions/0.22.0: resolution: {integrity: sha512-t4fKikazahwNKmwD+CE/icHyuZldWvNMupJhjxdk9T/KxHFx3zCGjHT3MKavwYP6abzgAAm5WwzD1oHlmj7dyg==} engines: {node: '>=8.0.0'} @@ -14816,7 +14872,7 @@ packages: dev: false file:projects/opentelemetry-instrumentation-azure-sdk.tgz: - resolution: {integrity: sha512-QpbQ27v5Vwh319CCySclHolgDDf/5uAAanlMcSlD51Mmdr6y9T+ym/8Y8KQQOy2/RlmfNxREoLkZoeTXS/eu0g==, tarball: file:projects/opentelemetry-instrumentation-azure-sdk.tgz} + resolution: {integrity: sha512-tYTfMG9ScH53m1tT2oK3P8zvFXnmXKVsP+66hK3zZHb0zmGOwyGm1ieOfq5gr8vQrdqfgF6WSL1oyXKfW3JqVA==, tarball: file:projects/opentelemetry-instrumentation-azure-sdk.tgz} name: '@rush-temp/opentelemetry-instrumentation-azure-sdk' version: 0.0.0 dependencies: @@ -14826,6 +14882,8 @@ packages: '@opentelemetry/api': 1.0.4 '@opentelemetry/core': 1.0.1_@opentelemetry+api@1.0.4 '@opentelemetry/instrumentation': 0.27.0_@opentelemetry+api@1.0.4 + '@opentelemetry/sdk-trace-base': 1.0.1_@opentelemetry+api@1.0.4 + '@opentelemetry/sdk-trace-node': 1.0.1_@opentelemetry+api@1.0.4 '@types/chai': 4.3.0 '@types/mocha': 7.0.2 '@types/node': 12.20.43 diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/package.json b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/package.json index 6c0d57c4d9f5..c78fd954e3c8 100644 --- a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/package.json +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/package.json @@ -6,7 +6,8 @@ "main": "dist/index.js", "module": "dist-esm/src/index.js", "browser": { - "./dist-esm/src/instrumentation.js": "./dist-esm/src/instrumentation.browser.js" + "./dist-esm/src/instrumentation.js": "./dist-esm/src/instrumentation.browser.js", + "./dist-esm/test/public/util/tracerProvider.js": "./dist-esm/test/public/util/tracerProvider.browser.js" }, "//metadata": { "constantPaths": [ @@ -86,9 +87,12 @@ }, "devDependencies": { "@azure-tools/test-recorder": "^1.0.0", + "@azure/core-rest-pipeline": "^1.5.1", "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^3.0.0", "@microsoft/api-extractor": "^7.18.11", + "@opentelemetry/sdk-trace-base": "^1.0.1", + "@opentelemetry/sdk-trace-node": "^1.0.1", "@types/chai": "^4.1.6", "@types/mocha": "^7.0.2", "@types/node": "^12.0.0", diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/src/instrumenter.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/src/instrumenter.ts index ec16053bb86c..bcbc6e7c98b0 100644 --- a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/src/instrumenter.ts +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/src/instrumenter.ts @@ -25,7 +25,15 @@ export class OpenTelemetryInstrumenter implements Instrumenter { .getTracer(spanOptions.packageName, spanOptions.packageVersion) .startSpan(name, toSpanOptions(spanOptions)); - const ctx = spanOptions?.tracingContext || context.active(); + let ctx = spanOptions?.tracingContext || context.active(); + + // COMPAT: remove when core-rest-pipeline has upgraded to core-tracing 1.0 + // https://github.com/Azure/azure-sdk-for-js/issues/20567 + const newNamespaceKey = Symbol.for("@azure/core-tracing namespace"); + const oldNamespaceKey = Symbol.for("az.namespace"); + if (!ctx.getValue(oldNamespaceKey) && ctx.getValue(newNamespaceKey)) { + ctx = ctx.setValue(oldNamespaceKey, ctx.getValue(newNamespaceKey)); + } return { span: new OpenTelemetrySpanWrapper(span), diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/internal/node/instrumentation.spec.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/internal/node/instrumentation.spec.ts new file mode 100644 index 000000000000..bf2dd7dca252 --- /dev/null +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/internal/node/instrumentation.spec.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { SpanKind, SpanStatusCode } from "@opentelemetry/api"; +import { TestClient, tracingClientAttributes } from "../../public/util/testClient"; + +import { assert } from "chai"; +import { inMemoryExporter } from "../../public/util/setup"; + +describe("instrumentation end-to-end tests", () => { + beforeEach(() => { + inMemoryExporter.reset(); + }); + + // This is node-only since we use the BasicTracerProvider in the browser + // which does not set up a context manager. Altenatively we can always pull in + // @opentelemetry/sdk-trace-web but it did not feel necessary at this time. + describe("with a configured client", () => { + it("works when using withSpan", async () => { + await new TestClient().exampleOperation(); + const spans = inMemoryExporter.getFinishedSpans(); + assert.lengthOf(spans, 3); + const [coreRestPipeline, inner, outer] = spans; + + // Check parenting chain + assert.equal(coreRestPipeline.parentSpanId, inner.spanContext().spanId); + assert.equal(inner.parentSpanId, outer.spanContext().spanId); + assert.notExists(outer.parentSpanId); + + // Check default span kind + assert.equal(outer.kind, SpanKind.INTERNAL); + + // Check specified span kind + assert.equal(inner.kind, SpanKind.CLIENT); + + // Check status + assert.deepEqual(coreRestPipeline.status, { code: SpanStatusCode.OK }); + assert.deepEqual(inner.status, { code: SpanStatusCode.OK }); + assert.deepEqual(outer.status, { code: SpanStatusCode.OK }); + + // Check instrumentationLibrary + assert.deepEqual(outer.instrumentationLibrary, { + name: tracingClientAttributes.packageName, + version: tracingClientAttributes.packageVersion, + }); + + // Check attributes on all spans + assert.equal(coreRestPipeline.attributes["az.namespace"], tracingClientAttributes.namespace); + assert.equal(inner.attributes["az.namespace"], tracingClientAttributes.namespace); + assert.equal(outer.attributes["az.namespace"], tracingClientAttributes.namespace); + }); + }); +}); diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/instrumenter.spec.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/instrumenter.spec.ts index 4c711efb1acd..36125598f577 100644 --- a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/instrumenter.spec.ts +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/instrumenter.spec.ts @@ -4,16 +4,16 @@ import { OpenTelemetryInstrumenter, propagator } from "../../src/instrumenter"; import { SpanKind, context, trace } from "@opentelemetry/api"; import { TracingSpan, TracingSpanKind } from "@azure/core-tracing"; -import { resetTracer, setTracer } from "./util/testTracerProvider"; + import { Context } from "mocha"; import { OpenTelemetrySpanWrapper } from "../../src/spanWrapper"; -import { TestSpan } from "./util/testSpan"; -import { TestTracer } from "./util/testTracer"; +import { Span } from "@opentelemetry/sdk-trace-base"; import { assert } from "chai"; +import { inMemoryExporter } from "./util/setup"; import sinon from "sinon"; -function unwrap(span: TracingSpan): TestSpan { - return (span as OpenTelemetrySpanWrapper).unwrap() as TestSpan; +function unwrap(span: TracingSpan): Span { + return (span as OpenTelemetrySpanWrapper).unwrap() as Span; } describe("OpenTelemetryInstrumenter", () => { @@ -26,7 +26,7 @@ describe("OpenTelemetryInstrumenter", () => { it("uses the passed in context if it exists", () => { const propagationSpy = sinon.spy(propagator); - const span = new TestTracer().startSpan("test"); + const span = trace.getTracer("test").startSpan("test"); const tracingContext = trace.setSpan(context.active(), span); instrumenter.createRequestHeaders(tracingContext); assert.isTrue(propagationSpy.inject.calledWith(tracingContext)); @@ -40,24 +40,20 @@ describe("OpenTelemetryInstrumenter", () => { }); }); - // TODO: the following still uses existing test support for OTel. - // Once the new APIs are available we should move away from those. describe("#startSpan", () => { - let tracer: TestTracer; const packageName = "test-package"; const packageVersion = "test-version"; - beforeEach(() => { - tracer = setTracer(tracer); - }); - afterEach(() => { - resetTracer(); + beforeEach(() => { + inMemoryExporter.reset(); }); it("returns a newly started TracingSpan", () => { const { span } = instrumenter.startSpan("test", { packageName, packageVersion }); + span.end(); const otSpan = unwrap(span); - assert.equal(otSpan, tracer.getActiveSpans()[0]); + assert.lengthOf(inMemoryExporter.getFinishedSpans(), 1); + assert.equal(otSpan, inMemoryExporter.getFinishedSpans()[0]); assert.equal(otSpan.kind, SpanKind.INTERNAL); }); @@ -90,6 +86,19 @@ describe("OpenTelemetryInstrumenter", () => { assert.equal(trace.getSpan(tracingContext), unwrap(span)); }); + + it("adds az.namespace as a context attribute for compatibility", async () => { + const currentContext = context + .active() + .setValue(Symbol.for("@azure/core-tracing namespace"), "test-namespace"); + + const { tracingContext } = instrumenter.startSpan("test", { + tracingContext: currentContext, + packageName, + }); + + assert.equal(tracingContext.getValue(Symbol.for("az.namespace")), "test-namespace"); + }); }); describe("when a context is not provided", () => { @@ -208,7 +217,7 @@ describe("OpenTelemetryInstrumenter", () => { // Function syntax instrumenter.withContext(context.active(), function (this: any) { - assert.isUndefined(this); + assert.notExists(this); }); instrumenter.withContext( context.active(), diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/spanWrapper.spec.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/spanWrapper.spec.ts index 0f3ae47dcba7..57db7f0dded2 100644 --- a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/spanWrapper.spec.ts +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/spanWrapper.spec.ts @@ -1,26 +1,26 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { SpanStatusCode, trace } from "@opentelemetry/api"; + import { OpenTelemetrySpanWrapper } from "../../src/spanWrapper"; -import { SpanStatusCode } from "@opentelemetry/api"; -import { TestSpan } from "./util/testSpan"; -import { TestTracer } from "./util/testTracer"; import { assert } from "chai"; +import { getExportedSpan } from "./util/testHelpers"; +import { inMemoryExporter } from "./util/setup"; describe("OpenTelemetrySpanWrapper", () => { - let otSpan: TestSpan; let span: OpenTelemetrySpanWrapper; beforeEach(() => { - otSpan = new TestTracer().startSpan("test"); - span = new OpenTelemetrySpanWrapper(otSpan); + span = new OpenTelemetrySpanWrapper(trace.getTracer("test").startSpan("test-span")); + inMemoryExporter.reset(); }); describe("#setStatus", () => { describe("with a successful status", () => { it("sets the status on the span", () => { span.setStatus({ status: "success" }); - + const otSpan = getExportedSpan(span); assert.deepEqual(otSpan.status, { code: SpanStatusCode.OK }); }); }); @@ -28,7 +28,7 @@ describe("OpenTelemetrySpanWrapper", () => { describe("with an error", () => { it("sets the failed status on the span", () => { span.setStatus({ status: "error" }); - + const otSpan = getExportedSpan(span); assert.deepEqual(otSpan.status, { code: SpanStatusCode.ERROR }); }); @@ -36,7 +36,11 @@ describe("OpenTelemetrySpanWrapper", () => { const error = new Error("test"); span.setStatus({ status: "error", error }); - assert.deepEqual(otSpan.exception, error); + const otSpan = getExportedSpan(span); + assert.lengthOf(otSpan.events, 1); + const exception = otSpan.events[0]; + assert.equal(exception.name, "exception"); + assert.equal(exception.attributes!["exception.message"], error.message); }); }); }); @@ -46,48 +50,42 @@ describe("OpenTelemetrySpanWrapper", () => { span.setAttribute("test", "value"); span.setAttribute("array", ["value"]); + const otSpan = getExportedSpan(span); assert.deepEqual(otSpan.attributes, { test: "value", array: ["value"] }); }); it("ignores null", () => { span.setAttribute("test", null); + const otSpan = getExportedSpan(span); assert.isEmpty(otSpan.attributes); }); it("ignores undefined", () => { span.setAttribute("test", undefined); + const otSpan = getExportedSpan(span); assert.isEmpty(otSpan.attributes); }); }); - describe("#end", () => { - it("ends the wrapped span", () => { - span.end(); - - assert.isTrue(otSpan.endCalled); - }); - }); - describe("#recordException", () => { it("sets the error on the wrapped span", () => { const error = new Error("test"); span.recordException(error); - assert.deepEqual(otSpan.exception, error); - }); - it("does not change the status", () => { - const error = "test"; - span.recordException(error); - - assert.deepEqual(otSpan.status, { code: SpanStatusCode.UNSET }); + const otSpan = getExportedSpan(span); + assert.lengthOf(otSpan.events, 1); + const exception = otSpan.events[0]; + assert.equal(exception.name, "exception"); + assert.equal(exception.attributes!["exception.message"], error.message); }); }); describe("#isRecording", () => { it("returns the value of the wrapped span", () => { - assert.equal(span.isRecording(), otSpan.isRecording()); + // Our setup creates recording spans + assert.isTrue(span.isRecording()); }); }); }); diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/setup.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/setup.ts new file mode 100644 index 000000000000..f2c25b9b0482 --- /dev/null +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/setup.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base"; + +import { OpenTelemetryInstrumenter } from "../../../src/instrumenter"; +import { useInstrumenter } from "@azure/core-tracing"; + +import { tracerProvider } from "./tracerProvider"; + +// Setup all the necessary instrumenters, exporters, etc. +export const inMemoryExporter = new InMemorySpanExporter(); +tracerProvider.addSpanProcessor(new SimpleSpanProcessor(inMemoryExporter)); +tracerProvider.register(); +useInstrumenter(new OpenTelemetryInstrumenter()); diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testClient.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testClient.ts new file mode 100644 index 000000000000..0309ebf6d5cd --- /dev/null +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testClient.ts @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { OperationTracingOptions, TracingClient, createTracingClient } from "@azure/core-tracing"; +import { + Pipeline, + PipelineResponse, + createDefaultHttpClient, + createHttpHeaders, + createPipelineFromOptions, +} from "@azure/core-rest-pipeline"; + +import { SDK_VERSION } from "../../../src/constants"; + +/** + * A partial interface compatible with OperationOptions. + */ +export interface Options { + tracingOptions?: OperationTracingOptions; +} + +/** + * Options passed to {@link createTracingClient}, exported for validation. + */ +export const tracingClientAttributes = { + namespace: "Microsoft.Test", + packageName: "@azure/opentelemetry-instrumentation-azure-sdk", + packageVersion: SDK_VERSION, +}; + +/** + * A small client that uses core-rest-pipeline to make requests. + */ +export class TestClient { + tracingClient: TracingClient; + pipeline: Pipeline; + + constructor() { + this.tracingClient = createTracingClient(tracingClientAttributes); + this.pipeline = createPipelineFromOptions({}); + } + + /** + * The entrypoint of this client, which the tests will call into + */ + exampleOperation(options: Options = {}): Promise { + return this.tracingClient.withSpan("TestClient.outer", options, (updatedOptions) => + this.generatedClientOperation(updatedOptions) + ); + } + + private generatedClientOperation(options: Options = {}): Promise { + return this.tracingClient.withSpan( + "TestClient.inner", + options, + (updatedOptions) => { + return this.pipeline.sendRequest(createDefaultHttpClient(), { + headers: createHttpHeaders(), + method: "GET", + requestId: "1", + timeout: 10 * 1000, // 10s + url: "https://example.org", + withCredentials: false, + ...updatedOptions, + }); + }, + { spanKind: "client" } + ); + } +} diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testHelpers.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testHelpers.ts new file mode 100644 index 000000000000..ac2983e7a3ba --- /dev/null +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testHelpers.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { OpenTelemetrySpanWrapper } from "../../../src/spanWrapper"; +import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; +import { assert } from "chai"; +import { inMemoryExporter } from "./setup"; + +export function getExportedSpan(span: OpenTelemetrySpanWrapper): ReadableSpan { + // Also tests that we end the underlying span by proxy + span.end(); + assert.lengthOf(inMemoryExporter.getFinishedSpans(), 1); + return inMemoryExporter.getFinishedSpans()[0]; +} diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testSpan.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testSpan.ts deleted file mode 100644 index c67099593047..000000000000 --- a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testSpan.ts +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { - Link, - Span, - SpanAttributeValue, - SpanAttributes, - SpanContext, - SpanKind, - SpanStatus, - SpanStatusCode, - TimeInput, - Tracer, -} from "@opentelemetry/api"; - -/** - * A mock span useful for testing. - */ -export class TestSpan implements Span { - /** - * The Span's current name - */ - name: string; - - /** - * The Span's current status - */ - status: SpanStatus; - - /** - * The Span's kind - */ - kind: SpanKind; - - /** - * True if end() has been called on the Span - */ - endCalled: boolean; - - /** - * The start time of the Span - */ - readonly startTime: TimeInput; - - /** - * The id of the parent Span, if any. - */ - readonly parentSpanId?: string; - - /** - * Known attributes, if any. - */ - readonly attributes: SpanAttributes; - - private _context: SpanContext; - private readonly _tracer: Tracer; - - /** - * The recorded exception, if any. - */ - exception?: Error; - - /** - * Any links provided when creating this span. - */ - links: Link[]; - - /** - * Starts a new Span. - * @param parentTracer- The tracer that created this Span - * @param name - The name of the span. - * @param context - The SpanContext this span belongs to - * @param kind - The SpanKind of this Span - * @param parentSpanId - The identifier of the parent Span - * @param startTime - The startTime of the event (defaults to now) - */ - constructor( - parentTracer: Tracer, - name: string, - context: SpanContext, - kind: SpanKind, - parentSpanId?: string, - startTime: TimeInput = Date.now(), - attributes: SpanAttributes = {}, - links: Link[] = [] - ) { - this._tracer = parentTracer; - this.name = name; - this.kind = kind; - this.startTime = startTime; - this.parentSpanId = parentSpanId; - this.status = { - code: SpanStatusCode.UNSET, - }; - this.endCalled = false; - this._context = context; - this.attributes = attributes; - this.links = links; - } - - /** - * Returns the Tracer that created this Span - */ - tracer(): Tracer { - return this._tracer; - } - - /** - * Returns the SpanContext associated with this Span. - */ - spanContext(): SpanContext { - return this._context; - } - - /** - * Marks the end of Span execution. - * @param _endTime - The time to use as the Span's end time. Defaults to - * the current time. - */ - end(_endTime?: number): void { - this.endCalled = true; - } - - /** - * Sets a status on the span. Overrides the default of SpanStatusCode.OK. - * @param status - The status to set. - */ - setStatus(status: SpanStatus): this { - this.status = status; - return this; - } - - /** - * Returns whether this span will be recorded - */ - isRecording(): boolean { - return true; - } - - /** - * Sets an attribute on the Span - * @param key - The attribute key - * @param value - The attribute value - */ - setAttribute(key: string, value: SpanAttributeValue): this { - this.attributes[key] = value; - return this; - } - - /** - * Sets attributes on the Span - * @param attributes - The attributes to add - */ - setAttributes(attributes: SpanAttributes): this { - for (const key of Object.keys(attributes)) { - this.attributes[key] = attributes[key]; - } - return this; - } - - addEvent(): this { - throw new Error("Method not implemented."); - } - recordException(exception: Error): void { - this.exception = exception; - } - updateName(): this { - throw new Error("Method not implemented."); - } -} diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testTracer.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testTracer.ts deleted file mode 100644 index 09af6ccf7092..000000000000 --- a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testTracer.ts +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { - Context, - SpanContext, - SpanKind, - SpanOptions, - TraceFlags, - Tracer, - context, - trace, -} from "@opentelemetry/api"; - -import { TestSpan } from "./testSpan"; - -/** - * Simple representation of a Span that only has name and child relationships. - * Children should be arranged in the order they were created. - */ -export interface SpanGraphNode { - /** - * The Span name - */ - name: string; - /** - * All child Spans of this Span - */ - children: SpanGraphNode[]; -} - -/** - * Contains all the spans for a particular TraceID - * starting at unparented roots - */ -export interface SpanGraph { - /** - * All Spans without a parentSpanId - */ - roots: SpanGraphNode[]; -} - -/** - * A mock tracer useful for testing - */ -export class TestTracer implements Tracer { - constructor(public name?: string, public version?: string) {} - private traceIdCounter = 0; - private getNextTraceId(): string { - this.traceIdCounter++; - return this.traceIdCounter.toString().padStart(32, "0"); - } - - private spanIdCounter = 0; - private getNextSpanId(): string { - this.spanIdCounter++; - return this.spanIdCounter.toString().padStart(16, "0"); - } - - private rootSpans: TestSpan[] = []; - private knownSpans: TestSpan[] = []; - - /** - * Returns all Spans that were created without a parent - */ - getRootSpans(): TestSpan[] { - return this.rootSpans; - } - - /** - * Returns all Spans this Tracer knows about - */ - getKnownSpans(): TestSpan[] { - return this.knownSpans; - } - - /** - * Returns all Spans where end() has not been called - */ - getActiveSpans(): TestSpan[] { - return this.knownSpans.filter((span) => { - return !span.endCalled; - }); - } - - /** - * Return all Spans for a particular trace, grouped by their - * parent Span in a tree-like structure - * @param traceId - The traceId to return the graph for - */ - getSpanGraph(traceId: string): SpanGraph { - const traceSpans = this.knownSpans.filter((span) => { - return span.spanContext().traceId === traceId; - }); - - const roots: SpanGraphNode[] = []; - const nodeMap: Map = new Map(); - - for (const span of traceSpans) { - const spanId = span.spanContext().spanId; - const node: SpanGraphNode = { - name: span.name, - children: [], - }; - nodeMap.set(spanId, node); - if (span.parentSpanId) { - const parent = nodeMap.get(span.parentSpanId); - if (!parent) { - throw new Error( - `Span with name ${node.name} has an unknown parentSpan with id ${span.parentSpanId}` - ); - } - parent.children.push(node); - } else { - roots.push(node); - } - } - - return { - roots, - }; - } - - /** - * Starts a new Span. - * @param name - The name of the span. - * @param options - The SpanOptions used during Span creation. - */ - startSpan(name: string, options?: SpanOptions, currentContext?: Context): TestSpan { - const parentContext = trace.getSpanContext(currentContext || context.active()); - - let traceId: string; - let isRootSpan = false; - - if (parentContext && parentContext.traceId) { - traceId = parentContext.traceId; - } else { - traceId = this.getNextTraceId(); - isRootSpan = true; - } - - const spanContext: SpanContext = { - traceId, - spanId: this.getNextSpanId(), - traceFlags: TraceFlags.NONE, - }; - const span = new TestSpan( - this, - name, - spanContext, - options?.kind || SpanKind.INTERNAL, - parentContext ? parentContext.spanId : undefined, - options?.startTime, - options?.attributes, - options?.links - ); - this.knownSpans.push(span); - if (isRootSpan) { - this.rootSpans.push(span); - } - return span; - } - - /** - * Added to support testing. We do not support `startActiveSpan` in general because it uses async_hooks - * which is experimental. Only added to support TestTracerProvider compatibility with OTel Tracers. - */ - startActiveSpan(): never { - throw new Error("Method not implemented."); - } -} diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testTracerProvider.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testTracerProvider.ts deleted file mode 100644 index cca68d106517..000000000000 --- a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/testTracerProvider.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { TracerProvider, trace } from "@opentelemetry/api"; -import { TestTracer } from "./testTracer"; - -export class TestTracerProvider implements TracerProvider { - private tracer = new TestTracer(); - - getTracer(): TestTracer { - return this.tracer; - } - - register(): boolean { - return trace.setGlobalTracerProvider(this); - } - - disable(): void { - trace.disable(); - } - - setTracer(tracer: TestTracer): void { - this.tracer = tracer; - } -} - -let tracerProvider: TestTracerProvider; - -export function setTracer(tracer?: TestTracer): TestTracer { - resetTracer(); - tracerProvider = new TestTracerProvider(); - tracerProvider.register(); - if (tracer) { - tracerProvider.setTracer(tracer); - } - return tracerProvider.getTracer(); -} - -export function resetTracer(): void { - tracerProvider?.disable(); -} diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/tracerProvider.browser.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/tracerProvider.browser.ts new file mode 100644 index 000000000000..d901537439f7 --- /dev/null +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/tracerProvider.browser.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base"; + +// Use BasicTracerProvider in the browser. +export const tracerProvider = new BasicTracerProvider(); diff --git a/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/tracerProvider.ts b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/tracerProvider.ts new file mode 100644 index 000000000000..24fb60cbe5b3 --- /dev/null +++ b/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/test/public/util/tracerProvider.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node"; + +export const tracerProvider = new NodeTracerProvider();