archunit/common/logging/
check_logger.rsuse crate::common::{ArchUnitError, LogEventKind, LogLevel, LogRecord, LoggingOptions};
#[derive(Debug, Clone, Copy)]
pub struct CheckLogger<'a> {
options: Option<&'a LoggingOptions>,
}
impl<'a> CheckLogger<'a> {
#[must_use]
pub const fn new(options: Option<&'a LoggingOptions>) -> Self {
Self { options }
}
#[must_use]
pub const fn is_enabled(&self) -> bool {
self.options.is_some()
}
pub fn validate(&self) -> Result<(), ArchUnitError> {
self.options.map_or(Ok(()), LoggingOptions::validate)
}
pub fn start_check(&self, rule_name: impl AsRef<str>) -> Result<(), ArchUnitError> {
self.emit(LogLevel::Info, LogEventKind::StartCheck, rule_name)
}
pub fn end_check(
&self,
rule_name: impl AsRef<str>,
violation_count: usize,
) -> Result<(), ArchUnitError> {
let level = if violation_count == 0 {
LogLevel::Info
} else {
LogLevel::Warn
};
self.emit(
level,
LogEventKind::EndCheck,
format!("{}; violations={violation_count}", rule_name.as_ref()),
)
}
pub fn log_progress(&self, message: impl AsRef<str>) -> Result<(), ArchUnitError> {
self.emit(LogLevel::Debug, LogEventKind::Progress, message)
}
pub fn log_violation(&self, message: impl AsRef<str>) -> Result<(), ArchUnitError> {
self.emit(LogLevel::Warn, LogEventKind::Violation, message)
}
pub fn log_metric(
&self,
metric_name: impl AsRef<str>,
subject: impl AsRef<str>,
value: f64,
threshold: Option<f64>,
) -> Result<(), ArchUnitError> {
let threshold =
threshold.map_or_else(String::new, |threshold| format!("; threshold={threshold}"));
self.emit(
LogLevel::Debug,
LogEventKind::Metric,
format!(
"{} [{}]={value}{threshold}",
metric_name.as_ref(),
subject.as_ref()
),
)
}
pub fn debug(&self, message: impl AsRef<str>) -> Result<(), ArchUnitError> {
self.emit(LogLevel::Debug, LogEventKind::Debug, message)
}
pub fn info(&self, message: impl AsRef<str>) -> Result<(), ArchUnitError> {
self.emit(LogLevel::Info, LogEventKind::Info, message)
}
pub fn warn(&self, message: impl AsRef<str>) -> Result<(), ArchUnitError> {
self.emit(LogLevel::Warn, LogEventKind::Warn, message)
}
pub fn error(&self, message: impl AsRef<str>) -> Result<(), ArchUnitError> {
self.emit(LogLevel::Error, LogEventKind::Error, message)
}
fn emit(
&self,
level: LogLevel,
event: LogEventKind,
message: impl AsRef<str>,
) -> Result<(), ArchUnitError> {
let Some(options) = self.options else {
return Ok(());
};
if !options.accepts(level) {
return Ok(());
}
options.write(&LogRecord::new(level, event, message))
}
}
#[cfg(test)]
mod tests {
use super::CheckLogger;
#[test]
fn absent_options_disable_every_vocabulary_method() {
let logger = CheckLogger::new(None);
assert!(!logger.is_enabled());
assert!(logger.validate().is_ok());
assert!(logger.start_check("rule").is_ok());
assert!(logger.log_progress("progress").is_ok());
assert!(logger.log_violation("violation").is_ok());
assert!(
logger
.log_metric("count", "subject", 1.0, Some(2.0))
.is_ok()
);
assert!(logger.debug("debug").is_ok());
assert!(logger.info("info").is_ok());
assert!(logger.warn("warn").is_ok());
assert!(logger.error("error").is_ok());
assert!(logger.end_check("rule", 0).is_ok());
}
}