soltrace 0.1.0

Structured, Borsh-serialized Anchor events as a drop-in replacement for msg! in Solana programs.
Documentation
//! The `sol_log!` macro and its level-specific convenience wrappers.
//!
//! Import the macros you need:
//! ```rust,ignore
//! use soltrace::{sol_info, sol_warn, sol_error};
//! ```

/// Emits a structured [`SolTraceEvent`](crate::SolTraceEvent) with a
/// Borsh-serialized payload.
///
/// # Arguments
///
/// * `$level` — a [`LogLevel`](crate::LogLevel) variant
/// * `$event_name` — any expression implementing `ToString`; use a string
///   literal for zero runtime overhead
/// * `$payload` — any value implementing `borsh::BorshSerialize`
///
/// # Serialization failure
///
/// If Borsh serialization of `$payload` fails, a sentinel event with
/// `event_name = "__soltrace_serialization_error"` and an empty payload is
/// emitted. The failure is observable off-chain without panicking on-chain.
///
/// # devnet-only feature
///
/// When the `devnet-only` Cargo feature is enabled on this crate, the entire
/// macro body compiles to a no-op. Enable it in production builds to achieve
/// zero logging overhead; omit it in devnet/test builds to restore
/// observability.
///
/// # Example
///
/// ```rust,ignore
/// use anchor_lang::prelude::*;
/// use soltrace::{sol_log, LogLevel};
///
/// #[derive(AnchorSerialize, AnchorDeserialize)]
/// struct SwapExecuted { amount: u64 }
///
/// sol_log!(LogLevel::Info, "swap_executed", SwapExecuted { amount: 500 });
/// ```
#[macro_export]
macro_rules! sol_log {
    ($level:expr, $event_name:expr, $payload:expr) => {{
        let __result = anchor_lang::prelude::borsh::BorshSerialize::try_to_vec(&$payload);
        let __event = match __result {
            Ok(__bytes) => $crate::SolTraceEvent::new($level, $event_name, __bytes),
            Err(_) => $crate::SolTraceEvent::new(
                $crate::LogLevel::Error,
                "__soltrace_serialization_error",
                Vec::new(),
            ),
        };
        $crate::__private::emit_event(__event);
    }};
}

/// Emits a [`SolTraceEvent`](crate::SolTraceEvent) at
/// [`LogLevel::Trace`](crate::LogLevel::Trace).
///
/// Use for fine-grained entry/exit tracing during development.
/// Disable in production via `soltrace = { features = ["devnet-only"] }`.
///
/// # Example
///
/// ```rust,ignore
/// use soltrace::sol_trace;
/// sol_trace!("fn_enter", FnEnter { instruction: 1 });
/// ```
#[macro_export]
macro_rules! sol_trace {
    ($event_name:expr, $payload:expr) => {
        $crate::sol_log!($crate::LogLevel::Trace, $event_name, $payload)
    };
}

/// Emits a [`SolTraceEvent`](crate::SolTraceEvent) at
/// [`LogLevel::Debug`](crate::LogLevel::Debug).
///
/// Use for internal state snapshots that are too verbose for production.
///
/// # Example
///
/// ```rust,ignore
/// use soltrace::sol_debug;
/// sol_debug!("reserves_snapshot", ReservesSnapshot { token_a: 1000, token_b: 2000 });
/// ```
#[macro_export]
macro_rules! sol_debug {
    ($event_name:expr, $payload:expr) => {
        $crate::sol_log!($crate::LogLevel::Debug, $event_name, $payload)
    };
}

/// Emits a [`SolTraceEvent`](crate::SolTraceEvent) at
/// [`LogLevel::Info`](crate::LogLevel::Info).
///
/// Use for meaningful instruction milestones that indexers should consume.
///
/// # Example
///
/// ```rust,ignore
/// use soltrace::sol_info;
/// sol_info!("swap_executed", SwapExecuted { amount: 500, user: ctx.accounts.user.key() });
/// ```
#[macro_export]
macro_rules! sol_info {
    ($event_name:expr, $payload:expr) => {
        $crate::sol_log!($crate::LogLevel::Info, $event_name, $payload)
    };
}

/// Emits a [`SolTraceEvent`](crate::SolTraceEvent) at
/// [`LogLevel::Warn`](crate::LogLevel::Warn).
///
/// Use when something unexpected occurred but the instruction can continue.
/// Off-chain monitors should alert on warn events.
///
/// # Example
///
/// ```rust,ignore
/// use soltrace::sol_warn;
/// sol_warn!("slippage_exceeded", SlippageWarning { bps: 150, limit_bps: 100 });
/// ```
#[macro_export]
macro_rules! sol_warn {
    ($event_name:expr, $payload:expr) => {
        $crate::sol_log!($crate::LogLevel::Warn, $event_name, $payload)
    };
}

/// Emits a [`SolTraceEvent`](crate::SolTraceEvent) at
/// [`LogLevel::Error`](crate::LogLevel::Error).
///
/// Use when a specific sub-operation fails but the instruction recovers and
/// returns `Ok(())`. If the instruction is about to return `Err`, emit this
/// first so the failure is recorded before the transaction reverts.
///
/// # Example
///
/// ```rust,ignore
/// use soltrace::sol_error;
/// sol_error!("oracle_stale", OracleError { age_slots: 300 });
/// ```
#[macro_export]
macro_rules! sol_error {
    ($event_name:expr, $payload:expr) => {
        $crate::sol_log!($crate::LogLevel::Error, $event_name, $payload)
    };
}