1use crate::{ops::runtime::log::LogOps, storage::stable::env::Env};
2use candid::CandidType;
3use derive_more::Display;
4use serde::{Deserialize, Serialize};
5use std::cell::Cell;
6
7#[derive(
12 Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, CandidType, Display, Serialize, Deserialize,
13)]
14pub enum Level {
15 Debug, Info,
17 Ok,
18 Warn,
19 Error, }
21
22#[derive(Clone, Copy, Display, Eq, PartialEq)]
27#[remain::sorted]
28pub enum Topic {
29 App,
30 Auth,
31 CanisterLifecycle,
32 CanisterPool,
33 CanisterState,
34 Cycles,
35 Icrc,
36 Init,
37 Memory,
38 Perf,
39 Sharding,
40 Sync,
41 Topology,
42 Wasm,
43}
44
45thread_local! {
46 static LOG_READY: Cell<bool> = const { Cell::new(false) };
47}
48
49pub fn set_ready() {
50 LOG_READY.with(|ready| ready.set(true));
51}
52
53#[must_use]
54pub fn is_ready() -> bool {
55 LOG_READY.with(Cell::get)
56}
57
58#[macro_export]
59macro_rules! log {
60 ($topic:expr, $level:ident, $fmt:expr $(, $arg:expr)* $(,)?) => {{
64 $crate::log!(@inner Some(&$topic.to_string()), $crate::log::Level::$level, $fmt $(, $arg)*);
65 }};
66
67 ($level:ident, $fmt:expr $(, $arg:expr)* $(,)?) => {{
71 $crate::log!(@inner None::<&str>, $crate::log::Level::$level, $fmt $(, $arg)*);
72 }};
73
74 (@inner $topic:expr, $level:expr, $fmt:expr $(, $arg:expr)*) => {{
78 if $crate::log::is_ready() {
79 let level = $level;
80 let topic_opt: Option<&str> = $topic;
81 let message = format!($fmt $(, $arg)*);
82
83 let crate_name = env!("CARGO_PKG_NAME");
85 let _ = $crate::log::__append_runtime_log(crate_name, topic_opt, level, &message);
86
87 let ty_raw = $crate::log::__canister_role_string().unwrap_or_else(|| "...".to_string());
88
89 let ty_disp = $crate::utils::format::ellipsize_middle(&ty_raw, 9, 4, 4);
90 let ty_centered = format!("{:^9}", ty_disp);
91
92 let final_msg = if let Some(t) = topic_opt {
93 format!("[{t}] {message}")
94 } else {
95 message
96 };
97
98 let (color, reset) = match level {
99 $crate::log::Level::Ok => ("\x1b[32m", "\x1b[0m"),
100 $crate::log::Level::Info => ("\x1b[34m", "\x1b[0m"),
101 $crate::log::Level::Warn => ("\x1b[33m", "\x1b[0m"),
102 $crate::log::Level::Error => ("\x1b[31m", "\x1b[0m"),
103 $crate::log::Level::Debug => ("", ""),
104 };
105
106 let label = format!("{color}{:^5}{reset}", level.to_string().to_uppercase());
107 let line = format!("{label}|{ty_centered}| {final_msg}");
108
109 $crate::cdk::println!("{line}");
110 }
111 }};
112}
113
114#[doc(hidden)]
119pub(crate) fn __append_runtime_log(
120 crate_name: &str,
121 topic: Option<&str>,
122 level: Level,
123 message: &str,
124) -> Result<u64, crate::Error> {
125 LogOps::append_runtime_log(crate_name, topic, level, message)
126}
127
128#[doc(hidden)]
129#[must_use]
130pub(crate) fn __canister_role_string() -> Option<String> {
131 Env::get_canister_role().map(|role| role.to_string())
132}