diff --git a/.gitignore b/.gitignore index 9dcdc86554e..1f35392751d 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,9 @@ package.json.lerna_backup # VsCode configs .vscode/ +#Visual Studio +.vs/ + #IDEA .idea *.iml diff --git a/benchmark/tracer.js b/benchmark/tracer.js index 274cd6de99f..27ba3520c63 100644 --- a/benchmark/tracer.js +++ b/benchmark/tracer.js @@ -4,7 +4,7 @@ const benchmark = require('./benchmark'); const opentelemetry = require('../packages/opentelemetry-api'); const { BasicTracerProvider, BatchSpanProcessor, InMemorySpanExporter, SimpleSpanProcessor } = require('../packages/opentelemetry-tracing'); -const logger = new opentelemetry.NoopLogger(); +const diagLogger = opentelemetry.createNoopDiagLogger(); const setups = [ { @@ -13,7 +13,7 @@ const setups = [ }, { name: 'BasicTracerProvider', - provider: new BasicTracerProvider({ logger }) + provider: new BasicTracerProvider({ logger: diagLogger }) }, { name: 'BasicTracerProvider with SimpleSpanProcessor', @@ -63,7 +63,7 @@ for (const setup of setups) { suite.run({ async: false }); } function getProvider(processor) { - const provider = new BasicTracerProvider({ logger }); + const provider = new BasicTracerProvider({ logger: diagLogger }); provider.addSpanProcessor(processor); return provider; } diff --git a/examples/collector-exporter-node/metrics.js b/examples/collector-exporter-node/metrics.js index 5f59a6e191a..f7102c3bf1c 100644 --- a/examples/collector-exporter-node/metrics.js +++ b/examples/collector-exporter-node/metrics.js @@ -1,15 +1,18 @@ 'use strict'; -const { ConsoleLogger, LogLevel } = require('@opentelemetry/core'); +const { DiagConsoleLogger, DiagLogLevel, diag } = require('@opentelemetry/api'); const { CollectorMetricExporter } = require('@opentelemetry/exporter-collector'); // const { CollectorMetricExporter } = require('@opentelemetry/exporter-collector-grpc'); // const { CollectorMetricExporter } = require('@opentelemetry/exporter-collector-proto'); const { MeterProvider } = require('@opentelemetry/metrics'); +diag.setLogger(new DiagConsoleLogger()); +diag.setLogLevel(DiagLogLevel.DEBUG); + const metricExporter = new CollectorMetricExporter({ serviceName: 'basic-metric-service', // url: 'http://localhost:55681/v1/metrics', - logger: new ConsoleLogger(LogLevel.DEBUG), + logger: diag, }); const meter = new MeterProvider({ diff --git a/examples/collector-exporter-node/tracing.js b/examples/collector-exporter-node/tracing.js index af9b617b046..64ba49f9cb9 100644 --- a/examples/collector-exporter-node/tracing.js +++ b/examples/collector-exporter-node/tracing.js @@ -1,15 +1,16 @@ 'use strict'; const opentelemetry = require('@opentelemetry/api'); -// const { ConsoleLogger, LogLevel} = require('@opentelemetry/core'); const { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/tracing'); const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector'); // const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector-grpc'); // const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector-proto'); +// opentelemetry.diag.setLogger(new opentelemetry.DiagConsoleLogger()); +// opentelemetry.diag.setLogLevel(opentelemetry.DiagLogLevel.DEBUG); + const exporter = new CollectorTraceExporter({ serviceName: 'basic-service', - // logger: new ConsoleLogger(LogLevel.DEBUG), // headers: { // foo: 'bar' // }, diff --git a/examples/metrics/metrics/observer.js b/examples/metrics/metrics/observer.js index aaece51489e..010bea50da1 100644 --- a/examples/metrics/metrics/observer.js +++ b/examples/metrics/metrics/observer.js @@ -1,7 +1,7 @@ 'use strict'; const { MeterProvider } = require('@opentelemetry/metrics'); -const { ConsoleLogger, LogLevel } = require('@opentelemetry/core'); +const { DiagConsoleLogger, DiagLogLevel, diagLogLevelFilter } = require('@opentelemetry/api'); const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); const exporter = new PrometheusExporter( @@ -61,7 +61,7 @@ meter.createBatchObserver((observerBatchResult) => { }); }, { maxTimeoutUpdateMS: 500, - logger: new ConsoleLogger(LogLevel.DEBUG) + logger: diagLogLevelFilter(DiagLogLevel.DEBUG, new DiagConsoleLogger()) }, ); diff --git a/examples/tracer-web/examples/metrics/index.js b/examples/tracer-web/examples/metrics/index.js index 6aefa8cdde3..7090c78dc3e 100644 --- a/examples/tracer-web/examples/metrics/index.js +++ b/examples/tracer-web/examples/metrics/index.js @@ -1,12 +1,12 @@ 'use strict'; -const { ConsoleLogger, LogLevel } = require('@opentelemetry/core'); +const { DiagConsoleLogger, DiagLogLevel, diagLogLevelFilter } = require('@opentelemetry/api'); const { CollectorMetricExporter } = require('@opentelemetry/exporter-collector'); const { MeterProvider } = require('@opentelemetry/metrics'); const metricExporter = new CollectorMetricExporter({ serviceName: 'basic-metric-service', - logger: new ConsoleLogger(LogLevel.DEBUG), + logger: diagLogLevelFilter(DiagLogLevel.DEBUG, new DiagConsoleLogger()), }); let interval; diff --git a/packages/opentelemetry-api/src/api/diag.ts b/packages/opentelemetry-api/src/api/diag.ts new file mode 100644 index 00000000000..f9914b3f066 --- /dev/null +++ b/packages/opentelemetry-api/src/api/diag.ts @@ -0,0 +1,153 @@ +/* + * 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 { + DiagLogger, + DiagLogFunction, + createNoopDiagLogger, + diagLoggerFunctions, +} from '../diag/logger'; +import { DiagLogLevel, createLogLevelDiagLogger } from '../diag/logLevel'; +import { + API_BACKWARDS_COMPATIBILITY_VERSION, + GLOBAL_DIAG_LOGGER_API_KEY, + makeGetter, + _global, +} from './global-utils'; + +/** Internal simple Noop Diag API that returns a noop logger and does not allow any changes */ +function noopDiagApi(): DiagAPI { + const noopApi = createNoopDiagLogger() as DiagAPI; + + noopApi.getLogger = () => noopApi; + noopApi.setLogger = noopApi.getLogger; + noopApi.setLogLevel = () => {}; + + return noopApi; +} + +/** + * Singleton object which represents the entry point to the OpenTelemetry internal + * diagnostic API + */ +export class DiagAPI implements DiagLogger { + /** Get the singleton instance of the DiagAPI API */ + public static instance(): DiagAPI { + let theInst = null; + if (_global[GLOBAL_DIAG_LOGGER_API_KEY]) { + // Looks like a previous instance was set, so try and fetch it + theInst = _global[GLOBAL_DIAG_LOGGER_API_KEY]?.( + API_BACKWARDS_COMPATIBILITY_VERSION + ) as DiagAPI; + } + + if (!theInst) { + theInst = new DiagAPI(); + _global[GLOBAL_DIAG_LOGGER_API_KEY] = makeGetter( + API_BACKWARDS_COMPATIBILITY_VERSION, + theInst, + noopDiagApi() + ); + } + + return theInst; + } + + /** + * Private internal constructor + * @private + */ + private constructor() { + let _logLevel: DiagLogLevel = DiagLogLevel.INFO; + let _filteredLogger: DiagLogger | null; + let _logger: DiagLogger = createNoopDiagLogger(); + + function _logProxy(funcName: keyof DiagLogger): DiagLogFunction { + return function () { + const orgArguments = arguments as unknown; + const theLogger = _filteredLogger || _logger; + const theFunc = theLogger[funcName]; + if (typeof theFunc === 'function') { + return theFunc.apply( + theLogger, + orgArguments as Parameters + ); + } + }; + } + + // Using self local variable for minification purposes as 'this' cannot be minified + const self = this; + + // DiagAPI specific functions + + self.getLogger = (): DiagLogger => { + // Return itself if no existing logger is defined (defaults effectively to a Noop) + return _logger; + }; + + self.setLogger = (logger: DiagLogger): DiagLogger => { + const prevLogger = _logger; + if (prevLogger !== logger && logger !== self) { + // Simple special case to avoid any possible infinite recursion on the logging functions + _logger = logger || createNoopDiagLogger(); + _filteredLogger = createLogLevelDiagLogger(_logLevel, _logger); + } + + return prevLogger; + }; + + self.setLogLevel = (maxLogLevel: DiagLogLevel) => { + if (maxLogLevel !== _logLevel) { + _logLevel = maxLogLevel; + if (_logger) { + _filteredLogger = createLogLevelDiagLogger(maxLogLevel, _logger); + } + } + }; + + for (let i = 0; i < diagLoggerFunctions.length; i++) { + const name = diagLoggerFunctions[i]; + self[name] = _logProxy(name); + } + } + + /** + * Return the currently configured logger instance, if no logger has been configured + * it will return itself so any log level filtering will still be applied in this case. + */ + public getLogger!: () => DiagLogger; + + /** + * Set the DiagLogger instance + * @param logger - The DiagLogger instance to set as the default logger + * @returns The previously registered DiagLogger + */ + public setLogger!: (logger: DiagLogger) => DiagLogger; + + /** Set the default maximum diagnostic logging level */ + public setLogLevel!: (maxLogLevel: DiagLogLevel) => void; + + // DiagLogger implementation + public verbose!: DiagLogFunction; + public debug!: DiagLogFunction; + public info!: DiagLogFunction; + public warn!: DiagLogFunction; + public startupInfo!: DiagLogFunction; + public error!: DiagLogFunction; + public critical!: DiagLogFunction; + public terminal!: DiagLogFunction; +} diff --git a/packages/opentelemetry-api/src/api/global-utils.ts b/packages/opentelemetry-api/src/api/global-utils.ts index 60481a114b6..b0a0e522247 100644 --- a/packages/opentelemetry-api/src/api/global-utils.ts +++ b/packages/opentelemetry-api/src/api/global-utils.ts @@ -18,6 +18,7 @@ import { ContextManager } from '@opentelemetry/context-base'; import { TextMapPropagator } from '../context/propagation/TextMapPropagator'; import { TracerProvider } from '../trace/tracer_provider'; import { _globalThis } from '../platform'; +import { DiagAPI } from '../api/diag'; export const GLOBAL_CONTEXT_MANAGER_API_KEY = Symbol.for( 'io.opentelemetry.js.api.context' @@ -28,11 +29,16 @@ export const GLOBAL_PROPAGATION_API_KEY = Symbol.for( ); export const GLOBAL_TRACE_API_KEY = Symbol.for('io.opentelemetry.js.api.trace'); +export const GLOBAL_DIAG_LOGGER_API_KEY = Symbol.for( + 'io.opentelemetry.js.api.diag' +); + type Get = (version: number) => T; type OtelGlobal = Partial<{ [GLOBAL_CONTEXT_MANAGER_API_KEY]: Get; [GLOBAL_PROPAGATION_API_KEY]: Get; [GLOBAL_TRACE_API_KEY]: Get; + [GLOBAL_DIAG_LOGGER_API_KEY]: Get; }>; export const _global = _globalThis as OtelGlobal; diff --git a/packages/opentelemetry-api/src/common/Logger.ts b/packages/opentelemetry-api/src/common/Logger.ts index f4bccbe9dbd..3f0d0406884 100644 --- a/packages/opentelemetry-api/src/common/Logger.ts +++ b/packages/opentelemetry-api/src/common/Logger.ts @@ -16,7 +16,10 @@ export type LogFunction = (message: string, ...args: unknown[]) => void; -/** Defines a logger interface. */ +/** Defines a logger interface. + * @deprecated This interface will be removed prior to v1.0, use the api.diag + * @see {@link DiagLogger} and {@link DiagAPI} + */ export interface Logger { error: LogFunction; warn: LogFunction; diff --git a/packages/opentelemetry-api/src/diag/consoleLogger.ts b/packages/opentelemetry-api/src/diag/consoleLogger.ts new file mode 100644 index 00000000000..27f7cb9fbb7 --- /dev/null +++ b/packages/opentelemetry-api/src/diag/consoleLogger.ts @@ -0,0 +1,112 @@ +/* + * 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 { DiagLogger, DiagLogFunction } from './logger'; + +const consoleMap: { n: keyof DiagLogger; c: keyof Console }[] = [ + { n: 'terminal', c: 'error' }, + { n: 'critical', c: 'error' }, + { n: 'error', c: 'error' }, + { n: 'warn', c: 'warn' }, + { n: 'info', c: 'info' }, + { n: 'debug', c: 'debug' }, + { n: 'verbose', c: 'trace' }, + { n: 'startupInfo', c: 'info' }, +]; + +/** + * A simple Immutable Console based diagnostic logger which will output any messages to the Console. + * If you want to limit the amount of logging to a specific level or lower use the + * {@link diagLogLevelFilter} + */ +export class DiagConsoleLogger implements DiagLogger { + constructor() { + function _consoleFunc(funcName: keyof Console): DiagLogFunction { + return function () { + const orgArguments = arguments; + if (console) { + // Some environments only expose the console when the F12 developer console is open + let theFunc = console[funcName]; + if (typeof theFunc !== 'function') { + // Not all environments support all functions + theFunc = console.log; + } + + // One last final check + if (typeof theFunc === 'function') { + return theFunc.apply(console, orgArguments); + } + } + }; + } + + for (let i = 0; i < consoleMap.length; i++) { + this[consoleMap[i].n] = _consoleFunc(consoleMap[i].c); + } + } + + /** + * Log a terminal situation that would cause the API to completely fail to initialize, + * if this type of message is logged functionality of the API is not expected to be functional. + */ + public terminal!: DiagLogFunction; + + /** + * Log a critical error that NEEDS to be addressed, functionality of the component that emits + * this log detail may non-functional. While the overall API may be. + */ + public critical!: DiagLogFunction; + + /** Log an error scenario that was not expected and caused the requested operation to fail. */ + public error!: DiagLogFunction; + + /** + * Logs a general informational message that is used for logging component startup and version + * information without causing additional general informational messages when the logging level + * is set to DiagLogLevel.WARN or lower. + */ + public startupInfo!: DiagLogFunction; + + /** + * Log a warning scenario to inform the developer of an issues that should be investigated. + * The requested operation may or may not have succeeded or completed. + */ + public warn!: DiagLogFunction; + + /** + * Log a general informational message, this should not affect functionality. + * This is also the default logging level so this should NOT be used for logging + * debugging level information. + */ + public info!: DiagLogFunction; + + /** + * Log a general debug message that can be useful for identifying a failure. + * Information logged at this level may include diagnostic details that would + * help identify a failure scenario. Useful scenarios would be to log the execution + * order of async operations + */ + public debug!: DiagLogFunction; + + /** + * Log a detailed (verbose) trace level logging that can be used to identify failures + * where debug level logging would be insufficient, this level of tracing can include + * input and output parameters and as such may include PII information passing through + * the API. As such it is recommended that this level of tracing should not be enabled + * in a production environment. + */ + public verbose!: DiagLogFunction; +} diff --git a/packages/opentelemetry-api/src/diag/logLevel.ts b/packages/opentelemetry-api/src/diag/logLevel.ts new file mode 100644 index 00000000000..31241801ca8 --- /dev/null +++ b/packages/opentelemetry-api/src/diag/logLevel.ts @@ -0,0 +1,161 @@ +/* + * 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 { DiagAPI } from '../api/diag'; +import { Logger } from '../common/Logger'; +import { DiagLogger, DiagLogFunction, createNoopDiagLogger } from './logger'; + +/** + * Defines the available internal logging levels for the diagnostic logger, the numeric values + * of the levels are defined to match the original values from the initial LogLevel to avoid + * compatibility/migration issues for any implementation that assume the numeric ordering. + */ +export enum DiagLogLevel { + /** Diagnostic Logging level setting to disable all logging (except and forced logs) */ + NONE = 0, + + /** + * Identifies a terminal situation that would cause the API to completely fail to initialize, + * if this type of error is logged functionality of the API is not expected to be functional. + */ + TERMINAL = 10, + + /** + * Identifies a critical error that needs to be addressed, functionality of the component + * that emits this log detail may non-functional. + */ + CRITICAL = 20, + + /** Identifies an error scenario */ + ERROR = 30, + + /** Identifies startup and failure (lower) scenarios */ + STARTUP = 40, + + /** Identifies a warning scenario */ + WARN = 50, + + /** General informational log message */ + INFO = 60, + + /** General debug log message */ + DEBUG = 70, + + /** + * Detailed trace level logging should only be used for development, should only be set + * in a development environment. + */ + VERBOSE = 80, + + /** Used to set the logging level to include all logging */ + ALL = 9999, +} + +/** + * This is equivalent to: + * type LogLevelString = 'NONE' | TERMINAL' | 'CRITICAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'VERBOSE' | 'ALL'; + */ +export type DiagLogLevelString = keyof typeof DiagLogLevel; + +/** + * Mapping from DiagLogger function name to Legacy Logger function used if + * the logger instance doesn't have the DiagLogger function + */ +const fallbackLoggerFuncMap: { [n: string]: keyof Logger } = { + terminal: 'error', + critical: 'error', + error: 'error', + warn: 'warn', + info: 'info', + debug: 'debug', + verbose: 'debug', + startupInfo: 'info', +}; + +/** Mapping from DiagLogger function name to logging level. */ +const levelMap: { n: keyof DiagLogger; l: DiagLogLevel }[] = [ + { n: 'terminal', l: DiagLogLevel.TERMINAL }, + { n: 'critical', l: DiagLogLevel.CRITICAL }, + { n: 'error', l: DiagLogLevel.ERROR }, + { n: 'warn', l: DiagLogLevel.WARN }, + { n: 'info', l: DiagLogLevel.INFO }, + { n: 'debug', l: DiagLogLevel.DEBUG }, + { n: 'verbose', l: DiagLogLevel.VERBOSE }, + { n: 'startupInfo', l: DiagLogLevel.ERROR }, +]; + +/** + * Create a Diagnostic logger which limits the messages that are logged via the wrapped logger based on whether the + * message has a {@link DiagLogLevel} equal to the maximum logging level or lower, unless the {@link DiagLogLevel} is + * NONE which will return a noop logger instance. This can be useful to reduce the amount of logging used for the + * system or for a specific component based on any local configuration. + * If you don't supply a logger it will use the global api.diag as the destination which will use the + * current logger and any filtering it may have applied. + * To avoid / bypass any global level filtering you should pass the current logger returned via + * api.diag.getLogger() however, any changes to the logger used by api.diag won't be reflected for this case. + * @param maxLevel - The max level to log any logging of a lower + * @param logger - The specific logger to limit, if not defined or supplied will default to api.diag + * @implements {@link DiagLogger} + * @returns {DiagLogger} + */ + +export function createLogLevelDiagLogger( + maxLevel: DiagLogLevel, + logger?: DiagLogger | null +): DiagLogger { + if (maxLevel < DiagLogLevel.NONE) { + maxLevel = DiagLogLevel.NONE; + } else if (maxLevel > DiagLogLevel.ALL) { + maxLevel = DiagLogLevel.ALL; + } + + if (maxLevel === DiagLogLevel.NONE) { + return createNoopDiagLogger(); + } + + if (!logger) { + logger = DiagAPI.instance(); + } + + function _filterFunc( + theLogger: DiagLogger, + funcName: keyof DiagLogger, + theLevel: DiagLogLevel + ): DiagLogFunction { + if (maxLevel >= theLevel) { + return function () { + const orgArguments = arguments as unknown; + const theFunc = + theLogger[funcName] || theLogger[fallbackLoggerFuncMap[funcName]]; + if (theFunc && typeof theFunc === 'function') { + return theFunc.apply( + logger, + orgArguments as Parameters + ); + } + }; + } + return function () {}; + } + + const newLogger = {} as DiagLogger; + for (let i = 0; i < levelMap.length; i++) { + const name = levelMap[i].n; + newLogger[name] = _filterFunc(logger, name, levelMap[i].l); + } + + return newLogger; +} diff --git a/packages/opentelemetry-api/src/diag/logger.ts b/packages/opentelemetry-api/src/diag/logger.ts new file mode 100644 index 00000000000..6b01ff9b1b9 --- /dev/null +++ b/packages/opentelemetry-api/src/diag/logger.ts @@ -0,0 +1,121 @@ +/* + * 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 { Logger } from '../common/Logger'; + +/** + * Defines a type which can be used for as a parameter without breaking backward + * compatibility. The {@link Logger} reference will be removed with the removal + * of the Logger definition, this can be used as a replacement for functions + * that are currently passing Logger references during migration to minimize + * breaks that will occur with the removal of the Logger interface. + */ +export type OptionalDiagLogger = Logger | DiagLogger | null | undefined; + +export type DiagLogFunction = (message: string, ...args: unknown[]) => void; + +/** + * Defines an internal diagnostic logger interface which is used to log internal diagnostic + * messages, you can set the default diagnostic logger via the {@link DiagAPI} setLogger function. + * API provided implementations include :- + * - a No-Op {@link createNoopDiagLogger} + * - a {@link DiagLogLevel} filtering wrapper {@link diagLogLevelFilter} + * - a general Console {@link DiagConsoleLogger} version. + */ +export interface DiagLogger { + /** + * Log a terminal situation that would cause the API to completely fail to initialize, + * if this type of message is logged functionality of the API is not expected to be functional. + */ + terminal: DiagLogFunction; + + /** + * Log a critical error that NEEDS to be addressed, functionality of the component that emits + * this log detail may be limited or non-functional depending on when this message is emitted. + * Unlike terminal message, it is expected that the overall API may still be functional, again + * depending on what component and when this message is emitted. + */ + critical: DiagLogFunction; + + /** Log an error scenario that was not expected and caused the requested operation to fail. */ + error: DiagLogFunction; + + /** + * Logs a general informational message that is used for logging component startup and version + * information without causing additional general informational messages when the logging level + * is set to DiagLogLevel.WARN or lower. + */ + startupInfo: DiagLogFunction; + + /** + * Log a warning scenario to inform the developer of an issues that should be investigated. + * The requested operation may or may not have succeeded or completed. + */ + warn: DiagLogFunction; + + /** + * Log a general informational message, this should not affect functionality. + * This is also the default logging level so this should NOT be used for logging + * debugging level information. + */ + info: DiagLogFunction; + + /** + * Log a general debug message that can be useful for identifying a failure. + * Information logged at this level may include diagnostic details that would + * help identify a failure scenario. + * For example: Logging the order of execution of async operations. + */ + debug: DiagLogFunction; + + /** + * Log a detailed (verbose) trace level logging that can be used to identify failures + * where debug level logging would be insufficient, this level of tracing can include + * input and output parameters and as such may include PII information passing through + * the API. As such it is recommended that this level of tracing should not be enabled + * in a production environment. + */ + verbose: DiagLogFunction; +} + +// DiagLogger implementation +export const diagLoggerFunctions: Array = [ + 'verbose', + 'debug', + 'info', + 'warn', + 'startupInfo', + 'error', + 'critical', + 'terminal', +]; + +function noopLogFunction() {} + +/** + * Returns a No-Op Diagnostic logger where all messages do nothing. + * @implements {@link DiagLogger} + * @returns {DiagLogger} + */ +export function createNoopDiagLogger(): DiagLogger { + const diagLogger = {} as DiagLogger; + + for (let i = 0; i < diagLoggerFunctions.length; i++) { + diagLogger[diagLoggerFunctions[i]] = noopLogFunction; + } + + return diagLogger; +} diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index b3cbbd0f0f1..2a5e99d9ba0 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -42,6 +42,9 @@ export * from './trace/trace_flags'; export * from './trace/trace_state'; export * from './trace/tracer_provider'; export * from './trace/tracer'; +export * from './diag/consoleLogger'; +export * from './diag/logger'; +export * from './diag/logLevel'; export { INVALID_SPANID, @@ -74,8 +77,20 @@ export type { PropagationAPI } from './api/propagation'; /** Entrypoint for propagation API */ export const propagation = PropagationAPI.getInstance(); +import { DiagAPI } from './api/diag'; +export type { DiagAPI } from './api/diag'; + +/** + * Entrypoint for Diag API. + * Defines Diagnostic handler used for internal diagnostic logging operations. + * The default provides a Noop DiagLogger implementation which may be changed via the + * diag.setLogger(logger: DiagLogger) function. + */ +export const diag = DiagAPI.instance(); + export default { trace, context, propagation, + diag, }; diff --git a/packages/opentelemetry-api/src/trace/NoopLogger.ts b/packages/opentelemetry-api/src/trace/NoopLogger.ts index 1fb8b184d07..4dea9a5d393 100644 --- a/packages/opentelemetry-api/src/trace/NoopLogger.ts +++ b/packages/opentelemetry-api/src/trace/NoopLogger.ts @@ -16,7 +16,10 @@ import { Logger } from '../common/Logger'; -/** No-op implementation of Logger */ +/** No-op implementation of Logger + * @deprecated This class will be removed prior to v1.0, use the api.diag + * @see {@link DiagAPI}, {@link DiagLogger} and {@link createNoopDiagLogger} + */ export class NoopLogger implements Logger { // By default does nothing debug(_message: string, ..._args: unknown[]) {} diff --git a/packages/opentelemetry-api/test/diag/consoleLogger.test.ts b/packages/opentelemetry-api/test/diag/consoleLogger.test.ts new file mode 100644 index 00000000000..b1150308f86 --- /dev/null +++ b/packages/opentelemetry-api/test/diag/consoleLogger.test.ts @@ -0,0 +1,212 @@ +/* + * 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 { DiagConsoleLogger } from '../../src/diag/consoleLogger'; +import { diagLoggerFunctions } from '../../src/diag/logger'; + +const consoleFuncs: Array = [ + 'debug', + 'info', + 'warn', + 'error', + 'log', + 'trace', +]; + +const expectedConsoleMap: { [n: string]: keyof Console } = { + terminal: 'error', + critical: 'error', + error: 'error', + warn: 'warn', + info: 'info', + debug: 'debug', + verbose: 'trace', + startupInfo: 'info', +}; + +describe('DiagConsoleLogger', () => { + const origConsole = console; + const orig: any = {}; + const calledArgs: any = {}; + + // Save original functions + consoleFuncs.forEach(fName => { + orig[fName] = console[fName]; + calledArgs[fName] = null; + }); + + let canMockConsole = true; + + try { + // eslint-disable-next-line no-global-assign + console = origConsole; + } catch (ex) { + // Not supported on CI pipeline (works locally) + canMockConsole = false; + } + + beforeEach(() => { + // mock Console + consoleFuncs.forEach(fName => { + console[fName] = (...args: unknown[]) => { + calledArgs[fName] = args; + }; + }); + }); + + afterEach(() => { + // restore + if (canMockConsole) { + try { + // eslint-disable-next-line no-global-assign + console = origConsole; + } catch (ex) { + // Not supported on CI pipeline + canMockConsole = false; + } + } + + consoleFuncs.forEach(fName => { + calledArgs[fName] = null; + console[fName] = orig[fName]; + }); + }); + + describe('constructor', () => { + diagLoggerFunctions.forEach(fName => { + it(`console logger should provide ${fName} function`, () => { + const consoleLogger: any = new DiagConsoleLogger(); + consoleLogger[fName](`${fName} called %s`, 'param1'); + assert.ok( + typeof consoleLogger[fName] === 'function', + `Must have a ${fName} function` + ); + }); + + it(`should log ${expectedConsoleMap[fName]} message with ${fName} call only`, () => { + const consoleLogger: any = new DiagConsoleLogger(); + consoleLogger[fName](`${fName} called %s`, 'param1'); + + // Make sure only gets logged once + let matches = 0; + consoleFuncs.forEach(cName => { + if (cName !== expectedConsoleMap[fName]) { + assert.deepStrictEqual(calledArgs[cName], null); + } else { + assert.deepStrictEqual(calledArgs[expectedConsoleMap[fName]], [ + `${fName} called %s`, + 'param1', + ]); + matches++; + } + }); + + assert.deepStrictEqual(calledArgs.log, null); + assert.strictEqual(matches, 1, 'should log at least once'); + }); + + consoleFuncs.forEach(cName => { + it(`should log ${fName} message even when console doesn't support ${cName} call before construction`, () => { + console[cName] = undefined; + const consoleLogger: any = new DiagConsoleLogger(); + consoleLogger[fName](`${fName} called %s`, 'param1'); + if (cName !== expectedConsoleMap[fName]) { + assert.deepStrictEqual(calledArgs[cName], null); + } else { + assert.deepStrictEqual(calledArgs.log, [ + `${fName} called %s`, + 'param1', + ]); + } + }); + + it(`should log ${fName} message even when console doesn't support ${cName} call after construction`, () => { + const consoleLogger: any = new DiagConsoleLogger(); + console[cName] = undefined; + consoleLogger[fName](`${fName} called %s`, 'param1'); + if (cName !== expectedConsoleMap[fName]) { + assert.deepStrictEqual(calledArgs[cName], null); + } else { + assert.deepStrictEqual(calledArgs.log, [ + `${fName} called %s`, + 'param1', + ]); + } + }); + }); + }); + + if (canMockConsole) { + diagLoggerFunctions.forEach(fName => { + const cName = expectedConsoleMap[fName]; + it(`should not throw even when console is not supported for ${fName} call`, () => { + // eslint-disable-next-line no-global-assign + (console as any) = undefined; + const consoleLogger: any = new DiagConsoleLogger(); + consoleLogger[fName](`${fName} called %s`, 'param1'); + assert.deepStrictEqual(calledArgs[cName], null); + assert.deepStrictEqual(calledArgs.log, null); + }); + + it(`should not throw even when console is disabled after construction for ${fName} call`, () => { + const consoleLogger: any = new DiagConsoleLogger(); + // eslint-disable-next-line no-global-assign + (console as any) = undefined; + consoleLogger[fName](`${fName} called %s`, 'param1'); + assert.deepStrictEqual(calledArgs[expectedConsoleMap[fName]], null); + assert.deepStrictEqual(calledArgs.log, null); + }); + + it(`should not throw even when console is invalid after construction for ${fName} call`, () => { + const invalidConsole = { + debug: 1, + warn: 2, + error: 3, + trace: 4, + info: 5, + log: 6, + }; + + const consoleLogger = new DiagConsoleLogger(); + // eslint-disable-next-line no-global-assign + (console as any) = invalidConsole; + consoleLogger[fName](`${fName} called %s`, 'param1'); + assert.deepStrictEqual(calledArgs[expectedConsoleMap[fName]], null); + assert.deepStrictEqual(calledArgs.log, null); + }); + + it(`should not throw even when console is invalid before construction for ${fName} call`, () => { + const invalidConsole = { + debug: 1, + warn: 2, + error: 3, + trace: 4, + info: 5, + log: 6, + }; + + // eslint-disable-next-line no-global-assign + (console as any) = invalidConsole; + const consoleLogger = new DiagConsoleLogger(); + consoleLogger[fName](`${fName} called %s`, 'param1'); + assert.deepStrictEqual(calledArgs[expectedConsoleMap[fName]], null); + assert.deepStrictEqual(calledArgs.log, null); + }); + }); + } + }); +}); diff --git a/packages/opentelemetry-api/test/diag/logLevel.test.ts b/packages/opentelemetry-api/test/diag/logLevel.test.ts new file mode 100644 index 00000000000..b984135e1c6 --- /dev/null +++ b/packages/opentelemetry-api/test/diag/logLevel.test.ts @@ -0,0 +1,366 @@ +/* + * 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 { diag } from '../../src'; +import { Logger } from '../../src/common/Logger'; +import { + createNoopDiagLogger, + DiagLogger, + diagLoggerFunctions, +} from '../../src/diag/logger'; +import { + DiagLogLevel, + createLogLevelDiagLogger, +} from '../../src/diag/logLevel'; + +const incompleteLoggerFuncs: Array = [ + 'debug', + 'info', + 'warn', + 'error', +]; + +const expectedIncompleteMap: { [n: string]: keyof Console } = { + terminal: 'error', + critical: 'error', + error: 'error', + warn: 'warn', + info: 'info', + debug: 'debug', + verbose: 'debug', + startupInfo: 'info', +}; + +describe('LogLevelFilter DiagLogger', () => { + const calledArgs: any = { + terminal: null, + critical: null, + error: null, + warn: null, + info: null, + debug: null, + verbose: null, + startupInfo: null, + }; + + let dummyLogger: DiagLogger; + + /** Simulated Legacy logger */ + let incompleteLogger: DiagLogger; + + beforeEach(() => { + // set no logger + diag.setLogger(null as any); + diag.setLogLevel(DiagLogLevel.INFO); + + // mock + dummyLogger = {} as DiagLogger; + diagLoggerFunctions.forEach(fName => { + dummyLogger[fName] = (...args: unknown[]) => { + calledArgs[fName] = args; + }; + }); + + incompleteLogger = {} as DiagLogger; + incompleteLoggerFuncs.forEach(fName => { + incompleteLogger[fName] = (...args: unknown[]) => { + calledArgs[fName] = args; + }; + }); + }); + + afterEach(() => { + // restore + diagLoggerFunctions.forEach(fName => { + calledArgs[fName] = null; + }); + }); + + describe('constructor', () => { + const levelMap: Array<{ + message: string; + level: DiagLogLevel; + ignoreFuncs: Array; + }> = [ + { message: 'ALL', level: DiagLogLevel.ALL, ignoreFuncs: [] }, + { message: 'greater than ALL', level: 32768, ignoreFuncs: [] }, + { message: 'VERBOSE', level: DiagLogLevel.VERBOSE, ignoreFuncs: [] }, + { message: 'DEBUG', level: DiagLogLevel.DEBUG, ignoreFuncs: ['verbose'] }, + { + message: 'INFO', + level: DiagLogLevel.INFO, + ignoreFuncs: ['verbose', 'debug'], + }, + { + message: 'WARN', + level: DiagLogLevel.WARN, + ignoreFuncs: ['verbose', 'debug', 'info'], + }, + { + message: 'ERROR', + level: DiagLogLevel.ERROR, + ignoreFuncs: ['verbose', 'debug', 'info', 'warn'], + }, + { + message: 'CRITICAL', + level: DiagLogLevel.CRITICAL, + ignoreFuncs: [ + 'verbose', + 'debug', + 'info', + 'warn', + 'error', + 'startupInfo', + ], + }, + { + message: 'TERMINAL', + level: DiagLogLevel.TERMINAL, + ignoreFuncs: [ + 'verbose', + 'debug', + 'info', + 'warn', + 'error', + 'critical', + 'startupInfo', + ], + }, + { + message: 'between TERMINAL and NONE', + level: 1, + ignoreFuncs: [ + 'verbose', + 'debug', + 'info', + 'warn', + 'error', + 'critical', + 'terminal', + 'startupInfo', + ], + }, + { + message: 'NONE', + level: DiagLogLevel.NONE, + ignoreFuncs: [ + 'verbose', + 'debug', + 'info', + 'warn', + 'error', + 'critical', + 'terminal', + 'startupInfo', + ], + }, + { + message: 'less than NONE', + level: -1000, + ignoreFuncs: [ + 'verbose', + 'debug', + 'info', + 'warn', + 'error', + 'critical', + 'terminal', + 'startupInfo', + ], + }, + ]; + + levelMap.forEach(map => { + diagLoggerFunctions.forEach(fName => { + it(`should log ${fName} message with ${map.message} level`, () => { + const testLogger = createLogLevelDiagLogger(map.level, dummyLogger); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if (fName === lName && map.ignoreFuncs.indexOf(lName) === -1) { + assert.deepStrictEqual(calledArgs[lName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + + it(`should be noop for null with explicit noop Logger log ${fName} message with ${map.message} level`, () => { + const testLogger = createLogLevelDiagLogger( + map.level, + createNoopDiagLogger() + ); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + assert.strictEqual(calledArgs[lName], null); + }); + }); + + it(`should be noop and not throw for null and no default Logger log ${fName} message with ${map.message} level`, () => { + const testLogger = createLogLevelDiagLogger(map.level, null); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + assert.strictEqual(calledArgs[lName], null); + }); + }); + + it(`should be noop and not throw for undefined and no default Logger log ${fName} message with ${map.message} level`, () => { + const testLogger = createLogLevelDiagLogger(map.level, undefined); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + assert.strictEqual(calledArgs[lName], null); + }); + }); + + it(`should use default logger for undefined and log ${fName} message with ${map.message} level`, () => { + diag.setLogger(dummyLogger); + diag.setLogLevel(DiagLogLevel.ALL); + const testLogger = createLogLevelDiagLogger(map.level, undefined); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if (fName === lName && map.ignoreFuncs.indexOf(lName) === -1) { + assert.deepStrictEqual(calledArgs[lName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + + it(`should use default logger for null and log ${fName} message with ${map.message} level`, () => { + diag.setLogger(dummyLogger); + diag.setLogLevel(DiagLogLevel.ALL); + const testLogger = createLogLevelDiagLogger(map.level, null); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if (fName === lName && map.ignoreFuncs.indexOf(lName) === -1) { + assert.deepStrictEqual(calledArgs[lName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + + it(`incomplete (legacy) logger should log ${fName} message to ${expectedIncompleteMap[fName]} with ${map.message} level`, () => { + const testLogger = createLogLevelDiagLogger( + map.level, + incompleteLogger + ); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + const expectedLog = expectedIncompleteMap[lName]; + if (fName === lName && map.ignoreFuncs.indexOf(lName) === -1) { + assert.deepStrictEqual(calledArgs[expectedLog], [ + `${fName} called %s`, + 'param1', + ]); + } + }); + }); + + levelMap.forEach(masterLevelMap => { + it(`diag setLogLevel is not ignored when set to ${masterLevelMap.message} and using default logger to log ${fName} message with ${map.message} level`, () => { + diag.setLogger(dummyLogger); + diag.setLogLevel(masterLevelMap.level); + + const testLogger = createLogLevelDiagLogger(map.level); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if ( + fName === lName && + map.ignoreFuncs.indexOf(lName) === -1 && + masterLevelMap.ignoreFuncs.indexOf(lName) === -1 + ) { + assert.deepStrictEqual(calledArgs[lName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + + it(`diag setLogLevel is ignored when set to ${masterLevelMap.message} when using a specific logger to log ${fName} message with ${map.message} level`, () => { + diag.setLogger(dummyLogger); + diag.setLogLevel(masterLevelMap.level); + + const testLogger = createLogLevelDiagLogger( + map.level, + diag.getLogger() + ); + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if (fName === lName && map.ignoreFuncs.indexOf(lName) === -1) { + assert.deepStrictEqual(calledArgs[lName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + }); + + it(`diag setLogLevel and logger should log ${fName} message with ${map.message} level`, () => { + diag.setLogger(dummyLogger); + diag.setLogLevel(map.level); + diag[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if (fName === lName && map.ignoreFuncs.indexOf(lName) === -1) { + assert.deepStrictEqual(calledArgs[lName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + + it(`should not throw with an invalid DiagLogger calling ${fName} with ${map.message} level`, () => { + const invalidLogger = { + debug: 1, + warn: 2, + error: 3, + trace: 4, + info: 5, + log: 6, + }; + + const testLogger = createLogLevelDiagLogger( + map.level, + invalidLogger as any + ); + + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + assert.strictEqual(calledArgs[lName], null); + }); + }); + }); + }); + }); +}); diff --git a/packages/opentelemetry-api/test/diag/logger.test.ts b/packages/opentelemetry-api/test/diag/logger.test.ts new file mode 100644 index 00000000000..d1629e964e8 --- /dev/null +++ b/packages/opentelemetry-api/test/diag/logger.test.ts @@ -0,0 +1,97 @@ +/* + * 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 { diag, DiagLogLevel } from '../../src'; +import { + DiagLogger, + createNoopDiagLogger, + diagLoggerFunctions, +} from '../../src/diag/logger'; + +describe('DiagLogger functions', () => { + const calledArgs: any = { + terminal: null, + critical: null, + error: null, + warn: null, + info: null, + debug: null, + verbose: null, + startupInfo: null, + }; + + let dummyLogger: DiagLogger; + + beforeEach(() => { + // mock + dummyLogger = {} as DiagLogger; + diagLoggerFunctions.forEach(fName => { + dummyLogger[fName] = (...args: unknown[]) => { + calledArgs[fName] = args; + }; + }); + }); + + afterEach(() => { + // restore + diagLoggerFunctions.forEach(fName => { + calledArgs[fName] = null; + }); + }); + + describe('constructor', () => { + diagLoggerFunctions.forEach(fName => { + it(`should log with ${fName} message`, () => { + const testLogger = dummyLogger; + testLogger[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if (fName === lName) { + assert.deepStrictEqual(calledArgs[fName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + + it(`diag should log with ${fName} message`, () => { + diag.setLogger(dummyLogger); + diag.setLogLevel(DiagLogLevel.ALL); + diag[fName](`${fName} called %s`, 'param1'); + diagLoggerFunctions.forEach(lName => { + if (fName === lName) { + assert.deepStrictEqual(calledArgs[fName], [ + `${fName} called %s`, + 'param1', + ]); + } else { + assert.strictEqual(calledArgs[lName], null); + } + }); + }); + + it(`NoopLogger should implement all functions and not throw when ${fName} called`, () => { + const testLogger = createNoopDiagLogger(); + + assert.ok(typeof testLogger[fName], 'function'); + testLogger[fName](`${fName} called %s`, 'param1'); + }); + }); + }); +}); diff --git a/packages/opentelemetry-core/src/common/ConsoleLogger.ts b/packages/opentelemetry-core/src/common/ConsoleLogger.ts index 5dfe9e59882..ec430363af1 100644 --- a/packages/opentelemetry-core/src/common/ConsoleLogger.ts +++ b/packages/opentelemetry-core/src/common/ConsoleLogger.ts @@ -18,6 +18,10 @@ import { Logger } from '@opentelemetry/api'; import { LogLevel } from './types'; import { getEnv } from '../platform'; +/** + * @deprecated This class will be removed prior to v1.0, use {@link DiagConsoleLogger} from the api. + * @see {@link DiagLogLevel} {@link diagLogLevelFilter} {@link DiagConsoleLogger} from the api + */ export class ConsoleLogger implements Logger { constructor(level: LogLevel = getEnv().OTEL_LOG_LEVEL) { if (level >= LogLevel.DEBUG) { diff --git a/packages/opentelemetry-core/src/common/logging-error-handler.ts b/packages/opentelemetry-core/src/common/logging-error-handler.ts index 5925f4510bb..b3b34852084 100644 --- a/packages/opentelemetry-core/src/common/logging-error-handler.ts +++ b/packages/opentelemetry-core/src/common/logging-error-handler.ts @@ -14,9 +14,14 @@ * limitations under the License. */ -import { Logger, Exception } from '@opentelemetry/api'; -import { ConsoleLogger } from './ConsoleLogger'; -import { ErrorHandler, LogLevel } from './types'; +import { + Logger, + Exception, + DiagLogLevel, + DiagConsoleLogger, + createLogLevelDiagLogger, +} from '@opentelemetry/api'; +import { ErrorHandler } from './types'; /** * Returns a function that logs an error using the provided logger, or a @@ -24,7 +29,9 @@ import { ErrorHandler, LogLevel } from './types'; * @param {Logger} logger */ export function loggingErrorHandler(logger?: Logger): ErrorHandler { - logger = logger ?? new ConsoleLogger(LogLevel.ERROR); + logger = + logger ?? + createLogLevelDiagLogger(DiagLogLevel.ERROR, new DiagConsoleLogger()); return (ex: Exception) => { logger!.error(stringifyException(ex)); }; diff --git a/packages/opentelemetry-core/src/common/types.ts b/packages/opentelemetry-core/src/common/types.ts index 6e973c6f806..d24f7f1e5c8 100644 --- a/packages/opentelemetry-core/src/common/types.ts +++ b/packages/opentelemetry-core/src/common/types.ts @@ -16,6 +16,9 @@ import { Exception } from '@opentelemetry/api'; +/** @deprecated This enum will be removed prior to v1.0, use the {@link DiagLogLevel} + * @see {@link DiagLogLevel} from the api + */ export enum LogLevel { ERROR, WARN,