feat(diag-logger): introduce a new global level api.diag for internal diagnostic logging (#1880)
Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
This commit is contained in:
parent
000a8ac099
commit
1d682c2f75
|
|
@ -78,6 +78,9 @@ package.json.lerna_backup
|
|||
# VsCode configs
|
||||
.vscode/
|
||||
|
||||
#Visual Studio
|
||||
.vs/
|
||||
|
||||
#IDEA
|
||||
.idea
|
||||
*.iml
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
// },
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<DiagLogFunction>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
@ -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<T> = (version: number) => T;
|
||||
type OtelGlobal = Partial<{
|
||||
[GLOBAL_CONTEXT_MANAGER_API_KEY]: Get<ContextManager>;
|
||||
[GLOBAL_PROPAGATION_API_KEY]: Get<TextMapPropagator>;
|
||||
[GLOBAL_TRACE_API_KEY]: Get<TracerProvider>;
|
||||
[GLOBAL_DIAG_LOGGER_API_KEY]: Get<DiagAPI>;
|
||||
}>;
|
||||
|
||||
export const _global = _globalThis as OtelGlobal;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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<DiagLogFunction>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
@ -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<keyof DiagLogger> = [
|
||||
'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;
|
||||
}
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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[]) {}
|
||||
|
|
|
|||
|
|
@ -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<keyof Console> = [
|
||||
'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);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -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<keyof Logger> = [
|
||||
'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<keyof DiagLogger>;
|
||||
}> = [
|
||||
{ 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue