use log::{debug, error, info, warn};
use std::fmt::{self, Arguments, Display, Formatter};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Component {
Node,
Registry,
Service,
Database,
Transporter,
NetworkDiscovery,
System,
CLI,
Keys,
Custom(&'static str),
}
struct ComponentPrefixDisplay {
parent: Option<Component>,
component: Component,
}
impl Display for ComponentPrefixDisplay {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.parent {
Some(parent) if parent != Component::Node => {
write!(f, "{}.{}", parent.as_str(), self.component.as_str())
}
_ => write!(f, "{}", self.component.as_str()),
}
}
}
struct MaybeActionDisplay<'a>(Option<&'a str>);
impl Display for MaybeActionDisplay<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(path) = self.0 {
write!(f, "|action={path}")
} else {
Ok(())
}
}
}
struct MaybeEventDisplay<'a>(Option<&'a str>);
impl Display for MaybeEventDisplay<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(path) = self.0 {
write!(f, "|event={path}")
} else {
Ok(())
}
}
}
impl Component {
pub fn as_str(&self) -> &str {
match self {
Component::Node => "Node",
Component::Registry => "Registry",
Component::Service => "Service",
Component::Database => "DB",
Component::Transporter => "Network",
Component::NetworkDiscovery => "NetworkDiscovery",
Component::System => "System",
Component::CLI => "CLI",
Component::Keys => "Keys",
Component::Custom(name) => name,
}
}
}
#[derive(Clone)]
pub struct Logger {
component: Component,
node_id: String,
parent_component: Option<Component>,
action_path: Option<String>,
event_path: Option<String>,
}
impl Logger {
pub fn new_root(component: Component, node_id: &str) -> Self {
Self {
component,
node_id: node_id.to_string(),
parent_component: None,
action_path: None,
event_path: None,
}
}
pub fn with_component(&self, component: Component) -> Self {
Self {
component,
node_id: self.node_id.clone(),
parent_component: Some(self.component),
action_path: self.action_path.clone(),
event_path: self.event_path.clone(),
}
}
pub fn with_action_path(&self, path: impl Into<String>) -> Self {
Self {
component: self.component,
node_id: self.node_id.clone(),
parent_component: self.parent_component,
action_path: Some(path.into()),
event_path: self.event_path.clone(),
}
}
pub fn with_event_path(&self, path: impl Into<String>) -> Self {
Self {
component: self.component,
node_id: self.node_id.clone(),
parent_component: self.parent_component,
action_path: self.action_path.clone(),
event_path: Some(path.into()),
}
}
pub fn clone_logger(&self) -> Self {
self.clone()
}
pub fn node_id(&self) -> &str {
&self.node_id
}
pub fn action_path(&self) -> Option<&str> {
self.action_path.as_deref()
}
pub fn event_path(&self) -> Option<&str> {
self.event_path.as_deref()
}
fn component_prefix(&self) -> String {
match self.parent_component {
Some(parent) if parent != Component::Node => {
format!("{}.{}", parent.as_str(), self.component.as_str())
}
_ => self.component.as_str().to_string(),
}
}
fn full_prefix(&self) -> String {
let mut parts = Vec::new();
parts.push(self.component_prefix());
if let Some(path) = &self.action_path {
parts.push(format!("action={path}"));
}
if let Some(path) = &self.event_path {
parts.push(format!("event={path}"));
}
parts.join("|")
}
pub fn debug(&self, message: impl Into<String>) {
if log::log_enabled!(log::Level::Debug) {
if self.component == Component::Node && self.parent_component.is_none() {
debug!("[{}] {}", self.node_id, message.into());
} else {
debug!(
"[{}][{}] {}",
self.node_id,
self.full_prefix(),
message.into()
);
}
}
}
pub fn debug_args(&self, args: Arguments) {
if log::log_enabled!(log::Level::Debug) {
if self.component == Component::Node && self.parent_component.is_none() {
debug!("[{}] {}", self.node_id, args);
} else {
debug!(
"[{}][{}{}{}] {}",
self.node_id,
ComponentPrefixDisplay {
parent: self.parent_component,
component: self.component
},
MaybeActionDisplay(self.action_path()),
MaybeEventDisplay(self.event_path()),
args
);
}
}
}
pub fn info(&self, message: impl Into<String>) {
if log::log_enabled!(log::Level::Info) {
if self.component == Component::Node && self.parent_component.is_none() {
info!("[{}] {}", self.node_id, message.into());
} else {
info!(
"[{}][{}] {}",
self.node_id,
self.full_prefix(),
message.into()
);
}
}
}
pub fn info_args(&self, args: Arguments) {
if log::log_enabled!(log::Level::Info) {
if self.component == Component::Node && self.parent_component.is_none() {
info!("[{}] {}", self.node_id, args);
} else {
info!(
"[{}][{}{}{}] {}",
self.node_id,
ComponentPrefixDisplay {
parent: self.parent_component,
component: self.component
},
MaybeActionDisplay(self.action_path()),
MaybeEventDisplay(self.event_path()),
args
);
}
}
}
pub fn info_static(&self, msg: &'static str) {
if log::log_enabled!(log::Level::Info) {
if self.component == Component::Node && self.parent_component.is_none() {
info!("[{}] {}", self.node_id, msg);
} else {
info!(
"[{}][{}{}{}] {}",
self.node_id,
ComponentPrefixDisplay {
parent: self.parent_component,
component: self.component
},
MaybeActionDisplay(self.action_path()),
MaybeEventDisplay(self.event_path()),
msg
);
}
}
}
pub fn warn(&self, message: impl Into<String>) {
if log::log_enabled!(log::Level::Warn) {
if self.component == Component::Node && self.parent_component.is_none() {
warn!("[{}] {}", self.node_id, message.into());
} else {
warn!(
"[{}][{}] {}",
self.node_id,
self.full_prefix(),
message.into()
);
}
}
}
pub fn warn_args(&self, args: Arguments) {
if log::log_enabled!(log::Level::Warn) {
if self.component == Component::Node && self.parent_component.is_none() {
warn!("[{}] {}", self.node_id, args);
} else {
warn!(
"[{}][{}{}{}] {}",
self.node_id,
ComponentPrefixDisplay {
parent: self.parent_component,
component: self.component
},
MaybeActionDisplay(self.action_path()),
MaybeEventDisplay(self.event_path()),
args
);
}
}
}
pub fn error(&self, message: impl Into<String>) {
if log::log_enabled!(log::Level::Error) {
if self.component == Component::Node && self.parent_component.is_none() {
error!("[{}] {}", self.node_id, message.into());
} else {
error!(
"[{}][{}] {}",
self.node_id,
self.full_prefix(),
message.into()
);
}
}
}
pub fn error_args(&self, args: Arguments) {
if log::log_enabled!(log::Level::Error) {
if self.component == Component::Node && self.parent_component.is_none() {
error!("[{}] {}", self.node_id, args);
} else {
error!(
"[{}][{}{}{}] {}",
self.node_id,
ComponentPrefixDisplay {
parent: self.parent_component,
component: self.component
},
MaybeActionDisplay(self.action_path()),
MaybeEventDisplay(self.event_path()),
args
);
}
}
}
}
pub trait LoggingContext {
fn component(&self) -> Component;
fn service_path(&self) -> Option<&str>;
fn action_path(&self) -> Option<&str> {
None
}
fn event_path(&self) -> Option<&str> {
None
}
fn logger(&self) -> &Logger;
fn log_debug(&self, message: String) {
if log::log_enabled!(log::Level::Debug) {
let prefix = self.log_prefix();
let logger = self.logger();
if self.component() == Component::Node && prefix == "Node" {
debug!("[{}] {}", logger.node_id(), message);
} else {
debug!("[{}][{}] {}", logger.node_id(), prefix, message);
}
}
}
fn log_info(&self, message: String) {
if log::log_enabled!(log::Level::Info) {
let prefix = self.log_prefix();
let logger = self.logger();
if self.component() == Component::Node && prefix == "Node" {
info!("[{}] {}", logger.node_id(), message);
} else {
info!("[{}][{}] {}", logger.node_id(), prefix, message);
}
}
}
fn log_warn(&self, message: String) {
if log::log_enabled!(log::Level::Warn) {
let prefix = self.log_prefix();
let logger = self.logger();
if self.component() == Component::Node && prefix == "Node" {
warn!("[{}] {}", logger.node_id(), message);
} else {
warn!("[{}][{}] {}", logger.node_id(), prefix, message);
}
}
}
fn log_error(&self, message: String) {
if log::log_enabled!(log::Level::Error) {
let prefix = self.log_prefix();
let logger = self.logger();
if self.component() == Component::Node && prefix == "Node" {
error!("[{}] {}", logger.node_id(), message);
} else {
error!("[{}][{}] {}", logger.node_id(), prefix, message);
}
}
}
fn log_prefix(&self) -> String {
let mut parts = Vec::new();
match self.service_path() {
Some(path) => parts.push(format!("{}:{}", self.component().as_str(), path)),
None => parts.push(self.component().as_str().to_string()),
}
if let Some(path) = self.action_path() {
parts.push(format!("action={path}"));
}
if let Some(path) = self.event_path() {
parts.push(format!("event={path}"));
}
parts.join("|")
}
}