@opentelemetry/instrumentation-bunyan
Version:
OpenTelemetry instrumentation for `bunyan` logger
146 lines • 6.1 kB
JavaScript
;
/*
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenTelemetryBunyanStream = void 0;
const api_logs_1 = require("@opentelemetry/api-logs");
/** @knipignore */
const version_1 = require("./version");
const DEFAULT_INSTRUMENTATION_SCOPE_NAME = version_1.PACKAGE_NAME;
const DEFAULT_INSTRUMENTATION_SCOPE_VERSION = version_1.PACKAGE_VERSION;
// This block is a copy (modulo code style and TypeScript types) of the Bunyan
// code that defines log level value and names. These values won't ever change
// in bunyan@1. This file is part of *instrumenting* Bunyan, so we want to
// avoid a dependency on the library.
const TRACE = 10;
const DEBUG = 20;
const INFO = 30;
const WARN = 40;
const ERROR = 50;
const FATAL = 60;
const levelFromName = {
trace: TRACE,
debug: DEBUG,
info: INFO,
warn: WARN,
error: ERROR,
fatal: FATAL,
};
const nameFromLevel = {};
Object.keys(levelFromName).forEach(function (name) {
nameFromLevel[levelFromName[name]] = name;
});
const OTEL_SEV_NUM_FROM_BUNYAN_LEVEL = {
[TRACE]: api_logs_1.SeverityNumber.TRACE,
[DEBUG]: api_logs_1.SeverityNumber.DEBUG,
[INFO]: api_logs_1.SeverityNumber.INFO,
[WARN]: api_logs_1.SeverityNumber.WARN,
[ERROR]: api_logs_1.SeverityNumber.ERROR,
[FATAL]: api_logs_1.SeverityNumber.FATAL,
};
const EXTRA_SEV_NUMS = [
api_logs_1.SeverityNumber.TRACE2,
api_logs_1.SeverityNumber.TRACE3,
api_logs_1.SeverityNumber.TRACE4,
api_logs_1.SeverityNumber.DEBUG2,
api_logs_1.SeverityNumber.DEBUG3,
api_logs_1.SeverityNumber.DEBUG4,
api_logs_1.SeverityNumber.INFO2,
api_logs_1.SeverityNumber.INFO3,
api_logs_1.SeverityNumber.INFO4,
api_logs_1.SeverityNumber.WARN2,
api_logs_1.SeverityNumber.WARN3,
api_logs_1.SeverityNumber.WARN4,
api_logs_1.SeverityNumber.ERROR2,
api_logs_1.SeverityNumber.ERROR3,
api_logs_1.SeverityNumber.ERROR4,
api_logs_1.SeverityNumber.FATAL2,
api_logs_1.SeverityNumber.FATAL3,
api_logs_1.SeverityNumber.FATAL4,
];
function severityNumberFromBunyanLevel(lvl) {
// Fast common case: one of the known levels
const sev = OTEL_SEV_NUM_FROM_BUNYAN_LEVEL[lvl];
if (sev !== undefined) {
return sev;
}
// Otherwise, scale the Bunyan level range -- 10 (TRACE) to 70 (FATAL+10)
// -- onto the extra OTel severity numbers (TRACE2, TRACE3, ..., FATAL4).
// Values below bunyan.TRACE map to SeverityNumber.TRACE2, which may be
// considered a bit weird, but it means the unnumbered levels are always
// just for exactly values.
const relativeLevelWeight = (lvl - 10) / (70 - 10);
const otelSevIdx = Math.floor(relativeLevelWeight * EXTRA_SEV_NUMS.length);
const cappedOTelIdx = Math.min(EXTRA_SEV_NUMS.length - 1, Math.max(0, otelSevIdx));
const otelSevValue = EXTRA_SEV_NUMS[cappedOTelIdx];
return otelSevValue;
}
/**
* A Bunyan stream for sending log records to the OpenTelemetry Logs SDK.
*/
class OpenTelemetryBunyanStream {
_otelLogger;
constructor() {
this._otelLogger = api_logs_1.logs.getLogger(DEFAULT_INSTRUMENTATION_SCOPE_NAME, DEFAULT_INSTRUMENTATION_SCOPE_VERSION);
}
/**
* Convert from https://github.com/trentm/node-bunyan#log-record-fields
* to https://opentelemetry.io/docs/specs/otel/logs/data-model/
*
* Dev Notes:
* - We drop the Bunyan 'v' field. It is meant to indicate the format
* of the Bunyan log record. FWIW, it has always been `0`.
* - The standard Bunyan `hostname` and `pid` fields are removed because they
* are redundant with the OpenTelemetry `host.name` and `process.pid`
* Resource attributes, respectively. This code cannot change the
* LoggerProvider's `resource`, so getting the OpenTelemetry equivalents
* depends on the user using relevant OpenTelemetry resource detectors.
* "examples/telemetry.js" shows using HostDetector and ProcessDetector for
* this.
* - The Bunyan `name` field *could* naturally map to OpenTelemetry's
* `service.name` resource attribute. However, that is debatable, as some
* users might use `name` more like a log4j logger name.
* - Strip the `trace_id` et al fields that may have been added by the
* the _emit wrapper.
*/
write(rec) {
const { time, level, msg, v, // eslint-disable-line @typescript-eslint/no-unused-vars
hostname, // eslint-disable-line @typescript-eslint/no-unused-vars
pid, // eslint-disable-line @typescript-eslint/no-unused-vars
trace_id, // eslint-disable-line @typescript-eslint/no-unused-vars
span_id, // eslint-disable-line @typescript-eslint/no-unused-vars
trace_flags, // eslint-disable-line @typescript-eslint/no-unused-vars
...fields } = rec;
let timestamp = undefined;
if (typeof time.getTime === 'function') {
timestamp = time.getTime(); // ms
}
else {
fields.time = time; // Expose non-Date "time" field on attributes.
}
const otelRec = {
timestamp,
observedTimestamp: timestamp,
severityNumber: severityNumberFromBunyanLevel(level),
severityText: nameFromLevel[level],
body: msg,
attributes: fields,
};
this._otelLogger.emit(otelRec);
}
}
exports.OpenTelemetryBunyanStream = OpenTelemetryBunyanStream;
//# sourceMappingURL=OpenTelemetryBunyanStream.js.map