soroban_sdk/logs.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
//! Logging contains types for logging debug events.
//!
//! See [`log`][crate::log] for how to conveniently log debug events.
use core::fmt::Debug;
use crate::{env::internal::EnvBase, Env, Val};
/// Log a debug event.
///
/// Takes a [Env], a literal string, and an optional trailing sequence of
/// arguments that may be any value that are convertible to [`Val`]. The
/// string and arguments are appended as-is to the log, as the body of a
/// structured diagnostic event. Such events may be emitted from the host as
/// auxiliary diagnostic XDR, or converted to strings later for debugging.
///
/// `log!` statements are only enabled in non optimized builds that have
/// `debug-assertions` enabled. To enable `debug-assertions` add the following
/// lines to `Cargo.toml`, then build with the profile specified, `--profile
/// release-with-logs`. See the cargo docs for how to use [custom profiles].
///
/// ```toml
/// [profile.release-with-logs]
/// inherits = "release"
/// debug-assertions = true
/// ```
///
/// [custom profiles]:
/// https://doc.rust-lang.org/cargo/reference/profiles.html#custom-profiles
///
/// ### Examples
///
/// Log a string:
///
/// ```
/// use soroban_sdk::{log, Env};
///
/// let env = Env::default();
///
/// log!(&env, "a log entry");
/// ```
///
/// Log a string with values:
///
/// ```
/// use soroban_sdk::{log, symbol_short, Symbol, Env};
///
/// let env = Env::default();
///
/// let value = 5;
/// log!(&env, "a log entry", value, symbol_short!("another"));
/// ```
///
/// Assert on logs in tests:
///
/// ```
/// # #[cfg(feature = "testutils")]
/// # {
/// use soroban_sdk::{log, symbol_short, Symbol, Env};
///
/// let env = Env::default();
///
/// let value = 5;
/// log!(&env, "a log entry", value, symbol_short!("another"));
///
/// use soroban_sdk::testutils::Logs;
/// let logentry = env.logs().all().last().unwrap().clone();
/// assert!(logentry.contains("[\"a log entry\", 5, another]"));
/// # }
/// ```
#[macro_export]
macro_rules! log {
($env:expr, $fmt:literal $(,)?) => {
if cfg!(debug_assertions) {
$env.logs().add($fmt, &[]);
}
};
($env:expr, $fmt:literal, $($args:expr),* $(,)?) => {
if cfg!(debug_assertions) {
$env.logs().add($fmt, &[
$(
<_ as $crate::IntoVal<Env, $crate::Val>>::into_val(&$args, $env)
),*
]);
}
};
}
/// Logs logs debug events.
///
/// See [`log`][crate::log] for how to conveniently log debug events.
#[derive(Clone)]
pub struct Logs(Env);
impl Debug for Logs {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Logs")
}
}
impl Logs {
#[inline(always)]
pub(crate) fn env(&self) -> &Env {
&self.0
}
#[inline(always)]
pub(crate) fn new(env: &Env) -> Logs {
Logs(env.clone())
}
#[deprecated(note = "use [Logs::add]")]
#[inline(always)]
pub fn log(&self, msg: &'static str, args: &[Val]) {
self.add(msg, args);
}
/// Log a debug event.
///
/// Takes a literal string and a sequence of trailing values to add
/// as a log entry in the diagnostic event stream.
///
/// See [`log`][crate::log] for how to conveniently log debug events.
#[inline(always)]
pub fn add(&self, msg: &'static str, args: &[Val]) {
if cfg!(debug_assertions) {
let env = self.env();
env.log_from_slice(msg, args).unwrap();
#[cfg(any(test, feature = "testutils"))]
{
use crate::testutils::Logs;
std::println!("{}", self.all().last().unwrap());
}
}
}
}
#[cfg(any(test, feature = "testutils"))]
use crate::testutils;
#[cfg(any(test, feature = "testutils"))]
#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
impl testutils::Logs for Logs {
fn all(&self) -> std::vec::Vec<String> {
use crate::xdr::{
ContractEventBody, ContractEventType, ScSymbol, ScVal, ScVec, StringM, VecM,
};
let env = self.env();
let log_sym = ScSymbol(StringM::try_from("log").unwrap());
let log_topics = ScVec(VecM::try_from(vec![ScVal::Symbol(log_sym)]).unwrap());
env.host()
.get_diagnostic_events()
.unwrap()
.0
.into_iter()
.filter_map(|e| match (&e.event.type_, &e.event.body) {
(ContractEventType::Diagnostic, ContractEventBody::V0(ce))
if &ce.topics == &log_topics.0 =>
{
Some(format!("{}", &e))
}
_ => None,
})
.collect::<std::vec::Vec<_>>()
}
fn print(&self) {
std::println!("{}", self.all().join("\n"))
}
}