Skip to content

Commit b903bce

Browse files
authored
feat(instrumentation-winston): Allow log level to be configured for log sending (#2016)
* Allow log level to be configured for log sending * Support other winston levels * Updating tests, transport callback update
1 parent df2fa5a commit b903bce

File tree

8 files changed

+225
-8
lines changed

8 files changed

+225
-8
lines changed

package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/winston-transport/src/OpenTelemetryTransportV3.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,15 @@ export class OpenTelemetryTransportV3 extends TransportStream {
2727
this._logger = logs.getLogger('@opentelemetry/winston-transport', VERSION);
2828
}
2929

30-
public override log(info: any, next: () => void) {
30+
public override log(info: any, callback: () => void) {
3131
try {
3232
emitLogRecord(info, this._logger);
3333
} catch (error) {
3434
this.emit('warn', error);
3535
}
36-
setImmediate(() => {
37-
this.emit('logged', info);
38-
});
39-
if (next) {
40-
setImmediate(next);
36+
this.emit('logged', info);
37+
if (callback) {
38+
callback();
4139
}
4240
}
4341
}

plugins/node/opentelemetry-instrumentation-winston/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ logger.info('foobar');
6060
| Option | Type | Description |
6161
| ----------------------- | ----------------- | ----------- |
6262
| `disableLogSending` | `boolean` | Whether to disable [log sending](#log-sending). Default `false`. |
63+
| `logSeverity` | `SeverityNumber` | Control severity level for [log sending](#log-sending). Default `SeverityNumber.UNSPECIFIED`, it will use Winston Logger's current level when unspecified. |
6364
| `disableLogCorrelation` | `boolean` | Whether to disable [log correlation](#log-correlation). Default `false`. |
6465
| `logHook` | `LogHookFunction` | An option hook to inject additional context to a log record after trace-context has been added. This requires `disableLogCorrelation` to be false. |
6566

plugins/node/opentelemetry-instrumentation-winston/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"winston2": "npm:[email protected]"
6565
},
6666
"dependencies": {
67+
"@opentelemetry/api-logs": "^0.50.0",
6768
"@opentelemetry/instrumentation": "^0.50.0"
6869
},
6970
"homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-winston#readme"

plugins/node/opentelemetry-instrumentation-winston/src/instrumentation.ts

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { context, trace, isSpanContextValid, Span } from '@opentelemetry/api';
18+
import { SeverityNumber } from '@opentelemetry/api-logs';
1819
import {
1920
InstrumentationBase,
2021
InstrumentationNodeModuleDefinition,
@@ -206,7 +207,17 @@ export class WinstonInstrumentation extends InstrumentationBase {
206207
let newTransports = Array.isArray(originalTransports)
207208
? originalTransports
208209
: [];
209-
const openTelemetryTransport = new OpenTelemetryTransportV3();
210+
let transportOptions = {};
211+
if (config.logSeverity) {
212+
const winstonLevel = instrumentation._winstonLevelFromSeverity(
213+
config.logSeverity,
214+
args[0].levels
215+
);
216+
transportOptions = { level: winstonLevel };
217+
}
218+
const openTelemetryTransport = new OpenTelemetryTransportV3(
219+
transportOptions
220+
);
210221
if (originalTransports && !Array.isArray(originalTransports)) {
211222
newTransports = [originalTransports];
212223
}
@@ -244,4 +255,118 @@ export class WinstonInstrumentation extends InstrumentationBase {
244255
}
245256
return record;
246257
}
258+
259+
private _winstonLevelFromSeverity(
260+
severity: SeverityNumber,
261+
winstonLevels: { [key: string]: number } | undefined
262+
): string | undefined {
263+
if (winstonLevels) {
264+
if (isNpmLevels(winstonLevels)) {
265+
if (severity >= SeverityNumber.ERROR) {
266+
return 'error';
267+
} else if (severity >= SeverityNumber.WARN) {
268+
return 'warn';
269+
} else if (severity >= SeverityNumber.INFO) {
270+
return 'info';
271+
} else if (severity >= SeverityNumber.DEBUG3) {
272+
return 'http';
273+
} else if (severity >= SeverityNumber.DEBUG2) {
274+
return 'verbose';
275+
} else if (severity >= SeverityNumber.DEBUG) {
276+
return 'debug';
277+
} else if (severity >= SeverityNumber.TRACE) {
278+
return 'silly';
279+
}
280+
} else if (isCliLevels(winstonLevels)) {
281+
if (severity >= SeverityNumber.ERROR) {
282+
return 'error';
283+
} else if (severity >= SeverityNumber.WARN) {
284+
return 'warn';
285+
} else if (severity >= SeverityNumber.INFO3) {
286+
return 'help';
287+
} else if (severity >= SeverityNumber.INFO2) {
288+
return 'data';
289+
} else if (severity >= SeverityNumber.INFO) {
290+
return 'info';
291+
} else if (severity >= SeverityNumber.DEBUG) {
292+
return 'debug';
293+
} else if (severity >= SeverityNumber.TRACE4) {
294+
return 'prompt';
295+
} else if (severity >= SeverityNumber.TRACE3) {
296+
return 'verbose';
297+
} else if (severity >= SeverityNumber.TRACE2) {
298+
return 'input';
299+
} else if (severity >= SeverityNumber.TRACE) {
300+
return 'silly';
301+
}
302+
} else if (isSyslogLevels(winstonLevels)) {
303+
if (severity >= SeverityNumber.FATAL2) {
304+
return 'emerg';
305+
} else if (severity >= SeverityNumber.FATAL) {
306+
return 'alert';
307+
} else if (severity >= SeverityNumber.ERROR2) {
308+
return 'crit';
309+
} else if (severity >= SeverityNumber.ERROR) {
310+
return 'error';
311+
} else if (severity >= SeverityNumber.WARN) {
312+
return 'warning';
313+
} else if (severity >= SeverityNumber.INFO2) {
314+
return 'notice';
315+
} else if (severity >= SeverityNumber.INFO) {
316+
return 'info';
317+
} else if (severity >= SeverityNumber.TRACE) {
318+
return 'debug';
319+
}
320+
}
321+
// Unknown level
322+
this._diag.warn(
323+
'failed to configure severity with existing winston levels'
324+
);
325+
}
326+
327+
function isCliLevels(arg: any): boolean {
328+
return (
329+
arg &&
330+
arg.error !== undefined &&
331+
arg.warn &&
332+
arg.help &&
333+
arg.data &&
334+
arg.info &&
335+
arg.debug &&
336+
arg.prompt &&
337+
arg.verbose &&
338+
arg.input &&
339+
arg.silly
340+
);
341+
}
342+
343+
function isNpmLevels(arg: any): boolean {
344+
return (
345+
arg &&
346+
arg.error !== undefined &&
347+
arg.warn &&
348+
arg.info &&
349+
arg.http &&
350+
arg.verbose &&
351+
arg.debug &&
352+
arg.silly
353+
);
354+
}
355+
356+
function isSyslogLevels(arg: any): boolean {
357+
return (
358+
arg &&
359+
arg.emerg !== undefined &&
360+
arg.alert &&
361+
arg.crit &&
362+
arg.error &&
363+
arg.warning &&
364+
arg.notice &&
365+
arg.info &&
366+
arg.debug
367+
);
368+
}
369+
370+
return;
371+
}
247372
}

plugins/node/opentelemetry-instrumentation-winston/src/internal-types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type {
2222
export type Winston3LogMethod = Winston3Logger['write'];
2323
export type Winston3ConfigureMethod = Winston3Logger['configure'];
2424
export type { Winston3Logger };
25-
2625
export type { Winston2LogMethod };
2726
export type Winston2LoggerModule = {
2827
Logger: Winston2Logger & {

plugins/node/opentelemetry-instrumentation-winston/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { Span } from '@opentelemetry/api';
18+
import { SeverityNumber } from '@opentelemetry/api-logs';
1819
import { InstrumentationConfig } from '@opentelemetry/instrumentation';
1920

2021
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -28,6 +29,11 @@ export interface WinstonInstrumentationConfig extends InstrumentationConfig {
2829
*/
2930
disableLogSending?: boolean;
3031

32+
/**
33+
* Control Log sending severity level, logs will be sent for specified severity and higher.
34+
*/
35+
logSeverity?: SeverityNumber;
36+
3137
/**
3238
* Whether to disable the injection trace-context fields, and possibly other
3339
* fields from `logHook()`, into log records for log correlation.

plugins/node/opentelemetry-instrumentation-winston/test/winston.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,4 +383,89 @@ describe('WinstonInstrumentation', () => {
383383
}
384384
});
385385
});
386+
describe('logSeverity config', () => {
387+
beforeEach(() => {
388+
instrumentation.setConfig({
389+
disableLogSending: false,
390+
});
391+
memoryLogExporter.getFinishedLogRecords().length = 0; // clear
392+
});
393+
394+
it('npm levels', () => {
395+
if (!isWinston2) {
396+
instrumentation.setConfig({
397+
disableLogSending: false,
398+
logSeverity: SeverityNumber.DEBUG,
399+
});
400+
initLogger(LevelsType.npm);
401+
logger.log('silly', 'silly');
402+
logger.log('debug', 'debug');
403+
logger.log('verbose', 'verbose');
404+
logger.log('http', 'http');
405+
logger.log('info', 'info');
406+
logger.log('warn', 'warn');
407+
logger.log('error', 'error');
408+
const logRecords = memoryLogExporter.getFinishedLogRecords();
409+
assert.strictEqual(logRecords.length, 6);
410+
assert.strictEqual(logRecords[0].body, 'debug');
411+
assert.strictEqual(logRecords[1].body, 'verbose');
412+
assert.strictEqual(logRecords[2].body, 'http');
413+
assert.strictEqual(logRecords[3].body, 'info');
414+
assert.strictEqual(logRecords[4].body, 'warn');
415+
assert.strictEqual(logRecords[5].body, 'error');
416+
}
417+
});
418+
419+
it('cli levels', () => {
420+
if (!isWinston2) {
421+
instrumentation.setConfig({
422+
disableLogSending: false,
423+
logSeverity: SeverityNumber.INFO,
424+
});
425+
initLogger(LevelsType.cli);
426+
logger.log('silly', 'silly');
427+
logger.log('input', 'input');
428+
logger.log('verbose', 'verbose');
429+
logger.log('prompt', 'prompt');
430+
logger.log('debug', 'debug');
431+
logger.log('info', 'info');
432+
logger.log('data', 'data');
433+
logger.log('help', 'help');
434+
logger.log('warn', 'warn');
435+
logger.log('error', 'error');
436+
const logRecords = memoryLogExporter.getFinishedLogRecords();
437+
assert.strictEqual(logRecords.length, 5);
438+
assert.strictEqual(logRecords[0].body, 'info');
439+
assert.strictEqual(logRecords[1].body, 'data');
440+
assert.strictEqual(logRecords[2].body, 'help');
441+
assert.strictEqual(logRecords[3].body, 'warn');
442+
assert.strictEqual(logRecords[4].body, 'error');
443+
}
444+
});
445+
446+
it('syslog levels', () => {
447+
if (!isWinston2) {
448+
instrumentation.setConfig({
449+
disableLogSending: false,
450+
logSeverity: SeverityNumber.WARN,
451+
});
452+
initLogger(LevelsType.syslog);
453+
logger.log('debug', 'debug');
454+
logger.log('info', 'info');
455+
logger.log('notice', 'notice');
456+
logger.log('warning', 'warning');
457+
logger.log('error', 'error');
458+
logger.log('crit', 'crit');
459+
logger.log('alert', 'alert');
460+
logger.log('emerg', 'emerg');
461+
const logRecords = memoryLogExporter.getFinishedLogRecords();
462+
assert.strictEqual(logRecords.length, 5);
463+
assert.strictEqual(logRecords[0].body, 'warning');
464+
assert.strictEqual(logRecords[1].body, 'error');
465+
assert.strictEqual(logRecords[2].body, 'crit');
466+
assert.strictEqual(logRecords[3].body, 'alert');
467+
assert.strictEqual(logRecords[4].body, 'emerg');
468+
}
469+
});
470+
});
386471
});

0 commit comments

Comments
 (0)