Skip to content
This repository has been archived by the owner on Nov 10, 2022. It is now read-only.

Commit

Permalink
chore: refactor diag logger (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
dyladan committed Feb 25, 2021
1 parent ed9ba80 commit fb98977
Show file tree
Hide file tree
Showing 15 changed files with 256 additions and 314 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
"@typescript-eslint/no-inferrable-types": ["error", { ignoreProperties: true }],
"arrow-parens": ["error", "as-needed"],
"prettier/prettier": ["error", { "singleQuote": true, "arrowParens": "avoid" }],
"prefer-spread": "off",
"node/no-deprecated-api": ["warn"],
"header/header": [2, "block", [{
pattern: / \* Copyright The OpenTelemetry Authors[\r\n]+ \*[\r\n]+ \* Licensed under the Apache License, Version 2\.0 \(the \"License\"\);[\r\n]+ \* you may not use this file except in compliance with the License\.[\r\n]+ \* You may obtain a copy of the License at[\r\n]+ \*[\r\n]+ \* https:\/\/www\.apache\.org\/licenses\/LICENSE-2\.0[\r\n]+ \*[\r\n]+ \* Unless required by applicable law or agreed to in writing, software[\r\n]+ \* distributed under the License is distributed on an \"AS IS\" BASIS,[\r\n]+ \* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\.[\r\n]+ \* See the License for the specific language governing permissions and[\r\n]+ \* limitations under the License\./gm,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p .",
"compile": "tsc --build",
"docs-test": "linkinator docs/out --silent --skip david-dm.org",
"docs": "typedoc --tsconfig tsconfig.docs.json",
"docs": "typedoc",
"lint:fix": "eslint src test --ext .ts --fix",
"lint": "eslint src test --ext .ts",
"test:browser": "nyc karma start --single-run",
Expand Down
114 changes: 53 additions & 61 deletions src/api/diag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,24 @@
* limitations under the License.
*/

import {
DiagLogger,
DiagLogFunction,
createNoopDiagLogger,
diagLoggerFunctions,
} from '../diag/logger';
import { DiagLogLevel, createLogLevelDiagLogger } from '../diag/logLevel';
import { createLogLevelDiagLogger } from '../diag/internal/logLevelLogger';
import { createNoopDiagLogger } from '../diag/internal/noopLogger';
import { DiagLogFunction, DiagLogger, DiagLogLevel } from '../diag/types';
import {
API_BACKWARDS_COMPATIBILITY_VERSION,
GLOBAL_DIAG_LOGGER_API_KEY,
makeGetter,
_global,
} from './global-utils';

function nop() {}

/** 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;
return Object.assign(
{ disable: nop, setLogger: nop },
createNoopDiagLogger()
);
}

/**
Expand Down Expand Up @@ -71,21 +66,18 @@ export class DiagAPI implements DiagLogger {
* @private
*/
private constructor() {
let _logLevel: DiagLogLevel = DiagLogLevel.INFO;
let _filteredLogger: DiagLogger | null;
let _logger: DiagLogger = createNoopDiagLogger();
let _filteredLogger: DiagLogger | undefined;

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>
);
}
// shortcut if logger not set
if (!_filteredLogger) return;
return _filteredLogger[funcName].apply(
_filteredLogger,
// work around Function.prototype.apply types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
arguments as any
);
};
}

Expand All @@ -94,57 +86,57 @@ export class DiagAPI implements DiagLogger {

// 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 (!logger || logger !== self) {
// Simple special case to avoid any possible infinite recursion on the logging functions
_logger = logger || createNoopDiagLogger();
_filteredLogger = createLogLevelDiagLogger(_logLevel, _logger);
self.setLogger = (
logger: DiagLogger,
logLevel: DiagLogLevel = DiagLogLevel.INFO
) => {
if (logger === self) {
if (_filteredLogger) {
const err = new Error(
'Cannot use diag as the logger for itself. Please use a DiagLogger implementation like ConsoleDiagLogger or a custom implementation'
);
_filteredLogger.error(err.stack ?? err.message);
logger = _filteredLogger;
} else {
// There isn't much we can do here.
// Logging to the console might break the user application.
return;
}
}

return prevLogger;
_filteredLogger = createLogLevelDiagLogger(logLevel, logger);
};

self.setLogLevel = (maxLogLevel: DiagLogLevel) => {
if (maxLogLevel !== _logLevel) {
_logLevel = maxLogLevel;
if (_logger) {
_filteredLogger = createLogLevelDiagLogger(maxLogLevel, _logger);
}
}
self.disable = () => {
_filteredLogger = undefined;
};

for (let i = 0; i < diagLoggerFunctions.length; i++) {
const name = diagLoggerFunctions[i];
self[name] = _logProxy(name);
}
self.verbose = _logProxy('verbose');
self.debug = _logProxy('debug');
self.info = _logProxy('info');
self.warn = _logProxy('warn');
self.error = _logProxy('error');
}

/**
* 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 - [Optional] The DiagLogger instance to set as the default logger, if not provided it will set it back as a noop
* Set the global DiagLogger and DiagLogLevel.
* If a global diag logger is already set, this will override it.
*
* @param logger - [Optional] The DiagLogger instance to set as the default logger.
* @param logLevel - [Optional] The DiagLogLevel used to filter logs sent to the logger. If not provided it will default to INFO.
* @returns The previously registered DiagLogger
*/
public setLogger!: (logger?: DiagLogger) => DiagLogger;

/** Set the default maximum diagnostic logging level */
public setLogLevel!: (maxLogLevel: DiagLogLevel) => void;
public setLogger!: (logger: DiagLogger, logLevel?: DiagLogLevel) => void;

// DiagLogger implementation
public verbose!: DiagLogFunction;
public debug!: DiagLogFunction;
public info!: DiagLogFunction;
public warn!: DiagLogFunction;
public error!: DiagLogFunction;

/**
* Unregister the global logger and return to Noop
*/
public disable!: () => void;
}
2 changes: 1 addition & 1 deletion src/diag/consoleLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { DiagLogger, DiagLogFunction } from './logger';
import { DiagLogger, DiagLogFunction } from './types';

const consoleMap: { n: keyof DiagLogger; c: keyof Console }[] = [
{ n: 'error', c: 'error' },
Expand Down
18 changes: 18 additions & 0 deletions src/diag/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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.
*/

export * from './consoleLogger';
export * from './types';
51 changes: 51 additions & 0 deletions src/diag/internal/logLevelLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { DiagLogFunction, DiagLogger, DiagLogLevel } from '../types';

export function createLogLevelDiagLogger(
maxLevel: DiagLogLevel,
logger: DiagLogger
): DiagLogger {
if (maxLevel < DiagLogLevel.NONE) {
maxLevel = DiagLogLevel.NONE;
} else if (maxLevel > DiagLogLevel.ALL) {
maxLevel = DiagLogLevel.ALL;
}

// In case the logger is null or undefined
logger = logger || {};

function _filterFunc(
funcName: keyof DiagLogger,
theLevel: DiagLogLevel
): DiagLogFunction {
const theFunc = logger[funcName];

if (typeof theFunc === 'function' && maxLevel >= theLevel) {
return theFunc.bind(logger);
}
return function () {};
}

return {
error: _filterFunc('error', DiagLogLevel.ERROR),
warn: _filterFunc('warn', DiagLogLevel.WARN),
info: _filterFunc('info', DiagLogLevel.INFO),
debug: _filterFunc('debug', DiagLogLevel.DEBUG),
verbose: _filterFunc('verbose', DiagLogLevel.VERBOSE),
};
}
34 changes: 34 additions & 0 deletions src/diag/internal/noopLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 } from '../types';

function noopLogFunction() {}

/**
* Returns a No-Op Diagnostic logger where all messages do nothing.
* @implements {@link DiagLogger}
* @returns {DiagLogger}
*/
export function createNoopDiagLogger(): DiagLogger {
return {
verbose: noopLogFunction,
debug: noopLogFunction,
info: noopLogFunction,
warn: noopLogFunction,
error: noopLogFunction,
};
}

0 comments on commit fb98977

Please sign in to comment.