UNPKG

@opentelemetry/instrumentation-pino

Version:
145 lines 7.06 kB
"use strict"; /* * 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.PinoInstrumentation = void 0; const api_1 = require("@opentelemetry/api"); const instrumentation_1 = require("@opentelemetry/instrumentation"); /** @knipignore */ const version_1 = require("./version"); const log_sending_utils_1 = require("./log-sending-utils"); const pinoVersions = ['>=5.14.0 <10']; const DEFAULT_LOG_KEYS = { traceId: 'trace_id', spanId: 'span_id', traceFlags: 'trace_flags', }; class PinoInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super(version_1.PACKAGE_NAME, version_1.PACKAGE_VERSION, config); } init() { return [ new instrumentation_1.InstrumentationNodeModuleDefinition('pino', pinoVersions, module => { const isESM = module[Symbol.toStringTag] === 'Module'; const moduleExports = isESM ? module.default : module; const instrumentation = this; const patchedPino = Object.assign((...args) => { const config = instrumentation.getConfig(); const isEnabled = instrumentation.isEnabled(); const logger = moduleExports(...args); // Setup "log correlation" -- injection of `trace_id` et al fields. // Note: If the Pino logger is configured with `nestedKey`, then // the `trace_id` et al fields added by `otelMixin` will be nested // as well. https://getpino.io/#/docs/api?id=mixin-function const otelMixin = instrumentation._getMixinFunction(); const mixinSym = moduleExports.symbols.mixinSym; const origMixin = logger[mixinSym]; if (origMixin === undefined) { logger[mixinSym] = otelMixin; } else { logger[mixinSym] = (ctx, level) => { return Object.assign(otelMixin(ctx, level), origMixin(ctx, level)); }; } // Setup "log sending" -- sending log records to the Logs API. // This depends on `pino.multistream`, which was added in v7.0.0. if (isEnabled && !config.disableLogSending && typeof moduleExports.multistream === 'function') { const otelTimestampFromTime = (0, log_sending_utils_1.getTimeConverter)(logger, moduleExports); const otelStream = new log_sending_utils_1.OTelPinoStream({ messageKey: logger[moduleExports.symbols.messageKeySym], levels: logger.levels, otelTimestampFromTime, }); otelStream[Symbol.for('pino.metadata')] = true; // for `stream.lastLevel` // An error typically indicates a Pino bug, or logger configuration // bug. `diag.warn` *once* for the first error on the assumption // subsequent ones stem from the same bug. otelStream.once('unknown', (line, err) => { instrumentation._diag.warn('could not send pino log line (will only log first occurrence)', { line, err }); }); // Use pino's own `multistream` to send to the original stream and // to the OTel Logs API/SDK. // https://getpino.io/#/docs/api?id=pinomultistreamstreamsarray-opts-gt-multistreamres const origStream = logger[moduleExports.symbols.streamSym]; logger[moduleExports.symbols.streamSym] = moduleExports.multistream([ // Use level `0` to never not log a record given to the stream. { level: 0, stream: origStream }, { level: 0, stream: otelStream }, ], { levels: logger.levels.values }); } return logger; }, moduleExports); if (typeof patchedPino.pino === 'function') { patchedPino.pino = patchedPino; } if (typeof patchedPino.default === 'function') { patchedPino.default = patchedPino; } /* istanbul ignore if */ if (isESM) { if (module.pino) { // This was added in pino@6.8.0 (https://github.com/pinojs/pino/pull/936). module.pino = patchedPino; } module.default = patchedPino; } return patchedPino; }), ]; } _callHook(span, record, level) { const { logHook } = this.getConfig(); if (!logHook) { return; } (0, instrumentation_1.safeExecuteInTheMiddle)(() => logHook(span, record, level), err => { if (err) { api_1.diag.error('pino instrumentation: error calling logHook', err); } }, true); } _getMixinFunction() { const instrumentation = this; return function otelMixin(_context, level) { if (!instrumentation.isEnabled() || instrumentation.getConfig().disableLogCorrelation) { return {}; } const span = api_1.trace.getSpan(api_1.context.active()); if (!span) { return {}; } const spanContext = span.spanContext(); if (!(0, api_1.isSpanContextValid)(spanContext)) { return {}; } const logKeys = instrumentation.getConfig().logKeys ?? DEFAULT_LOG_KEYS; const record = { [logKeys.traceId]: spanContext.traceId, [logKeys.spanId]: spanContext.spanId, [logKeys.traceFlags]: `0${spanContext.traceFlags.toString(16)}`, }; instrumentation._callHook(span, record, level); return record; }; } } exports.PinoInstrumentation = PinoInstrumentation; //# sourceMappingURL=instrumentation.js.map