UNPKG

@hyperlane-xyz/utils

Version:

General utilities and types for the Hyperlane network

149 lines 5.61 kB
import { BigNumber } from 'ethers'; import { pino } from 'pino'; import { inKubernetes, safelyAccessEnvVar } from './env.js'; // Level and format here should correspond with the agent options as much as possible // https://docs.hyperlane.xyz/docs/operate/config-reference#logfmt // A custom enum definition because pino does not export an enum // and because we use 'off' instead of 'silent' to match the agent options export var LogLevel; (function (LogLevel) { LogLevel["Trace"] = "trace"; LogLevel["Debug"] = "debug"; LogLevel["Info"] = "info"; LogLevel["Warn"] = "warn"; LogLevel["Error"] = "error"; LogLevel["Off"] = "off"; })(LogLevel || (LogLevel = {})); let logLevel = toPinoLevel(safelyAccessEnvVar('LOG_LEVEL', true)) || 'info'; function toPinoLevel(level) { if (level && pino.levels.values[level]) return level; // For backwards compat and also to match agent level options else if (level === 'none' || level === 'off') return 'silent'; else return undefined; } export function getLogLevel() { return logLevel; } export var LogFormat; (function (LogFormat) { LogFormat["Pretty"] = "pretty"; LogFormat["JSON"] = "json"; })(LogFormat || (LogFormat = {})); let logFormat = LogFormat.JSON; const envLogFormat = safelyAccessEnvVar('LOG_FORMAT', true); if (envLogFormat && Object.values(LogFormat).includes(envLogFormat)) logFormat = envLogFormat; export function getLogFormat() { return logFormat; } // Note, for brevity and convenience, the rootLogger is exported directly export let rootLogger = createHyperlanePinoLogger(logLevel, logFormat); export function getRootLogger() { return rootLogger; } export function configureRootLogger(newLogFormat, newLogLevel) { logFormat = newLogFormat; logLevel = toPinoLevel(newLogLevel) || logLevel; rootLogger = createHyperlanePinoLogger(logLevel, logFormat); return rootLogger; } export function setRootLogger(logger) { rootLogger = logger; return rootLogger; } export function createHyperlanePinoLogger(logLevel, logFormat) { // In development, pino-pretty is used for a better dev experience, // but only if the log format is 'pretty'. This allows for JSON logs // in development as well if explicitly configured. if (process.env.NODE_ENV === 'development' && logFormat === LogFormat.Pretty) { return pino({ level: logLevel, transport: { target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard', ignore: 'pid,hostname', }, }, }); } // In production (or other envs), use the original hook-based logger return pino({ level: logLevel, name: 'hyperlane', formatters: { // Remove pino's default bindings of hostname but keep pid bindings: (defaultBindings) => ({ pid: defaultBindings.pid }), }, hooks: { logMethod(inputArgs, method, level) { // Pino has no simple way of setting custom log shapes and they // recommend against using pino-pretty in production so when // pretty is enabled we circumvent pino and log directly to console if (logFormat === LogFormat.Pretty && level >= pino.levels.values[logLevel]) { // eslint-disable-next-line no-console console.log(...inputArgs); // Then return null to prevent pino from logging return null; } return method.apply(this, inputArgs); }, }, }); } export function ethersBigNumberSerializer(key, value) { // Check if the value looks like a serialized BigNumber if (typeof value === 'object' && value !== null && value.type === 'BigNumber' && value.hex) { return BigNumber.from(value.hex).toString(); } if (typeof value === 'bigint') { return value.toString(); } return value; } export async function tryInitializeGcpLogger(options) { if (!inKubernetes()) return null; try { const { createGcpLoggingPinoConfig } = await import( /* webpackIgnore: true */ '@google-cloud/pino-logging-gcp-config'); const serviceContext = options ? { service: options.service ?? 'hyperlane-service', version: options.version ?? 'unknown', } : {}; const gcpConfig = createGcpLoggingPinoConfig({ serviceContext }, { base: undefined, name: 'hyperlane', }); const gcpLogger = pino(gcpConfig); return gcpLogger; } catch (err) { rootLogger.warn(err, 'Could not initialize GCP structured logging, ensure @google-cloud/pino-logging-gcp-config is installed'); return null; } } export async function createServiceLogger(options) { const { service, version, module } = options; const gcpLogger = await tryInitializeGcpLogger({ service, version }); if (gcpLogger) { // Also update rootLogger so SDK components (like SmartProvider) that use // rootLogger.child() will inherit the GCP logging configuration setRootLogger(gcpLogger); return gcpLogger; } // For local development, create a child logger with module info return rootLogger.child({ module: module ?? service }); } //# sourceMappingURL=logging.js.map