Skip to content
This repository was archived by the owner on Nov 17, 2025. It is now read-only.

Commit fb98977

Browse files
authored
chore: refactor diag logger (#9)
1 parent ed9ba80 commit fb98977

File tree

15 files changed

+256
-314
lines changed

15 files changed

+256
-314
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module.exports = {
3232
"@typescript-eslint/no-inferrable-types": ["error", { ignoreProperties: true }],
3333
"arrow-parens": ["error", "as-needed"],
3434
"prettier/prettier": ["error", { "singleQuote": true, "arrowParens": "avoid" }],
35+
"prefer-spread": "off",
3536
"node/no-deprecated-api": ["warn"],
3637
"header/header": [2, "block", [{
3738
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,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p .",
1717
"compile": "tsc --build",
1818
"docs-test": "linkinator docs/out --silent --skip david-dm.org",
19-
"docs": "typedoc --tsconfig tsconfig.docs.json",
19+
"docs": "typedoc",
2020
"lint:fix": "eslint src test --ext .ts --fix",
2121
"lint": "eslint src test --ext .ts",
2222
"test:browser": "nyc karma start --single-run",

src/api/diag.ts

Lines changed: 53 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,24 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {
18-
DiagLogger,
19-
DiagLogFunction,
20-
createNoopDiagLogger,
21-
diagLoggerFunctions,
22-
} from '../diag/logger';
23-
import { DiagLogLevel, createLogLevelDiagLogger } from '../diag/logLevel';
17+
import { createLogLevelDiagLogger } from '../diag/internal/logLevelLogger';
18+
import { createNoopDiagLogger } from '../diag/internal/noopLogger';
19+
import { DiagLogFunction, DiagLogger, DiagLogLevel } from '../diag/types';
2420
import {
2521
API_BACKWARDS_COMPATIBILITY_VERSION,
2622
GLOBAL_DIAG_LOGGER_API_KEY,
2723
makeGetter,
2824
_global,
2925
} from './global-utils';
3026

27+
function nop() {}
28+
3129
/** Internal simple Noop Diag API that returns a noop logger and does not allow any changes */
3230
function noopDiagApi(): DiagAPI {
33-
const noopApi = createNoopDiagLogger() as DiagAPI;
34-
35-
noopApi.getLogger = () => noopApi;
36-
noopApi.setLogger = noopApi.getLogger;
37-
noopApi.setLogLevel = () => {};
38-
39-
return noopApi;
31+
return Object.assign(
32+
{ disable: nop, setLogger: nop },
33+
createNoopDiagLogger()
34+
);
4035
}
4136

4237
/**
@@ -71,21 +66,18 @@ export class DiagAPI implements DiagLogger {
7166
* @private
7267
*/
7368
private constructor() {
74-
let _logLevel: DiagLogLevel = DiagLogLevel.INFO;
75-
let _filteredLogger: DiagLogger | null;
76-
let _logger: DiagLogger = createNoopDiagLogger();
69+
let _filteredLogger: DiagLogger | undefined;
7770

7871
function _logProxy(funcName: keyof DiagLogger): DiagLogFunction {
7972
return function () {
80-
const orgArguments = arguments as unknown;
81-
const theLogger = _filteredLogger || _logger;
82-
const theFunc = theLogger[funcName];
83-
if (typeof theFunc === 'function') {
84-
return theFunc.apply(
85-
theLogger,
86-
orgArguments as Parameters<DiagLogFunction>
87-
);
88-
}
73+
// shortcut if logger not set
74+
if (!_filteredLogger) return;
75+
return _filteredLogger[funcName].apply(
76+
_filteredLogger,
77+
// work around Function.prototype.apply types
78+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
79+
arguments as any
80+
);
8981
};
9082
}
9183

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

9587
// DiagAPI specific functions
9688

97-
self.getLogger = (): DiagLogger => {
98-
// Return itself if no existing logger is defined (defaults effectively to a Noop)
99-
return _logger;
100-
};
101-
102-
self.setLogger = (logger?: DiagLogger): DiagLogger => {
103-
const prevLogger = _logger;
104-
if (!logger || logger !== self) {
105-
// Simple special case to avoid any possible infinite recursion on the logging functions
106-
_logger = logger || createNoopDiagLogger();
107-
_filteredLogger = createLogLevelDiagLogger(_logLevel, _logger);
89+
self.setLogger = (
90+
logger: DiagLogger,
91+
logLevel: DiagLogLevel = DiagLogLevel.INFO
92+
) => {
93+
if (logger === self) {
94+
if (_filteredLogger) {
95+
const err = new Error(
96+
'Cannot use diag as the logger for itself. Please use a DiagLogger implementation like ConsoleDiagLogger or a custom implementation'
97+
);
98+
_filteredLogger.error(err.stack ?? err.message);
99+
logger = _filteredLogger;
100+
} else {
101+
// There isn't much we can do here.
102+
// Logging to the console might break the user application.
103+
return;
104+
}
108105
}
109106

110-
return prevLogger;
107+
_filteredLogger = createLogLevelDiagLogger(logLevel, logger);
111108
};
112109

113-
self.setLogLevel = (maxLogLevel: DiagLogLevel) => {
114-
if (maxLogLevel !== _logLevel) {
115-
_logLevel = maxLogLevel;
116-
if (_logger) {
117-
_filteredLogger = createLogLevelDiagLogger(maxLogLevel, _logger);
118-
}
119-
}
110+
self.disable = () => {
111+
_filteredLogger = undefined;
120112
};
121113

122-
for (let i = 0; i < diagLoggerFunctions.length; i++) {
123-
const name = diagLoggerFunctions[i];
124-
self[name] = _logProxy(name);
125-
}
114+
self.verbose = _logProxy('verbose');
115+
self.debug = _logProxy('debug');
116+
self.info = _logProxy('info');
117+
self.warn = _logProxy('warn');
118+
self.error = _logProxy('error');
126119
}
127120

128121
/**
129-
* Return the currently configured logger instance, if no logger has been configured
130-
* it will return itself so any log level filtering will still be applied in this case.
131-
*/
132-
public getLogger!: () => DiagLogger;
133-
134-
/**
135-
* Set the DiagLogger instance
136-
* @param logger - [Optional] The DiagLogger instance to set as the default logger, if not provided it will set it back as a noop
122+
* Set the global DiagLogger and DiagLogLevel.
123+
* If a global diag logger is already set, this will override it.
124+
*
125+
* @param logger - [Optional] The DiagLogger instance to set as the default logger.
126+
* @param logLevel - [Optional] The DiagLogLevel used to filter logs sent to the logger. If not provided it will default to INFO.
137127
* @returns The previously registered DiagLogger
138128
*/
139-
public setLogger!: (logger?: DiagLogger) => DiagLogger;
140-
141-
/** Set the default maximum diagnostic logging level */
142-
public setLogLevel!: (maxLogLevel: DiagLogLevel) => void;
129+
public setLogger!: (logger: DiagLogger, logLevel?: DiagLogLevel) => void;
143130

144131
// DiagLogger implementation
145132
public verbose!: DiagLogFunction;
146133
public debug!: DiagLogFunction;
147134
public info!: DiagLogFunction;
148135
public warn!: DiagLogFunction;
149136
public error!: DiagLogFunction;
137+
138+
/**
139+
* Unregister the global logger and return to Noop
140+
*/
141+
public disable!: () => void;
150142
}

src/diag/consoleLogger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { DiagLogger, DiagLogFunction } from './logger';
17+
import { DiagLogger, DiagLogFunction } from './types';
1818

1919
const consoleMap: { n: keyof DiagLogger; c: keyof Console }[] = [
2020
{ n: 'error', c: 'error' },

src/diag/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export * from './consoleLogger';
18+
export * from './types';
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { DiagLogFunction, DiagLogger, DiagLogLevel } from '../types';
18+
19+
export function createLogLevelDiagLogger(
20+
maxLevel: DiagLogLevel,
21+
logger: DiagLogger
22+
): DiagLogger {
23+
if (maxLevel < DiagLogLevel.NONE) {
24+
maxLevel = DiagLogLevel.NONE;
25+
} else if (maxLevel > DiagLogLevel.ALL) {
26+
maxLevel = DiagLogLevel.ALL;
27+
}
28+
29+
// In case the logger is null or undefined
30+
logger = logger || {};
31+
32+
function _filterFunc(
33+
funcName: keyof DiagLogger,
34+
theLevel: DiagLogLevel
35+
): DiagLogFunction {
36+
const theFunc = logger[funcName];
37+
38+
if (typeof theFunc === 'function' && maxLevel >= theLevel) {
39+
return theFunc.bind(logger);
40+
}
41+
return function () {};
42+
}
43+
44+
return {
45+
error: _filterFunc('error', DiagLogLevel.ERROR),
46+
warn: _filterFunc('warn', DiagLogLevel.WARN),
47+
info: _filterFunc('info', DiagLogLevel.INFO),
48+
debug: _filterFunc('debug', DiagLogLevel.DEBUG),
49+
verbose: _filterFunc('verbose', DiagLogLevel.VERBOSE),
50+
};
51+
}

src/diag/internal/noopLogger.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { DiagLogger } from '../types';
18+
19+
function noopLogFunction() {}
20+
21+
/**
22+
* Returns a No-Op Diagnostic logger where all messages do nothing.
23+
* @implements {@link DiagLogger}
24+
* @returns {DiagLogger}
25+
*/
26+
export function createNoopDiagLogger(): DiagLogger {
27+
return {
28+
verbose: noopLogFunction,
29+
debug: noopLogFunction,
30+
info: noopLogFunction,
31+
warn: noopLogFunction,
32+
error: noopLogFunction,
33+
};
34+
}

0 commit comments

Comments
 (0)