Skip to content

Commit

Permalink
feat(instrumentation-bunyan): Allow log level to be configured for lo…
Browse files Browse the repository at this point in the history
…g sending (#1919)

* Allow log level to be configured for auto instrumentation and BunyanStream

* Addressing comments

* Update

* Update plugins/node/opentelemetry-instrumentation-bunyan/test/bunyan.test.ts

Co-authored-by: Trent Mick <[email protected]>

* Add test coverage

---------

Co-authored-by: Trent Mick <[email protected]>
  • Loading branch information
hectorhdzg and trentm authored Mar 11, 2024
1 parent 9d19ca4 commit 4dc812e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Log injection can be disabled with the `disableLogCorrelation: true` option.
| Option | Type | Description |
| ----------------------- | ----------------- | ----------- |
| `disableLogSending` | `boolean` | Whether to disable [log sending](#log-sending). Default `false`. |
| `logSeverity` | `SeverityNumber` | Control severity level for [log sending](#log-sending). Default `SeverityNumber.UNSPECIFIED`, it will use Bunnyan Logger's current level when unspecified. |
| `disableLogCorrelation` | `boolean` | Whether to disable [log correlation](#log-correlation). Default `false`. |
| `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. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { BunyanInstrumentationConfig } from './types';
import { VERSION } from './version';
import { OpenTelemetryBunyanStream } from './OpenTelemetryBunyanStream';
import type * as BunyanLogger from 'bunyan';
import { SeverityNumber } from '@opentelemetry/api-logs';

const DEFAULT_CONFIG: BunyanInstrumentationConfig = {
disableLogSending: false,
Expand Down Expand Up @@ -157,10 +158,15 @@ export class BunyanInstrumentation extends InstrumentationBase<
return;
}
this._diag.debug('Adding OpenTelemetryBunyanStream to logger');
let streamLevel = logger.level();
if (config.logSeverity) {
const bunyanLevel = bunyanLevelFromSeverity(config.logSeverity);
streamLevel = bunyanLevel || streamLevel;
}
logger.addStream({
type: 'raw',
stream: new OpenTelemetryBunyanStream(),
level: logger.level(),
level: streamLevel,
});
}

Expand All @@ -182,3 +188,20 @@ export class BunyanInstrumentation extends InstrumentationBase<
);
}
}

function bunyanLevelFromSeverity(severity: SeverityNumber): string | undefined {
if (severity >= SeverityNumber.FATAL) {
return 'fatal';
} else if (severity >= SeverityNumber.ERROR) {
return 'error';
} else if (severity >= SeverityNumber.WARN) {
return 'warn';
} else if (severity >= SeverityNumber.INFO) {
return 'info';
} else if (severity >= SeverityNumber.DEBUG) {
return 'debug';
} else if (severity >= SeverityNumber.TRACE) {
return 'trace';
}
return;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { Span } from '@opentelemetry/api';
import { SeverityNumber } from '@opentelemetry/api-logs';
import { InstrumentationConfig } from '@opentelemetry/instrumentation';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -28,6 +29,11 @@ export interface BunyanInstrumentationConfig extends InstrumentationConfig {
*/
disableLogSending?: boolean;

/**
* Control Log sending severity level, logs will be sent for specified severity and higher.
*/
logSeverity?: SeverityNumber;

/**
* Whether to disable the injection trace-context fields, and possibly other
* fields from `logHook()`, into log records for log correlation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,92 @@ describe('BunyanInstrumentation', () => {
assert.strictEqual(rec.body, 'hi');
assert.strictEqual(rec.attributes.aProperty, 'bar');
});

it('log record error level', () => {
instrumentation.setConfig({ logSeverity: SeverityNumber.FATAL });
// Setting `logSeverity` only has an impact on Loggers created
// *after* it is set. So we cannot test with the `log` created in
// `beforeEach()` above.
log = Logger.createLogger({ name: 'test-logger-name', stream });
log.error('error log');
log.fatal('fatal log');
const logRecords = memExporter.getFinishedLogRecords();
// Only one log record match configured severity
assert.strictEqual(logRecords.length, 1);
assert.strictEqual(logRecords[0].body, 'fatal log');
});

it('log record error level', () => {
instrumentation.setConfig({ logSeverity: SeverityNumber.ERROR });
// Setting `logSeverity` only has an impact on Loggers created
// *after* it is set. So we cannot test with the `log` created in
// `beforeEach()` above.
log = Logger.createLogger({ name: 'test-logger-name', stream });
log.warn('warn log');
log.error('error log');
const logRecords = memExporter.getFinishedLogRecords();
// Only one log record match configured severity
assert.strictEqual(logRecords.length, 1);
assert.strictEqual(logRecords[0].body, 'error log');
});

it('log record warn level', () => {
instrumentation.setConfig({ logSeverity: SeverityNumber.WARN });
// Setting `logSeverity` only has an impact on Loggers created
// *after* it is set. So we cannot test with the `log` created in
// `beforeEach()` above.
log = Logger.createLogger({ name: 'test-logger-name', stream });
log.info('info log');
log.warn('warn log');
const logRecords = memExporter.getFinishedLogRecords();
// Only one log record match configured severity
assert.strictEqual(logRecords.length, 1);
assert.strictEqual(logRecords[0].body, 'warn log');
});

it('log record info level', () => {
instrumentation.setConfig({ logSeverity: SeverityNumber.INFO });
// Setting `logSeverity` only has an impact on Loggers created
// *after* it is set. So we cannot test with the `log` created in
// `beforeEach()` above.
log = Logger.createLogger({ name: 'test-logger-name', stream });
log.debug('debug log');
log.info('info log');
const logRecords = memExporter.getFinishedLogRecords();
// Only one log record match configured severity
assert.strictEqual(logRecords.length, 1);
assert.strictEqual(logRecords[0].body, 'info log');
});

it('log record debug level', () => {
instrumentation.setConfig({ logSeverity: SeverityNumber.DEBUG });
log = Logger.createLogger({ name: 'test-logger-name', stream });
log.info('info log');
log.debug('debug log');
// Just the log.info() writes to `stream`.
sinon.assert.calledOnce(writeSpy);
// Both log.info() and log.debug() should be written to the OTel Logs SDK.
const logRecords = memExporter.getFinishedLogRecords();
assert.strictEqual(logRecords.length, 2);
assert.strictEqual(logRecords[0].body, 'info log');
assert.strictEqual(logRecords[1].body, 'debug log');
});

it('log record trace level', () => {
instrumentation.setConfig({ logSeverity: SeverityNumber.TRACE });
log = Logger.createLogger({ name: 'test-logger-name', stream });
log.info('info log');
log.debug('debug log');
log.debug('trace log');
// Just the log.info() writes to `stream`.
sinon.assert.calledOnce(writeSpy);
// Both log.info() and log.debug() should be written to the OTel Logs SDK.
const logRecords = memExporter.getFinishedLogRecords();
assert.strictEqual(logRecords.length, 3);
assert.strictEqual(logRecords[0].body, 'info log');
assert.strictEqual(logRecords[1].body, 'debug log');
assert.strictEqual(logRecords[2].body, 'trace log');
});
});

describe('disabled instrumentation', () => {
Expand Down

0 comments on commit 4dc812e

Please sign in to comment.