use crate::NanoTimeStamp;
use candid::CandidType;
use serde::{Deserialize, Serialize};
use std::fmt;
pub mod counter;
mod store;
pub use store::*;
mod buffer;
pub use buffer::*;
mod test;
#[derive(
CandidType, Default, Deserialize, Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord,
)]
pub enum LogVariant {
#[default]
#[serde(rename = "info")]
Info,
#[serde(rename = "warn")]
Warning,
#[serde(rename = "error")]
Error,
}
#[derive(CandidType, Deserialize, Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LogEntry {
pub timestamp: NanoTimeStamp,
pub cycle: Option<u128>,
pub counter: u64,
pub message: String,
pub file: &'static str,
pub variant: LogVariant,
pub line: u32,
pub version: &'static str,
}
impl fmt::Display for LogEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}][{}] {}:{} {}",
self.timestamp,
self.cycle.unwrap_or(0),
self.file,
self.line,
self.message
)
}
}
#[macro_export]
macro_rules! log {
($message:expr $(,$args:expr)* $(,)*) => {{
use $crate::logs::Sink;
let message = std::format!($message $(,$args)*);
println!("{}", &message);
(&$crate::logs::MAIN_LOG).append($crate::logs::LogEntry {
timestamp: $crate::NanoTimeStamp::now(),
cycle: None,
message,
variant: $crate::logs::LogVariant::Info,
file: std::file!(),
line: std::line!(),
version: env!("CARGO_PKG_VERSION"),
counter: $crate::logs::counter::log_increment()
});
}}
}
#[macro_export]
macro_rules! log_error {
($message:expr $(,$args:expr)* $(,)*) => {{
use $crate::logs::Sink;
let message = std::format!($message $(,$args)*);
println!("{}", &message);
(&$crate::logs::MAIN_LOG).append($crate::logs::LogEntry {
timestamp: $crate::NanoTimeStamp::now(),
cycle: None,
message,
variant: $crate::logs::LogVariant::Error,
file: std::file!(),
line: std::line!(),
version: env!("CARGO_PKG_VERSION"),
counter: $crate::logs::counter::log_increment()
});
}}
}
#[macro_export]
macro_rules! log_warning {
($message:expr $(,$args:expr)* $(,)*) => {{
use $crate::logs::Sink;
let message = std::format!($message $(,$args)*);
println!("{}", &message);
(&$crate::logs::MAIN_LOG).append($crate::logs::LogEntry {
timestamp: $crate::NanoTimeStamp::now(),
cycle: None,
message,
variant: $crate::logs::LogVariant::Warning,
file: std::file!(),
line: std::line!(),
version: env!("CARGO_PKG_VERSION"),
counter: $crate::logs::counter::log_increment()
});
}}
}
#[macro_export]
macro_rules! log_panic {
($message:expr $(,$args:expr)* $(,)*) => {{
use $crate::logs::Sink;
let message = std::format!($message $(,$args)*);
(&$crate::logs::MAIN_LOG).append($crate::logs::LogEntry {
timestamp: $crate::NanoTimeStamp::now(),
cycle: None,
variant: $crate::logs::LogVariant::Error,
message: message.clone(),
file: std::file!(),
line: std::line!(),
version: env!("CARGO_PKG_VERSION"),
counter: $crate::logs::counter::log_increment()
});
panic!("{}", &message);
}}
}
#[macro_export]
macro_rules! log_cycle {
($message:expr $(,$args:expr)* $(,)*) => {{
use $crate::logs::Sink;
#[cfg(not(target_arch = "wasm32"))]
use $crate::mocks::canister_balance_mock as canister_balance;
#[cfg(target_arch = "wasm32")]
use ic_cdk::api::canister_balance128 as canister_balance;
let message = std::format!($message $(,$args)*);
println!("{}", &message);
(&$crate::logs::MAIN_LOG).append($crate::logs::LogEntry {
timestamp: $crate::NanoTimeStamp::now(),
cycle: Some(canister_balance()),
message,
variant: $crate::logs::LogVariant::Info,
file: std::file!(),
line: std::line!(),
version: env!("CARGO_PKG_VERSION"),
counter: $crate::logs::counter::log_increment()
});
}}
}
#[macro_export]
macro_rules! log_performance {
($message:expr $(,$args:expr)* $(,)*) => {{
use $crate::logs::Sink;
#[cfg(not(target_arch = "wasm32"))]
use $crate::mocks::performance_counter_mock as canister_balance;
#[cfg(target_arch = "wasm32")]
use ic_cdk::api::performance_counter as canister_balance;
let message = std::format!($message $(,$args)*);
println!("{}", &message);
(&$crate::logs::MAIN_LOG).append($crate::logs::LogEntry {
timestamp: $crate::NanoTimeStamp::now(),
cycle: Some(canister_balance(1).into()),
message,
variant: $crate::logs::LogVariant::Info,
file: std::file!(),
line: std::line!(),
version: env!("CARGO_PKG_VERSION"),
counter: $crate::logs::counter::log_increment()
});
}}
}
#[macro_export]
macro_rules! throw_log {
($message:expr $(,$args:expr)* $(,)*) => {{
$crate::log!($message $(,$args)*);
return $crate::report(format!($message $(,$args)*));
}};
}
#[macro_export]
macro_rules! require_log {
($condition:expr, $($msg:tt)*) => {
if !$condition {
$crate::log!($($msg)*);
return $crate::report(format!($($msg)*));
}
};
}
#[cfg(test)]
mod test_utils {
use crate::{logs::export_log, require_log};
#[test]
fn test_log() {
fn sum_and_log(x: u64, y: u64) -> Result<u64, String> {
let result = x.saturating_add(y);
require_log!(result < 100, "Result is too big: {}", result);
Ok(result)
}
assert_eq!(sum_and_log(1, 2), Ok(3));
match sum_and_log(100, 2) {
Ok(_) => panic!("Should have failed"),
Err(e) => assert_eq!(e, "Result is too big: 102"),
}
assert_eq!(export_log()[0].message, "Result is too big: 102");
assert_eq!(export_log()[0].counter, 1);
}
}