Add global log options to CLI (#3519)
### Description - Add global log options to CLI - Improve consistency with agent log options - Other minor log-related cleanup ### Related issues Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3499 ### Backward compatibility Yes because the new log options haven't yet publishedpull/3522/head
parent
41cc7f5c4b
commit
5373d54ca5
@ -0,0 +1,5 @@ |
|||||||
|
--- |
||||||
|
'@hyperlane-xyz/cli': patch |
||||||
|
--- |
||||||
|
|
||||||
|
Add --log and --verbosity settings to CLI |
@ -1,38 +1,98 @@ |
|||||||
import { LevelWithSilent, pino } from 'pino'; |
import { LevelWithSilent, Logger, pino } from 'pino'; |
||||||
|
|
||||||
import { envVarToBoolean, safelyAccessEnvVar } from './env'; |
import { safelyAccessEnvVar } from './env'; |
||||||
|
|
||||||
let logLevel: LevelWithSilent = 'info'; |
// Level and format here should correspond with the agent options as much as possible
|
||||||
const envLogLevel = safelyAccessEnvVar('LOG_LEVEL')?.toLowerCase(); |
// https://docs.hyperlane.xyz/docs/operate/config-reference#logfmt
|
||||||
if (envLogLevel && pino.levels.values[envLogLevel]) { |
|
||||||
logLevel = envLogLevel as LevelWithSilent; |
// A custom enum definition because pino does not export an enum
|
||||||
} |
// and because we use 'off' instead of 'silent' to match the agent options
|
||||||
// For backwards compat and also to match agent level options
|
export enum LogLevel { |
||||||
else if (envLogLevel === 'none' || envLogLevel === 'off') { |
Debug = 'debug', |
||||||
logLevel = 'silent'; |
Info = 'info', |
||||||
} |
Warn = 'warn', |
||||||
|
Error = 'error', |
||||||
export const isLogPretty = envVarToBoolean(safelyAccessEnvVar('LOG_PRETTY')); |
Off = 'off', |
||||||
|
} |
||||||
export const rootLogger = pino({ |
|
||||||
level: logLevel, |
let logLevel: LevelWithSilent = |
||||||
name: 'hyperlane', |
toPinoLevel(safelyAccessEnvVar('LOG_LEVEL', true)) || 'info'; |
||||||
formatters: { |
|
||||||
// Remove pino's default bindings of hostname but keep pid
|
function toPinoLevel(level?: string): LevelWithSilent | undefined { |
||||||
bindings: (defaultBindings) => ({ pid: defaultBindings.pid }), |
if (level && pino.levels.values[level]) return level as LevelWithSilent; |
||||||
}, |
// For backwards compat and also to match agent level options
|
||||||
hooks: { |
else if (level === 'none' || level === 'off') return 'silent'; |
||||||
logMethod(inputArgs, method, level) { |
else return undefined; |
||||||
// 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
|
export function getLogLevel() { |
||||||
if (isLogPretty && level >= pino.levels.values[logLevel]) { |
return logLevel; |
||||||
// eslint-disable-next-line no-console
|
} |
||||||
console.log(...inputArgs); |
|
||||||
// Then return null to prevent pino from logging
|
export enum LogFormat { |
||||||
return null; |
Pretty = 'pretty', |
||||||
} |
JSON = 'json', |
||||||
return method.apply(this, inputArgs); |
} |
||||||
|
let logFormat: LogFormat = LogFormat.JSON; |
||||||
|
const envLogFormat = safelyAccessEnvVar('LOG_FORMAT', true) as |
||||||
|
| LogFormat |
||||||
|
| undefined; |
||||||
|
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: LogFormat, |
||||||
|
newLogLevel: LogLevel, |
||||||
|
) { |
||||||
|
logFormat = newLogFormat; |
||||||
|
logLevel = toPinoLevel(newLogLevel) || logLevel; |
||||||
|
rootLogger = createHyperlanePinoLogger(logLevel, logFormat); |
||||||
|
return rootLogger; |
||||||
|
} |
||||||
|
|
||||||
|
export function setRootLogger(logger: Logger) { |
||||||
|
rootLogger = logger; |
||||||
|
return rootLogger; |
||||||
|
} |
||||||
|
|
||||||
|
export function createHyperlanePinoLogger( |
||||||
|
logLevel: LevelWithSilent, |
||||||
|
logFormat: LogFormat, |
||||||
|
) { |
||||||
|
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); |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
} |
||||||
|
Loading…
Reference in new issue