@elastic/opentelemetry-node
Version:
Elastic Distribution of OpenTelemetry Node.js (EDOT Node.js)
132 lines (119 loc) • 4.5 kB
JavaScript
/*
* Copyright Elasticsearch B.V. and contributors
* SPDX-License-Identifier: Apache-2.0
*/
// This provides a `log` singleton Logger instance to be used for logging
// by this SDK.
//
// Dev Note: This file is loaded very early in bootstrapping the SDK, typically
// before ESM hooks are registered. To keep that code path simple, this file
// should keep deps to a minimum.
const luggite = require('./luggite');
const _globalThis = typeof globalThis === 'object' ? globalThis : global;
const _symLog = Symbol.for('elastic-otel-node.log');
// Dev Note: `OTEL_LOG_LEVEL`s are not standardized.
// https://github.com/open-telemetry/opentelemetry-specification/issues/920
// https://github.com/open-telemetry/opentelemetry-specification/issues/2039
const DEFAULT_OTEL_LOG_LEVEL = 'INFO';
const LUGGITE_LEVEL_FROM_OTEL_LOG_LEVEL = {
NONE: luggite.FATAL + 1, // TODO: support 'silent' luggite level
ERROR: 'error',
WARN: 'warn',
INFO: 'info',
DEBUG: 'debug',
VERBOSE: 'trace',
ALL: 'trace',
};
const DEFAULT_LOG_LEVEL =
LUGGITE_LEVEL_FROM_OTEL_LOG_LEVEL[DEFAULT_OTEL_LOG_LEVEL];
// As a hack for occasional unergonomic diag logging from upstream OTel JS, we
// sometimes filter out specific log messages. This should be used sparingly,
// and each case should ideally have an upstream issue to remove it or downgrade
// the log level.
const FILTER_OUT_DIAG_ERROR_MESSAGES = [
// https://github.com/open-telemetry/opentelemetry-js/pull/5546, @opentelemetry/resources@2.0.1
'Accessing resource attributes before async attributes settled',
];
const FILTER_OUT_DIAG_WARN_MESSAGES = [
// https://github.com/open-telemetry/opentelemetry-js-contrib/pull/2767
'No meter provider, using default',
];
const FILTER_OUT_DIAG_INFO_MESSAGES = [
// TODO: remove this log message from upstream
// ref: https://github.com/open-telemetry/opentelemetry-js/blob/4f0b6285af24b71a9fa022755aaa3b6a63ae5033/experimental/packages/opentelemetry-sdk-node/src/sdk.ts#L461
'OTEL_LOGS_EXPORTER contains "none". Logger provider will not be initialized.',
];
/**
* Return an OTel log level to use, based on the OTEL_LOG_LEVEL envvar.
* This is normalized to upper-case, and defaults to INFO if the value is
* not set or unrecognized.
*/
function otelLogLevelFromEnv() {
let otelLogLevel;
if (process.env.OTEL_LOG_LEVEL) {
otelLogLevel = process.env.OTEL_LOG_LEVEL.toUpperCase();
if (LUGGITE_LEVEL_FROM_OTEL_LOG_LEVEL[otelLogLevel] === undefined) {
otelLogLevel = null;
}
}
if (!otelLogLevel) {
otelLogLevel = DEFAULT_OTEL_LOG_LEVEL;
}
return otelLogLevel;
}
/**
* Create a logger using the level from OTEL_LOG_LEVEL, default 'info'.
*/
function createLogger() {
const level =
LUGGITE_LEVEL_FROM_OTEL_LOG_LEVEL[otelLogLevelFromEnv()] || null;
return luggite.createLogger({name: 'elastic-otel-node', level});
}
/**
* Register the singleton `log` to handle OTel `api.diag.*()` calls.
*/
function registerOTelDiagLogger(api) {
// TODO: when luggite supports .child, add a module/component attr for diag log output
api.diag.setLogger(
{
error: (msg, ...args) => {
if (FILTER_OUT_DIAG_ERROR_MESSAGES.includes(msg)) {
return;
}
log.error(msg, ...args);
},
warn: (msg, ...args) => {
if (FILTER_OUT_DIAG_WARN_MESSAGES.includes(msg)) {
return;
}
log.warn(msg, ...args);
},
info: (msg, ...args) => {
if (FILTER_OUT_DIAG_INFO_MESSAGES.includes(msg)) {
return;
}
log.info(msg, ...args);
},
debug: log.debug.bind(log),
verbose: log.trace.bind(log),
},
{
// Set the diag log level to pass through *all* records. The
// luggite logger (`log`) will handle filtering based on level.
logLevel: api.DiagLogLevel['ALL'],
}
);
}
// ---- main line
// Create, if necessary, and export a singleton `log` logger instance.
if (_globalThis[_symLog] === undefined) {
_globalThis[_symLog] = createLogger();
}
/** @type {import('./luggite').Logger} */
const log = _globalThis[_symLog];
// ---- exports
module.exports = {
log,
registerOTelDiagLogger,
DEFAULT_LOG_LEVEL, // this is a *luggite* log level
};