Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[instrumentation] - Suppress tracing using environment variables #20776

Merged
merged 13 commits into from
Mar 15, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

### Features Added

- Added the ability to disable Azure SDK Spans from being recorded by setting the `AZURE_TRACING_DISABLED` environment variable to true.
- Added support for `AZURE_HTTP_TRACING_DISABLED` environment variable which allows disabling all children of our core HTTP spans from being recorded.

### Breaking Changes

### Bugs Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"//metadata": {
"constantPaths": [
{
"path": "src/constants.ts",
"path": "src/configuration.ts",
"prefix": "SDK_VERSION"
}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

export const SDK_VERSION: string = "1.0.0-beta.2";

/**
* @internal
*
* Keys of known environment variables we look up.
*/
export type KnownEnvironmentKey = "AZURE_HTTP_TRACING_DISABLED" | "AZURE_TRACING_DISABLED";

/**
* @internal
*
* Cached values of environment variables that were fetched.
*/
export const environmentCache = new Map<KnownEnvironmentKey, string | undefined>();

/**
* Converts an environment variable to Boolean.
* the strings "false" and "0" are treated as falsy values.
*
* @internal
*/
export function envVarToBoolean(key: KnownEnvironmentKey): boolean {
maorleger marked this conversation as resolved.
Show resolved Hide resolved
if (!environmentCache.has(key)) {
loadEnvironmentVariable(key);
}
const value = (environmentCache.get(key) ?? "").toLowerCase();
return value !== "false" && value !== "0" && Boolean(value);
}

function loadEnvironmentVariable(key: KnownEnvironmentKey): void {
if (typeof process !== "undefined" && process.env) {
const rawValue = process.env[key] ?? process.env[key.toLowerCase()];
environmentCache.set(key, rawValue);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
InstrumentationConfig,
} from "@opentelemetry/instrumentation";
import { OpenTelemetryInstrumenter } from "./instrumenter";
import { SDK_VERSION } from "./constants";
import { SDK_VERSION } from "./configuration";
import { useInstrumenter } from "@azure/core-tracing";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
InstrumentationNodeModuleDefinition,
} from "@opentelemetry/instrumentation";
import { OpenTelemetryInstrumenter } from "./instrumenter";
import { SDK_VERSION } from "./constants";
import { SDK_VERSION } from "./configuration";

/**
* Configuration options that can be passed to {@link createAzureSdkInstrumentation} function.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import {
INVALID_SPAN_CONTEXT,
Span,
context,
defaultTextMapGetter,
defaultTextMapSetter,
trace,
} from "@opentelemetry/api";
import {
Instrumenter,
InstrumenterSpanOptions,
TracingContext,
TracingSpan,
} from "@azure/core-tracing";
import { context, defaultTextMapGetter, defaultTextMapSetter, trace } from "@opentelemetry/api";
import { W3CTraceContextPropagator, suppressTracing } from "@opentelemetry/core";

import { OpenTelemetrySpanWrapper } from "./spanWrapper";
import { W3CTraceContextPropagator } from "@opentelemetry/core";
import { envVarToBoolean } from "./configuration";
import { toSpanOptions } from "./transformations";

// While default propagation is user-configurable, Azure services always use the W3C implementation.
Expand All @@ -21,11 +29,23 @@ export class OpenTelemetryInstrumenter implements Instrumenter {
name: string,
spanOptions: InstrumenterSpanOptions
): { span: TracingSpan; tracingContext: TracingContext } {
const span = trace
.getTracer(spanOptions.packageName, spanOptions.packageVersion)
.startSpan(name, toSpanOptions(spanOptions));

let ctx = spanOptions?.tracingContext || context.active();
let span: Span;

if (envVarToBoolean("AZURE_TRACING_DISABLED")) {
// disable only our spans but not any downstream spans
span = trace.wrapSpanContext(INVALID_SPAN_CONTEXT);
maorleger marked this conversation as resolved.
Show resolved Hide resolved
} else {
// Create our span
span = trace
.getTracer(spanOptions.packageName, spanOptions.packageVersion)
.startSpan(name, toSpanOptions(spanOptions), ctx);

if (envVarToBoolean("AZURE_HTTP_TRACING_DISABLED") && name.toUpperCase().startsWith("HTTP")) {
// disable downstream spans
ctx = suppressTracing(ctx);
maorleger marked this conversation as resolved.
Show resolved Hide resolved
}
}

// COMPAT: remove when core-rest-pipeline has upgraded to core-tracing 1.0
// https://github.com/Azure/azure-sdk-for-js/issues/20567
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { KnownEnvironmentKey, envVarToBoolean, environmentCache } from "../../../src/configuration";

import { assert } from "chai";

describe("#envVarToBoolean", () => {
const key = "FOO" as KnownEnvironmentKey;

it("is false when env var is blank or missing", () => {
environmentCache.set(key, "");
assert.isFalse(envVarToBoolean(key));
environmentCache.delete(key);
assert.isFalse(envVarToBoolean(key));
});

it("is false when env var is 'false'", () => {
environmentCache.set(key, "false");
assert.isFalse(envVarToBoolean(key));
environmentCache.set(key, "False");
assert.isFalse(envVarToBoolean(key));
environmentCache.set(key, "FALSE");
assert.isFalse(envVarToBoolean(key));
});

it("is false when env var is 0", () => {
environmentCache.set(key, "0");
assert.isFalse(envVarToBoolean(key));
});

it("is true otherwise", () => {
environmentCache.set(key, "true");
assert.isTrue(envVarToBoolean(key));
environmentCache.set(key, "1");
assert.isTrue(envVarToBoolean(key));
});

it("reads from the environment on first access", () => {
const keyName = `caches-on-first-access-${Date.now()}`;
process.env[keyName] = "true";
assert.isTrue(envVarToBoolean(keyName as KnownEnvironmentKey));
delete process.env[keyName];
});

it("supports lowercase and uppercase search", () => {
const keyName = `is-case-insensitive-${Date.now()}`;
process.env[keyName] = "true";
assert.isTrue(envVarToBoolean(keyName.toUpperCase() as KnownEnvironmentKey));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { Context } from "mocha";
import { OpenTelemetrySpanWrapper } from "../../src/spanWrapper";
import { Span } from "@opentelemetry/sdk-trace-base";
import { assert } from "chai";
import { environmentCache } from "../../src/configuration";
import { inMemoryExporter } from "./util/setup";
import { isTracingSuppressed } from "@opentelemetry/core";
import sinon from "sinon";

function unwrap(span: TracingSpan): Span {
Expand Down Expand Up @@ -197,6 +199,70 @@ describe("OpenTelemetryInstrumenter", () => {
assert.deepEqual(links[0].context, trace.getSpan(linkedContext!)?.spanContext());
});
});

describe("environment variables", () => {
afterEach(() => {
environmentCache.clear();
});

describe("when AZURE_TRACING_DISABLED is set", () => {
it("suppresses tracing for our spans", () => {
environmentCache.set("AZURE_TRACING_DISABLED", "1");
const { tracingContext, span } = instrumenter.startSpan("test", {
packageName,
});
assert.isFalse(span.isRecording());
assert.isFalse(isTracingSuppressed(tracingContext));
});
});

describe("when AZURE_HTTP_TRACING_DISABLED is set", () => {
beforeEach(() => {
environmentCache.set("AZURE_HTTP_TRACING_DISABLED", "1");
});

it("suppresses tracing for downstream spans", () => {
const { span, tracingContext } = instrumenter.startSpan("HTTP POST", {
packageName,
});

assert.isTrue(span.isRecording());
assert.isTrue(isTracingSuppressed(tracingContext));
});

it("does not suppress internal spans", () => {
const { span, tracingContext } = instrumenter.startSpan("foo", {
packageName,
});

assert.isTrue(span.isRecording());
assert.isFalse(isTracingSuppressed(tracingContext));
});
});

describe("when both AZURE_TRACING_DISABLED and AZURE_HTTP_TRACING_DISABLED are set", () => {
beforeEach(() => {
environmentCache.set("AZURE_TRACING_DISABLED", "true");
environmentCache.set("AZURE_HTTP_TRACING_DISABLED", "True");
});

it("creates a non-recording span", () => {
const { span } = instrumenter.startSpan("foo", {
packageName,
});

assert.isFalse(span.isRecording());
});

it("does not suppress downstream spans", () => {
const { tracingContext } = instrumenter.startSpan("foo", {
packageName,
});

assert.isFalse(isTracingSuppressed(tracingContext));
});
});
});
});

describe("#withContext", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import { InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";

import { OpenTelemetryInstrumenter } from "../../../src/instrumenter";
import { useInstrumenter } from "@azure/core-tracing";

import { tracerProvider } from "./tracerProvider";
import { useInstrumenter } from "@azure/core-tracing";

// Setup all the necessary instrumenters, exporters, etc.
export const inMemoryExporter = new InMemorySpanExporter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
createPipelineFromOptions,
} from "@azure/core-rest-pipeline";

import { SDK_VERSION } from "../../../src/constants";
import { SDK_VERSION } from "../../../src/configuration";

/**
* A partial interface compatible with OperationOptions.
Expand Down