///|
/// A structured logger that emits entries through a `Handler`.
///
/// Logger calls are best-effort: errors raised by the handler are ignored.
struct Logger {
mut handler : &Handler
mut level : Level
mut config : Config?
}
///|
/// Log levels ordered from most severe to most verbose.
///
/// A record is emitted when its level is less than or equal to the effective
/// logger level.
pub(all) enum Level {
/// A fatal failure.
Fatal
/// An error that should be investigated.
Error
/// A warning about a degraded or unexpected condition.
Warn
/// General operational information.
Info
/// Detailed diagnostic information.
Debug
/// Very verbose diagnostic information.
Trace
} derive(Eq, Compare)
///|
pub impl Show for Level with fn to_string(self) -> String {
match self {
Fatal => "FATAL"
Error => "ERROR"
Warn => "WARN"
Info => "INFO"
Debug => "DEBUG"
Trace => "TRACE"
}
}
///|
pub impl Show for Level with fn output(self, logger) -> Unit {
logger.write_string(self.to_string())
}
///|
/// Create a logger that writes to `handler`.
///
/// The default level is `Info`. When `config` is provided, its root level is
/// used as the logger level and category-specific overrides are consulted for
/// each entry.
pub fn[H : Handler] Logger::Logger(
handler : H,
level? : Level = Info,
config? : Config,
) -> Logger {
let level = if config is Some(config) { config.level } else { level }
Logger::{ handler, level, config }
}
///|
fn Logger::is_level_enabled(
self : Logger,
level : Level,
category? : String,
) -> Bool {
let effective = if self.config is Some(config) &&
category is Some(category) &&
config.get_category_level(category) is Some(level) {
level
} else {
self.level
}
level <= effective
}
///|
/// Replace the handler used by this logger.
pub fn[H : Handler] Logger::set_handler(self : Logger, handler : H) -> Unit {
self.handler = handler
}
///|
/// Install a configuration object and reset the logger's root level from it.
pub fn Logger::set_config(self : Logger, config : Config) -> Unit {
self.config = Some(config)
self.level = config.level
}
///|
/// Set the logger's root level.
///
/// Category-specific overrides from an installed `Config` still take
/// precedence for matching categories.
pub fn Logger::set_level(self : Logger, level : Level) -> Unit {
self.level = level
}
///|
/// Start a log event at `level` if it is enabled.
#callsite(autofill(loc))
pub fn Logger::event(
self : Logger,
level : Level,
category? : String,
loc~ : SourceLoc,
) -> Event? {
if self.is_level_enabled(level, category?) {
Some(Event::{ logger: self, entry: Entry(level~, category?, source=loc) })
} else {
None
}
}
///|
/// Start a `Fatal` event if it is enabled.
#callsite(autofill(loc))
pub fn Logger::fatal(
self : Logger,
category? : String,
loc~ : SourceLoc,
) -> Event? {
self.event(Fatal, category?, loc~)
}
///|
/// Start an `Error` event if it is enabled.
#callsite(autofill(loc))
pub fn Logger::error(
self : Logger,
category? : String,
loc~ : SourceLoc,
) -> Event? {
self.event(Error, category?, loc~)
}
///|
/// Start a `Warn` event if it is enabled.
#callsite(autofill(loc))
pub fn Logger::warn(
self : Logger,
category? : String,
loc~ : SourceLoc,
) -> Event? {
self.event(Warn, category?, loc~)
}
///|
/// Start an `Info` event if it is enabled.
#callsite(autofill(loc))
pub fn Logger::info(
self : Logger,
category? : String,
loc~ : SourceLoc,
) -> Event? {
self.event(Info, category?, loc~)
}
///|
/// Start a `Debug` event if it is enabled.
#callsite(autofill(loc))
pub fn Logger::debug(
self : Logger,
category? : String,
loc~ : SourceLoc,
) -> Event? {
self.event(Debug, category?, loc~)
}
///|
/// Start a `Trace` event if it is enabled.
#callsite(autofill(loc))
pub fn Logger::trace(
self : Logger,
category? : String,
loc~ : SourceLoc,
) -> Event? {
self.event(Trace, category?, loc~)
}
///|
fn default_config() -> Config {
Config::from_env(env="MOON_XLOG") catch {
_ => Config()
}
}
///|
let global_logger : Logger = Logger(Stdout(), config=default_config())
///|
/// Return the process-wide root logger used by top-level logging helpers.
///
/// The returned logger can be configured with `set_handler`, `set_config`, and
/// `set_level`.
pub fn global() -> Logger {
global_logger
}
///|
/// Start a log event at `level` with the global logger if it is enabled.
#callsite(autofill(loc))
pub fn event(level : Level, category? : String, loc~ : SourceLoc) -> Event? {
global_logger.event(level, category?, loc~)
}
///|
/// Start a `Fatal` event with the global logger if it is enabled.
#callsite(autofill(loc))
pub fn fatal(category? : String, loc~ : SourceLoc) -> Event? {
global_logger.fatal(category?, loc~)
}
///|
/// Start an `Error` event with the global logger if it is enabled.
#callsite(autofill(loc))
pub fn error(category? : String, loc~ : SourceLoc) -> Event? {
global_logger.error(category?, loc~)
}
///|
/// Start a `Warn` event with the global logger if it is enabled.
#callsite(autofill(loc))
pub fn warn(category? : String, loc~ : SourceLoc) -> Event? {
global_logger.warn(category?, loc~)
}
///|
/// Start an `Info` event with the global logger if it is enabled.
#callsite(autofill(loc))
pub fn info(category? : String, loc~ : SourceLoc) -> Event? {
global_logger.info(category?, loc~)
}
///|
/// Start a `Debug` event with the global logger if it is enabled.
#callsite(autofill(loc))
pub fn debug(category? : String, loc~ : SourceLoc) -> Event? {
global_logger.debug(category?, loc~)
}
///|
/// Start a `Trace` event with the global logger if it is enabled.
#callsite(autofill(loc))
pub fn trace(category? : String, loc~ : SourceLoc) -> Event? {
global_logger.trace(category?, loc~)
}