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

Feat support otel sdk disabled env #3485

Merged
402 changes: 202 additions & 200 deletions experimental/CHANGELOG.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ This is an alternative to programmatically configuring an exporter or span proce
| OTEL_EXPORTER_OTLP_TRACES_PROTOCOL | The transport protocol to use on OTLP trace requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |
| OTEL_EXPORTER_OTLP_METRICS_PROTOCOL | The transport protocol to use on OTLP metric requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |

### Disable the SDK from the environment

Disable the SDK by setting the `OTEL_SDK_DISABLED` environment variable to `true`.
legendecas marked this conversation as resolved.
Show resolved Hide resolved


Additionally, you can specify other applicable environment variables that apply to each exporter such as the following:

- [OTLP exporter environment configuration](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#configuration-options)
Expand Down
17 changes: 17 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { NodeSDKConfiguration } from './types';
import { TracerProviderWithEnvExporters } from './TracerProviderWithEnvExporter';
import { getEnv } from '@opentelemetry/core';

/** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */

Expand Down Expand Up @@ -71,10 +72,18 @@ export class NodeSDK {
private _meterProvider?: MeterProvider;
private _serviceName?: string;

private _disabled?: boolean;

/**
* Create a new NodeJS SDK instance
*/
public constructor(configuration: Partial<NodeSDKConfiguration> = {}) {
if (getEnv().OTEL_SDK_DISABLED) {
this._disabled = true;
// Functions with possible side-effects are set
// to no-op via the _disabled flag
}

this._resource = configuration.resource ?? new Resource({});
this._resourceDetectors = configuration.resourceDetectors ?? [
envDetector,
Expand Down Expand Up @@ -175,6 +184,10 @@ export class NodeSDK {

/** Detect resource attributes */
public async detectResources(): Promise<void> {
if (this._disabled) {
return;
}

const internalConfig: ResourceDetectionConfig = {
detectors: this._resourceDetectors,
};
Expand All @@ -191,6 +204,10 @@ export class NodeSDK {
* Once the SDK has been configured, call this method to construct SDK components and register them with the OpenTelemetry API.
*/
public async start(): Promise<void> {
if (this._disabled) {
return;
}

if (this._autoDetectResources) {
await this.detectResources();
}
Expand Down
72 changes: 72 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,78 @@ describe('Node SDK', () => {
delete process.env.OTEL_RESOURCE_ATTRIBUTES;
});
});

describe('A disabled SDK should be no-op', () => {
beforeEach(() => {
env.OTEL_SDK_DISABLED = 'true';
});

afterEach(() => {
delete env.OTEL_SDK_DISABLED;
});

it('should not register a trace provider', async () => {
const sdk = new NodeSDK({});
await sdk.start();

assert.strictEqual(
(trace.getTracerProvider() as ProxyTracerProvider).getDelegate(),
delegate,
'sdk.start() should not change the global tracer provider'
);

await sdk.shutdown();
});

it('should not register a meter provider if a reader is provided', async () => {
const exporter = new ConsoleMetricExporter();
const metricReader = new PeriodicExportingMetricReader({
exporter: exporter,
exportIntervalMillis: 100,
exportTimeoutMillis: 100,
});

const sdk = new NodeSDK({
metricReader: metricReader,
autoDetectResources: false,
});
await sdk.start();

assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider));

await sdk.shutdown();
});

describe('detectResources should be no-op', async () => {
beforeEach(() => {
process.env.OTEL_RESOURCE_ATTRIBUTES =
'service.instance.id=627cc493,service.name=my-service,service.namespace=default,service.version=0.0.1';
});

afterEach(() => {
delete process.env.OTEL_RESOURCE_ATTRIBUTES;
});

it('detectResources will not read resources from env or manually', async () => {
const sdk = new NodeSDK({
autoDetectResources: true,
resourceDetectors: [
processDetector,
{
async detect(): Promise<Resource> {
return new Resource({ customAttr: 'someValue' });
},
},
envDetector,
],
});
await sdk.detectResources();
const resource = sdk['_resource'];

assert.deepStrictEqual(resource, Resource.empty());
});
});
});
});

describe('setup exporter from env', () => {
Expand Down
39 changes: 37 additions & 2 deletions packages/opentelemetry-core/src/utils/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ const DEFAULT_LIST_SEPARATOR = ',';
* Environment interface to define all names
*/

const ENVIRONMENT_BOOLEAN_KEYS = ['OTEL_SDK_DISABLED'] as const;

type ENVIRONMENT_BOOLEANS = {
[K in typeof ENVIRONMENT_BOOLEAN_KEYS[number]]?: boolean;
};

function isEnvVarABoolean(key: unknown): key is keyof ENVIRONMENT_BOOLEANS {
return (
ENVIRONMENT_BOOLEAN_KEYS.indexOf(key as keyof ENVIRONMENT_BOOLEANS) > -1
);
}

const ENVIRONMENT_NUMBERS_KEYS = [
'OTEL_BSP_EXPORT_TIMEOUT',
'OTEL_BSP_MAX_EXPORT_BATCH_SIZE',
Expand Down Expand Up @@ -107,7 +119,8 @@ export type ENVIRONMENT = {
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL?: string;
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL?: string;
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE?: string;
} & ENVIRONMENT_NUMBERS &
} & ENVIRONMENT_BOOLEANS &
ENVIRONMENT_NUMBERS &
ENVIRONMENT_LISTS;

export type RAW_ENVIRONMENT = {
Expand All @@ -122,6 +135,7 @@ export const DEFAULT_ATTRIBUTE_COUNT_LIMIT = 128;
* Default environment variables
*/
export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
OTEL_SDK_DISABLED: false,
CONTAINER_NAME: '',
ECS_CONTAINER_METADATA_URI_V4: '',
ECS_CONTAINER_METADATA_URI: '',
Expand Down Expand Up @@ -182,6 +196,25 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: 'cumulative',
};

/**
* @param key
* @param environment
* @param values
*/
function parseBoolean(
key: keyof ENVIRONMENT_BOOLEANS,
environment: ENVIRONMENT,
values: RAW_ENVIRONMENT
) {
if (typeof values[key] === 'undefined') {
return;
}

const value = String(values[key]);
// support case-insensitive "true"
environment[key] = value.toLowerCase() === 'true';
}

/**
* Parses a variable as number with number validation
* @param name
Expand Down Expand Up @@ -277,7 +310,9 @@ export function parseEnvironment(values: RAW_ENVIRONMENT): ENVIRONMENT {
break;

default:
if (isEnvVarANumber(key)) {
if (isEnvVarABoolean(key)) {
parseBoolean(key, environment, values);
} else if (isEnvVarANumber(key)) {
parseNumber(key, environment, values);
} else if (isEnvVarAList(key)) {
parseStringList(key, environment, values);
Expand Down
23 changes: 23 additions & 0 deletions packages/opentelemetry-core/test/utils/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ describe('environment', () => {
HOSTNAME: 'hostname',
KUBERNETES_SERVICE_HOST: 'https://k8s.host/',
NAMESPACE: 'namespace',
OTEL_SDK_DISABLED: 'true',
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 40,
OTEL_BSP_SCHEDULE_DELAY: 50,
OTEL_EXPORTER_JAEGER_AGENT_HOST: 'host.domain.com',
Expand All @@ -98,6 +99,7 @@ describe('environment', () => {
});
const env = getEnv();
assert.deepStrictEqual(env.OTEL_NO_PATCH_MODULES, ['a', 'b', 'c']);
assert.strictEqual(env.OTEL_SDK_DISABLED, true);
assert.strictEqual(env.OTEL_LOG_LEVEL, DiagLogLevel.ERROR);
assert.strictEqual(env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, 40);
assert.strictEqual(env.OTEL_ATTRIBUTE_COUNT_LIMIT, 50);
Expand Down Expand Up @@ -134,6 +136,27 @@ describe('environment', () => {
assert.strictEqual(env.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, 12000);
});

it('should parse OTEL_SDK_DISABLED truthy value despite casing', () => {
mockEnvironment({
OTEL_SDK_DISABLED: 'TrUe',
});
const env = getEnv();
assert.strictEqual(env.OTEL_SDK_DISABLED, true);
legendecas marked this conversation as resolved.
Show resolved Hide resolved
});

describe('OTEL_SDK_DISABLED falsy values', () => {
const falsyValues = ['False', ''];
for (const falsyValue of falsyValues) {
it(`should parse falsy value: ${falsyValue}`, () => {
mockEnvironment({
OTEL_SDK_DISABLED: falsyValue,
});
const env = getEnv();
assert.strictEqual(env.OTEL_SDK_DISABLED, false);
});
}
});

it('should parse OTEL_LOG_LEVEL despite casing', () => {
mockEnvironment({
OTEL_LOG_LEVEL: 'waRn',
Expand Down