canic_core/
log.rs

1use candid::CandidType;
2use derive_more::Display;
3use serde::{Deserialize, Serialize};
4
5///
6/// Debug
7///
8
9#[derive(
10    Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, CandidType, Display, Serialize, Deserialize,
11)]
12pub enum Level {
13    Debug, // least severe
14    Info,
15    Ok,
16    Warn,
17    Error, // most severe
18}
19
20///
21/// Topic
22///
23
24#[derive(Clone, Copy, Display, Eq, PartialEq)]
25#[remain::sorted]
26pub enum Topic {
27    App,
28    Auth,
29    CanisterLifecycle,
30    CanisterPool,
31    CanisterState,
32    Cycles,
33    Init,
34    Memory,
35    Perf,
36    Sharding,
37    Sync,
38    Topology,
39    Wasm,
40}
41
42#[macro_export]
43macro_rules! log {
44    // =========================================
45    // (1) With topic (normal + trailing comma)
46    // =========================================
47    ($topic:expr, $level:ident, $fmt:expr $(, $arg:expr)* $(,)?) => {{
48        $crate::log!(@inner Some(&$topic.to_string()), $crate::log::Level::$level, $fmt $(, $arg)*);
49    }};
50
51    // =========================================
52    // (2) No topic (normal + trailing comma)
53    // =========================================
54    ($level:ident, $fmt:expr $(, $arg:expr)* $(,)?) => {{
55        $crate::log!(@inner None::<&str>, $crate::log::Level::$level, $fmt $(, $arg)*);
56    }};
57
58    // =========================================
59    // INTERNAL
60    // =========================================
61    (@inner $topic:expr, $level:expr, $fmt:expr $(, $arg:expr)*) => {{
62        let level = $level;
63        let topic_opt: Option<&str> = $topic;
64        let message = format!($fmt $(, $arg)*);
65
66        // append entry
67        let crate_name = env!("CARGO_PKG_NAME");
68        let _ = $crate::log::__append_to_stable_log(crate_name, topic_opt, level, &message);
69
70        let ty_raw = $crate::log::__canister_role_string().unwrap_or_else(|| "...".to_string());
71
72        let ty_disp = $crate::utils::format::ellipsize_middle(&ty_raw, 9, 4, 4);
73        let ty_centered = format!("{:^9}", ty_disp);
74
75        let final_msg = if let Some(t) = topic_opt {
76            format!("[{t}] {message}")
77        } else {
78            message
79        };
80
81        let (color, reset) = match level {
82            $crate::log::Level::Ok    => ("\x1b[32m", "\x1b[0m"),
83            $crate::log::Level::Info  => ("\x1b[34m", "\x1b[0m"),
84            $crate::log::Level::Warn  => ("\x1b[33m", "\x1b[0m"),
85            $crate::log::Level::Error => ("\x1b[31m", "\x1b[0m"),
86            $crate::log::Level::Debug => ("", ""),
87        };
88
89        let label = format!("{color}{:^5}{reset}", level.to_string().to_uppercase());
90        let line = format!("{label}|{ty_centered}| {final_msg}");
91
92        $crate::cdk::println!("{line}");
93    }};
94}
95
96///
97/// Helpers
98///
99
100#[doc(hidden)]
101pub fn __append_to_stable_log(
102    crate_name: &str,
103    topic: Option<&str>,
104    level: Level,
105    message: &str,
106) -> Result<u64, crate::Error> {
107    crate::model::memory::log::StableLog::append(crate_name, topic, level, message)
108}
109
110#[doc(hidden)]
111#[must_use]
112pub fn __canister_role_string() -> Option<String> {
113    crate::model::memory::Env::get_canister_role().map(|role| role.to_string())
114}