solana_svm_log_collector/
lib.rs1#![cfg_attr(
2 not(feature = "agave-unstable-api"),
3 deprecated(
4 since = "3.1.0",
5 note = "This crate has been marked for formal inclusion in the Agave Unstable API. From \
6 v4.0.0 onward, the `agave-unstable-api` crate feature must be specified to \
7 acknowledge use of an interface that may break without warning."
8 )
9)]
10pub use log;
11use std::{cell::RefCell, rc::Rc};
12
13const LOG_MESSAGES_BYTES_LIMIT: usize = 10 * 1000;
14
15pub struct LogCollector {
16 pub messages: Vec<String>,
17 pub bytes_written: usize,
18 pub bytes_limit: Option<usize>,
19 pub limit_warning: bool,
20}
21
22impl Default for LogCollector {
23 fn default() -> Self {
24 Self {
25 messages: Vec::new(),
26 bytes_written: 0,
27 bytes_limit: Some(LOG_MESSAGES_BYTES_LIMIT),
28 limit_warning: false,
29 }
30 }
31}
32
33impl LogCollector {
34 pub fn log(&mut self, message: &str) {
35 let Some(limit) = self.bytes_limit else {
36 self.messages.push(message.to_string());
37 return;
38 };
39
40 let bytes_written = self.bytes_written.saturating_add(message.len());
41 if bytes_written >= limit {
42 if !self.limit_warning {
43 self.limit_warning = true;
44 self.messages.push(String::from("Log truncated"));
45 }
46 } else {
47 self.bytes_written = bytes_written;
48 self.messages.push(message.to_string());
49 }
50 }
51
52 pub fn get_recorded_content(&self) -> &[String] {
53 self.messages.as_slice()
54 }
55
56 pub fn new_ref() -> Rc<RefCell<Self>> {
57 Rc::new(RefCell::new(Self::default()))
58 }
59
60 pub fn new_ref_with_limit(bytes_limit: Option<usize>) -> Rc<RefCell<Self>> {
61 Rc::new(RefCell::new(Self {
62 bytes_limit,
63 ..Self::default()
64 }))
65 }
66
67 pub fn into_messages(self) -> Vec<String> {
68 self.messages
69 }
70}
71
72#[macro_export]
74macro_rules! ic_logger_msg {
75 ($log_collector:expr, $message:expr) => {
76 $crate::log::debug!(
77 target: "solana_runtime::message_processor::stable_log",
78 "{}",
79 $message
80 );
81 if let Some(log_collector) = $log_collector.as_ref() {
82 if let Ok(mut log_collector) = log_collector.try_borrow_mut() {
83 log_collector.log($message);
84 }
85 }
86 };
87 ($log_collector:expr, $fmt:expr, $($arg:tt)*) => {
88 $crate::log::debug!(
89 target: "solana_runtime::message_processor::stable_log",
90 $fmt,
91 $($arg)*
92 );
93 if let Some(log_collector) = $log_collector.as_ref() {
94 if let Ok(mut log_collector) = log_collector.try_borrow_mut() {
95 log_collector.log(&format!($fmt, $($arg)*));
96 }
97 }
98 };
99}
100
101#[macro_export]
103macro_rules! ic_msg {
104 ($invoke_context:expr, $message:expr) => {
105 $crate::ic_logger_msg!($invoke_context.get_log_collector(), $message)
106 };
107 ($invoke_context:expr, $fmt:expr, $($arg:tt)*) => {
108 $crate::ic_logger_msg!($invoke_context.get_log_collector(), $fmt, $($arg)*)
109 };
110}
111
112#[cfg(test)]
113pub(crate) mod tests {
114 use super::*;
115
116 #[test]
117 fn test_log_messages_bytes_limit() {
118 let mut lc = LogCollector::default();
119
120 for _i in 0..LOG_MESSAGES_BYTES_LIMIT * 2 {
121 lc.log("x");
122 }
123
124 let logs: Vec<_> = lc.into_messages();
125 assert_eq!(logs.len(), LOG_MESSAGES_BYTES_LIMIT);
126 for log in logs.iter().take(LOG_MESSAGES_BYTES_LIMIT - 1) {
127 assert_eq!(*log, "x".to_string());
128 }
129 assert_eq!(logs.last(), Some(&"Log truncated".to_string()));
130 }
131}