Skip to content

Commit 76413f0

Browse files
committed
fix: address review comments, add upgrade notes and tests
1 parent 7feca26 commit 76413f0

File tree

10 files changed

+376
-54
lines changed

10 files changed

+376
-54
lines changed

README.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,181 @@ To request automatic tracing support for a module not on this list, please [file
244244

245245
## Upgrade guidelines
246246

247+
### 0.17.0 to ???
248+
249+
[PR-1880](https://github.com/open-telemetry/opentelemetry-js/pull/1880) feat(diag-logger): introduce a new global level api.diag for internal diagnostic logging
250+
251+
[PR-1925](https://github.com/open-telemetry/opentelemetry-js/pull/1925) feat(diag-logger): part 2 - breaking changes - remove api.Logger, api.NoopLogger, core.LogLevel, core.ConsoleLogger
252+
253+
- These PR's remove the previous ```Logger``` and ```LogLevel``` implementations and change the way you should use the replacement ```DiagLogger``` and ```DiagLogLevel```, below are simple examples of how to change your existing usages.
254+
255+
#### New Usage patterns
256+
257+
- **Global Diagnostic Logging with no passed logger**
258+
259+
The new global [```api.diag```](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-api/src/api/diag.ts#L32) not only provides the ability to set a global diagnostic logger ```setLogger()``` and logging level ```setLogLevel()```, it is also a ```DiagLogger``` implementation and can be used directly to log diagnostic messages.
260+
261+
And the helper functions will fallback to this global DiagLogger when no logger is provided or configured.
262+
263+
```javascript
264+
// Previously (Not supported)
265+
266+
// Now
267+
import { diag } from "@opentelemetry/api";
268+
269+
diag.debug("...");
270+
271+
// Setting the default Global logger to use the Console
272+
import { diag, DiagConsoleLogger } from "@opentelemetry/api";
273+
diag.setLogger(new DiagConsoleLogger())
274+
275+
// Setting the log level limit applied to the global logger
276+
import { diag, DiagLogLevel } from "@opentelemetry/api";
277+
diag.setLogLevel(DiagLogLevel.ERROR);
278+
```
279+
280+
- **Common helper for passing and fetching a diagnostic logger via optional configuration**
281+
282+
```javascript
283+
// Previously
284+
import { Logger } from "@opentelemetry/api";
285+
interface MyConfig {
286+
logger: Logger
287+
}
288+
289+
// and usage
290+
import { NoopLogger } from "@opentelemetry/api";
291+
export function myFunction(config: MyConfig) {
292+
let logger = config.logger || new NoopLogger();
293+
}
294+
295+
// Now
296+
import { DiagLoggerConfig } from "@opentelemetry/api";
297+
interface MyConfig extends DiagLoggerConfig {
298+
// DiagLoggerConfig defines and optional DiagLogger and DiagLogLevel
299+
// So you can specify either or both and no need to define within
300+
// your own configuration.
301+
}
302+
303+
// And usage
304+
import { getDiagLoggerFromConfig } from "@opentelemetry/api";
305+
export function myFunction(config?: MyConfig) {
306+
// Config can be optional and is handled by the helper
307+
// Will default to api.diag instance if no logger is defined
308+
// and any api.diag logging level will also obeyed
309+
let logger = getDiagLoggerFromConfig(config);
310+
}
311+
312+
// or
313+
import { getDiagLoggerFromConfig, DiagConsoleLogger } from "@opentelemetry/api";
314+
export function myFunction(config?: MyConfig) {
315+
// Defaults to a Console logger rather than the api.diag if no logger is defined // Will use any defined the logging level - defaults to ALL!
316+
let logger = getDiagLoggerFromConfig(config, () => new DiagConsoleLogger());
317+
}
318+
319+
// if you want to fallback to the default api.diag logger and IGNORE the global
320+
// logging level you need to provide a fallback function, if no log level is
321+
// in the config it would default to ALL (So use ONLY during development)
322+
import { getDiagLoggerFromConfig, diag } from "@opentelemetry/api";
323+
export function myFunction(config?: MyConfig) {
324+
let logger = getDiagLoggerFromConfig(config, () => diag.getLogger());
325+
}
326+
```
327+
328+
#### Direct update path for existing code
329+
330+
Without refactoring your existing code to use ```api.diag``` or ```getDiagLoggerFromConfig``` (recommended path), below is how you would directly upgrade to the newer version so you can get compiling and running.
331+
332+
- **api.Logger**
333+
334+
The api.Logger has been renamed for clarity to highlight that this is for internal(OpenTelemetry and supporting components) diagnostic level logging and not for usage by a consuming application to send telemetry out of the executing environment.
335+
336+
The new ```DiagLogger``` provides the same set of functions as the previous Logger interface plus a few more, so direct replacement of the Logger is a simple case of changing to the new (renamed) type which is also exported by api.
337+
338+
```typescript
339+
// Previously
340+
import { Logger } from "@opentelemetry/api";
341+
export function(myLogger: Logger) {
342+
myLogger.debug("...");
343+
myLogger.info("...");
344+
myLogger.warn("...");
345+
myLogger.error("...");
346+
}
347+
348+
// Now
349+
import { DiagLogger } from "@opentelemetry/api";
350+
export function(myLogger: DiagLogger) {
351+
myLogger.debug("...");
352+
myLogger.info("...");
353+
myLogger.warn("...");
354+
myLogger.error("...");
355+
356+
// New levels
357+
myLogger.verbose("..");
358+
myLogger.startup("..");
359+
myLogger.critical("..");
360+
myLogger.terminal("..");
361+
}
362+
```
363+
364+
- **api.NoopLogger**
365+
366+
To support minification the NoopLogger class has been removed in favor of a factory method and generally direct usage should no longer be required as components should use the new ```api.diag``` or ```getDiagLoggerFromConfig```.
367+
368+
```typescript
369+
// Previous direct creation
370+
import { NoopLogger } from "@opentelemetry/api";
371+
let myLogger = new NoopLogger();
372+
373+
// Now
374+
import { createNoopDiagLogger } from "@opentelemetry/api";
375+
let myLogger = createNoopDiagLogger();
376+
```
377+
378+
- **core.LogLevel**
379+
380+
The core.LogLevel enumeration has been moved to the api package and renamed for clarity as with the ```api.DiagLogger``` to highlight its usage for diagnostic logging.
381+
382+
Note: Numeric compatibility of each enum value (DEBUG, INFO, WARN, ERROR) has NOT preserved.
383+
384+
```javascript
385+
// Previously
386+
import { LogLevel } from "@opentelemetry/core";
387+
388+
let logLevel = LogLevel.DEBUG;
389+
LogLevel.ERROR === 0 // true
390+
LogLevel.WARN === 1 // true
391+
LogLevel.INFO === 2 // true
392+
LogLevel.DEBUG === 3 // true
393+
394+
// Now
395+
import { DiagLogLevel } from "@opentelemetry/api";
396+
397+
let logLevel = DiagLogLevel.DEBUG;
398+
DiagLogLevel.ERROR !== 0 // true
399+
DiagLogLevel.WARN !== 1 // true
400+
DiagLogLevel.INFO !== 2 // true
401+
DiagLogLevel.DEBUG !== 3 // true
402+
```
403+
404+
Usage of the ```getEnv().OTEL_LOG_LEVEL``` now returns a ```api.DiagLogLevel``` and not the removed ```core.LogLevel```, the environment setting continues to support conversion from ```string``` case-insensitive values of the new Enum equivalent and but explicitly does not support initializing via a numeric value (or string version i.e. "0").
405+
406+
- **core.ConsoleLogger**
407+
408+
As with the ```core.LogLevel``` this has been moved to the ```api``` package and renamed to ```DiagConsoleLogger```.
409+
410+
Note: The previous version of the ```ConsoleLogger``` supported a LogLevel "limit", this functionality has been extracted into a helper wrapper sink ```createLogLevelDiagLogger()``` so it can be applied to any DiagLogger implementation.
411+
412+
```javascript
413+
// Previously
414+
import { ConsoleLogger } from "@opentelemetry/core";
415+
let myLogger = new ConsoleLogger(LogLevel.ERROR);
416+
417+
// Now
418+
import { DiagConsoleLogger, createLogLevelDiagLogger } from "@opentelemetry/api";
419+
let myLogger = createLogLevelDiagLogger(LogLevel.ERROR, new DiagConsoleLogger());
420+
```
421+
247422
### 0.16.0 to 0.17.0
248423

249424
[PR-1855](https://github.com/open-telemetry/opentelemetry-js/pull/1855) Use instrumentation loader to load plugins and instrumentations

packages/opentelemetry-api/src/diag/config.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,17 @@ export function getDiagLoggerFromConfig(
4646
getDefaultLogger?: () => DiagLogger
4747
): DiagLogger {
4848
let theLogger;
49+
const theConfig = config || {};
4950

50-
if (config) {
51-
theLogger = config.diagLogger;
52-
if (!theLogger && getDefaultLogger) {
53-
theLogger = getDefaultLogger();
54-
}
51+
theLogger = theConfig.diagLogger;
52+
if (!theLogger && getDefaultLogger) {
53+
theLogger = getDefaultLogger();
54+
}
5555

56-
const theLogLevel = config.diagLogLevel;
57-
if (theLogLevel !== undefined && theLogLevel !== null) {
58-
// Note: If no existing 'theLogger' is defined the helper will wrap the api.diag.getLogger()
59-
theLogger = createLogLevelDiagLogger(theLogLevel, theLogger);
60-
}
56+
const theLogLevel = theConfig.diagLogLevel;
57+
if (theLogLevel !== undefined && theLogLevel !== null) {
58+
// Note: If no existing 'theLogger' is defined the helper will wrap the api.diag.getLogger()
59+
theLogger = createLogLevelDiagLogger(theLogLevel, theLogger);
6160
}
6261

6362
if (!theLogger) {
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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 * as assert from 'assert';
18+
import { diag, DiagLogLevel, getDiagLoggerFromConfig } from '../../src';
19+
import { DiagLogger, diagLoggerFunctions } from '../../src/diag/logger';
20+
21+
describe('getDiagLoggerFromConfig', () => {
22+
const calledArgs: any = {
23+
terminal: null,
24+
critical: null,
25+
error: null,
26+
warn: null,
27+
info: null,
28+
debug: null,
29+
verbose: null,
30+
startupInfo: null,
31+
};
32+
33+
let dummyLogger: DiagLogger;
34+
35+
beforeEach(() => {
36+
// mock
37+
dummyLogger = {} as DiagLogger;
38+
diagLoggerFunctions.forEach(fName => {
39+
dummyLogger[fName] = (...args: unknown[]) => {
40+
calledArgs[fName] = args;
41+
};
42+
});
43+
});
44+
45+
afterEach(() => {
46+
// restore
47+
diagLoggerFunctions.forEach(fName => {
48+
calledArgs[fName] = null;
49+
});
50+
});
51+
52+
describe('constructor', () => {
53+
it('fallback to api.diag', () => {
54+
let logger = getDiagLoggerFromConfig();
55+
assert.deepStrictEqual(logger, diag);
56+
57+
logger = getDiagLoggerFromConfig(null);
58+
assert.deepStrictEqual(logger, diag);
59+
60+
logger = getDiagLoggerFromConfig(undefined);
61+
assert.deepStrictEqual(logger, diag);
62+
63+
logger = getDiagLoggerFromConfig({});
64+
assert.deepStrictEqual(logger, diag);
65+
66+
logger = getDiagLoggerFromConfig({
67+
diagLogger: (null as unknown) as DiagLogger,
68+
});
69+
assert.deepStrictEqual(logger, diag);
70+
71+
logger = getDiagLoggerFromConfig({
72+
diagLogger: undefined,
73+
});
74+
assert.deepStrictEqual(logger, diag);
75+
});
76+
77+
it('fallback via callback', () => {
78+
let logger = getDiagLoggerFromConfig(null, () => dummyLogger);
79+
assert.deepStrictEqual(logger, dummyLogger);
80+
81+
logger = getDiagLoggerFromConfig(undefined, () => dummyLogger);
82+
assert.deepStrictEqual(logger, dummyLogger);
83+
84+
logger = getDiagLoggerFromConfig({}, () => dummyLogger);
85+
assert.deepStrictEqual(logger, dummyLogger);
86+
87+
logger = getDiagLoggerFromConfig(
88+
{
89+
diagLogger: (null as unknown) as DiagLogger,
90+
},
91+
() => dummyLogger
92+
);
93+
assert.deepStrictEqual(logger, dummyLogger);
94+
95+
logger = getDiagLoggerFromConfig(
96+
{
97+
diagLogger: undefined,
98+
},
99+
() => dummyLogger
100+
);
101+
assert.deepStrictEqual(logger, dummyLogger);
102+
});
103+
104+
diagLoggerFunctions.forEach(fName => {
105+
it(`should log with ${fName} message with no log level`, () => {
106+
const testConfig = {
107+
diagLogger: dummyLogger,
108+
};
109+
const testLogger = getDiagLoggerFromConfig(testConfig);
110+
testLogger[fName](`${fName} called %s`, 'param1');
111+
diagLoggerFunctions.forEach(lName => {
112+
if (fName === lName) {
113+
assert.deepStrictEqual(calledArgs[fName], [
114+
`${fName} called %s`,
115+
'param1',
116+
]);
117+
} else {
118+
assert.strictEqual(calledArgs[lName], null);
119+
}
120+
});
121+
});
122+
123+
it(`should log with ${fName} message with null log level`, () => {
124+
const testConfig = {
125+
diagLogger: dummyLogger,
126+
diagLogLevel: (null as unknown) as DiagLogLevel,
127+
};
128+
const testLogger = getDiagLoggerFromConfig(testConfig);
129+
testLogger[fName](`${fName} called %s`, 'param1');
130+
diagLoggerFunctions.forEach(lName => {
131+
if (fName === lName) {
132+
assert.deepStrictEqual(calledArgs[fName], [
133+
`${fName} called %s`,
134+
'param1',
135+
]);
136+
} else {
137+
assert.strictEqual(calledArgs[lName], null);
138+
}
139+
});
140+
});
141+
142+
it(`should log with ${fName} message with explicit undefined log level`, () => {
143+
const testConfig = {
144+
diagLogger: dummyLogger,
145+
diagLogLevel: undefined,
146+
};
147+
const testLogger = getDiagLoggerFromConfig(testConfig);
148+
testLogger[fName](`${fName} called %s`, 'param1');
149+
diagLoggerFunctions.forEach(lName => {
150+
if (fName === lName) {
151+
assert.deepStrictEqual(calledArgs[fName], [
152+
`${fName} called %s`,
153+
'param1',
154+
]);
155+
} else {
156+
assert.strictEqual(calledArgs[lName], null);
157+
}
158+
});
159+
});
160+
});
161+
});
162+
});

0 commit comments

Comments
 (0)