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: add OTEL_LOG_LEVEL env var #974

Merged
merged 5 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/opentelemetry-core/src/common/ConsoleLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

import { Logger } from '@opentelemetry/api';
import { LogLevel } from './types';
import { getEnv } from '../platform';

export class ConsoleLogger implements Logger {
constructor(level: LogLevel = LogLevel.INFO) {
constructor(level: LogLevel = getEnv().OTEL_LOG_LEVEL) {
if (level >= LogLevel.DEBUG) {
this.debug = (...args) => {
console.debug(...args);
Expand Down
6 changes: 6 additions & 0 deletions packages/opentelemetry-core/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export enum LogLevel {
DEBUG,
}

/**
* This is equivalent to:
* type LogLevelString = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
*/
export type LogLevelString = keyof typeof LogLevel;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool you can do this!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was really happy to stumble upon it here https://www.typescriptlang.org/docs/handbook/enums.html#enums-at-compile-time 😄


/**
* This interface defines a fallback to read a timeOrigin when it is not available on performance.timeOrigin,
* this happens for example on Safari Mac
Expand Down
40 changes: 37 additions & 3 deletions packages/opentelemetry-core/src/utils/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export interface ENVIRONMENT {
}

const ENVIRONMENT_NUMBERS: Partial<keyof ENVIRONMENT>[] = [
'OTEL_LOG_LEVEL',
'OTEL_SAMPLING_PROBABILITY',
];

Expand All @@ -37,7 +36,7 @@ const ENVIRONMENT_NUMBERS: Partial<keyof ENVIRONMENT>[] = [
*/
export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
OTEL_NO_PATCH_MODULES: '',
OTEL_LOG_LEVEL: LogLevel.ERROR,
OTEL_LOG_LEVEL: LogLevel.INFO,
obecny marked this conversation as resolved.
Show resolved Hide resolved
OTEL_SAMPLING_PROBABILITY: 1,
};

Expand All @@ -64,6 +63,41 @@ function parseNumber(
}
}

/**
* Environmentally sets log level if valid log level string is provided
* @param key
* @param environment
* @param values
*/
function setLogLevelFromEnv(
key: keyof ENVIRONMENT,
environment: ENVIRONMENT_MAP | ENVIRONMENT,
values: ENVIRONMENT_MAP
) {
const value = values[key];
switch (typeof value === 'string' ? value.toUpperCase() : value) {
case 'DEBUG':
environment[key] = LogLevel.DEBUG;
break;

case 'INFO':
environment[key] = LogLevel.INFO;
break;

case 'WARN':
environment[key] = LogLevel.WARN;
break;

case 'ERROR':
environment[key] = LogLevel.ERROR;
break;

default:
// do nothing
break;
}
}

/**
* Parses environment values
* @param values
Expand All @@ -79,7 +113,7 @@ export function parseEnvironment(values: ENVIRONMENT_MAP): ENVIRONMENT {
break;

case 'OTEL_LOG_LEVEL':
parseNumber(key, environment, values, LogLevel.ERROR, LogLevel.DEBUG);
setLogLevelFromEnv(key, environment, values);
break;

default:
Expand Down
37 changes: 37 additions & 0 deletions packages/opentelemetry-core/test/common/ConsoleLogger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
import * as assert from 'assert';
import { ConsoleLogger } from '../../src/common/ConsoleLogger';
import { LogLevel } from '../../src/common/types';
import {
mockEnvironment,
removeMockEnvironment,
} from '../utils/environment.test';

describe('ConsoleLogger', () => {
const origDebug = console.debug;
Expand Down Expand Up @@ -54,6 +58,7 @@ describe('ConsoleLogger', () => {
console.info = origInfo;
console.warn = origWarn;
console.error = origError;
removeMockEnvironment();
});

describe('constructor', () => {
Expand All @@ -65,6 +70,8 @@ describe('ConsoleLogger', () => {
assert.deepStrictEqual(warnCalledArgs, ['warn called %s', 'param1']);
consoleLogger.info('info called %s', 'param1');
assert.deepStrictEqual(infoCalledArgs, ['info called %s', 'param1']);
consoleLogger.debug('debug called %s', 'param1');
assert.strictEqual(debugCalledArgs, undefined);
});

it('should log with debug', () => {
Expand Down Expand Up @@ -114,5 +121,35 @@ describe('ConsoleLogger', () => {
consoleLogger.debug('debug called %s', 'param1');
assert.strictEqual(debugCalledArgs, null);
});

it('should log with environmentally set level ', () => {
mockEnvironment({
OTEL_LOG_LEVEL: 'WARN',
});
const consoleLogger = new ConsoleLogger();
consoleLogger.error('error called');
assert.deepStrictEqual(errorCalledArgs, ['error called']);
consoleLogger.warn('warn called %s', 'param1');
assert.deepStrictEqual(warnCalledArgs, ['warn called %s', 'param1']);
consoleLogger.info('info called %s', 'param1');
assert.deepStrictEqual(infoCalledArgs, null);
consoleLogger.debug('debug called %s', 'param1');
assert.deepStrictEqual(debugCalledArgs, null);
});

it('should log with default log level if environmentally set level is invalid', () => {
mockEnvironment({
OTEL_LOG_LEVEL: 'INVALID_VALUE',
});
const consoleLogger = new ConsoleLogger();
consoleLogger.error('error called');
assert.deepStrictEqual(errorCalledArgs, ['error called']);
consoleLogger.warn('warn called %s', 'param1');
assert.deepStrictEqual(warnCalledArgs, ['warn called %s', 'param1']);
consoleLogger.info('info called %s', 'param1');
assert.deepStrictEqual(infoCalledArgs, ['info called %s', 'param1']);
consoleLogger.debug('debug called %s', 'param1');
assert.deepStrictEqual(debugCalledArgs, null);
});
});
});
36 changes: 32 additions & 4 deletions packages/opentelemetry-core/test/utils/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ import {
} from '../../src/utils/environment';
import * as assert from 'assert';
import * as sinon from 'sinon';
import { LogLevel } from '../../src';

let lastMock: ENVIRONMENT_MAP = {};

function mockEnvironment(values: ENVIRONMENT_MAP) {
/**
* Mocks environment used for tests.
*/
export function mockEnvironment(values: ENVIRONMENT_MAP) {
lastMock = values;
if (typeof process !== 'undefined') {
Object.keys(values).forEach(key => {
Expand All @@ -38,7 +42,10 @@ function mockEnvironment(values: ENVIRONMENT_MAP) {
}
}

function removeMockEnvironment() {
/**
* Removes mocked environment used for tests.
*/
export function removeMockEnvironment() {
if (typeof process !== 'undefined') {
Object.keys(lastMock).forEach(key => {
delete process.env[key];
Expand Down Expand Up @@ -68,15 +75,23 @@ describe('environment', () => {
mockEnvironment({
FOO: '1',
OTEL_NO_PATCH_MODULES: 'a,b,c',
OTEL_LOG_LEVEL: '1',
OTEL_LOG_LEVEL: 'ERROR',
OTEL_SAMPLING_PROBABILITY: '0.5',
});
const env = getEnv();
assert.strictEqual(env.OTEL_NO_PATCH_MODULES, 'a,b,c');
assert.strictEqual(env.OTEL_LOG_LEVEL, 1);
assert.strictEqual(env.OTEL_LOG_LEVEL, LogLevel.ERROR);
assert.strictEqual(env.OTEL_SAMPLING_PROBABILITY, 0.5);
});

it('should parse OTEL_LOG_LEVEL despite casing', () => {
mockEnvironment({
OTEL_LOG_LEVEL: 'waRn',
});
const env = getEnv();
assert.strictEqual(env.OTEL_LOG_LEVEL, LogLevel.WARN);
});

it('should parse environment variables and use defaults', () => {
const env = getEnv();
Object.keys(DEFAULT_ENVIRONMENT).forEach(envKey => {
Expand All @@ -89,4 +104,17 @@ describe('environment', () => {
});
});
});

describe('mockEnvironment', () => {
it('should remove a mock environment', () => {
mockEnvironment({
OTEL_LOG_LEVEL: 'DEBUG',
OTEL_SAMPLING_PROBABILITY: 0.5,
});
removeMockEnvironment();
const env = getEnv();
assert.strictEqual(env.OTEL_LOG_LEVEL, LogLevel.INFO);
assert.strictEqual(env.OTEL_SAMPLING_PROBABILITY, 1);
});
});
});
4 changes: 2 additions & 2 deletions packages/opentelemetry-metrics/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { LogLevel } from '@opentelemetry/core';
import { LogLevel, getEnv } from '@opentelemetry/core';
import * as api from '@opentelemetry/api';
import { MetricExporter } from './export/types';
import { Resource } from '@opentelemetry/resources';
Expand Down Expand Up @@ -43,7 +43,7 @@ export interface MeterConfig {

/** Default Meter configuration. */
export const DEFAULT_CONFIG = {
logLevel: LogLevel.INFO,
logLevel: getEnv().OTEL_LOG_LEVEL,
};

/** The default metric creation options value. */
Expand Down
4 changes: 2 additions & 2 deletions packages/opentelemetry-tracing/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { AlwaysOnSampler, LogLevel } from '@opentelemetry/core';
import { AlwaysOnSampler, getEnv } from '@opentelemetry/core';

/** Default limit for Message events per span */
export const DEFAULT_MAX_EVENTS_PER_SPAN = 128;
Expand All @@ -30,7 +30,7 @@ export const DEFAULT_MAX_LINKS_PER_SPAN = 32;
* used to extend the default value.
*/
export const DEFAULT_CONFIG = {
logLevel: LogLevel.INFO,
logLevel: getEnv().OTEL_LOG_LEVEL,
sampler: new AlwaysOnSampler(),
traceParams: {
numberOfAttributesPerSpan: DEFAULT_MAX_ATTRIBUTES_PER_SPAN,
Expand Down