Skip to main content

algo_sdk/
log.rs

1//! HFT-safe async logging — non-blocking, ~100ns per call.
2
3// =============================================================================
4// LOGGING - HFT-safe async logging
5// =============================================================================
6
7/// Log levels for algo logging.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9#[repr(u8)]
10pub enum LogLevel {
11    Trace = 0,
12    Debug = 1,
13    Info = 2,
14    Warn = 3,
15    Error = 4,
16}
17
18// Host import - only for WASM builds
19#[cfg(target_arch = "wasm32")]
20extern "C" {
21    fn host_log_impl(level: u8, ptr: *const u8, len: u32);
22}
23
24#[cfg(target_arch = "wasm32")]
25#[inline(always)]
26fn host_log(level: u8, ptr: *const u8, len: u32) {
27    unsafe {
28        host_log_impl(level, ptr, len);
29    }
30}
31
32// Stub for native builds (no-op)
33#[cfg(not(target_arch = "wasm32"))]
34#[inline(always)]
35fn host_log(_level: u8, _ptr: *const u8, _len: u32) {}
36
37// Re-export formatted log functions at module level so log_info!/log_warn!/etc.
38// macros can use the path $crate::log::info_fmt (not $crate::log::log::info_fmt).
39pub use self::log::{debug_fmt, error_fmt, info_fmt, log_fmt, warn_fmt};
40
41/// Algo logging - non-blocking, ~100ns per call.
42/// Logs are batched and viewable via dashboard with 1-2s delay.
43pub mod log {
44    use super::LogLevel;
45    use core::fmt::Write;
46
47    /// Log buffer for formatting.
48    struct LogBuf {
49        buf: [u8; 255],
50        pos: usize,
51    }
52
53    impl LogBuf {
54        #[inline(always)]
55        const fn new() -> Self {
56            Self {
57                buf: [0u8; 255],
58                pos: 0,
59            }
60        }
61
62        #[inline(always)]
63        fn as_slice(&self) -> &[u8] {
64            &self.buf[..self.pos]
65        }
66    }
67
68    impl Write for LogBuf {
69        fn write_str(&mut self, s: &str) -> core::fmt::Result {
70            let bytes = s.as_bytes();
71            let space = self.buf.len() - self.pos;
72            let n = bytes.len().min(space);
73            self.buf[self.pos..self.pos + n].copy_from_slice(&bytes[..n]);
74            self.pos += n;
75            Ok(())
76        }
77    }
78
79    #[inline(always)]
80    fn send(level: LogLevel, msg: &[u8]) {
81        super::host_log(level as u8, msg.as_ptr(), msg.len() as u32);
82    }
83
84    /// Log a trace message.
85    #[inline(always)]
86    pub fn trace(msg: &str) {
87        send(LogLevel::Trace, msg.as_bytes());
88    }
89
90    /// Log a debug message.
91    #[inline(always)]
92    pub fn debug(msg: &str) {
93        send(LogLevel::Debug, msg.as_bytes());
94    }
95
96    /// Log an info message.
97    #[inline(always)]
98    pub fn info(msg: &str) {
99        send(LogLevel::Info, msg.as_bytes());
100    }
101
102    /// Log a warning message.
103    #[inline(always)]
104    pub fn warn(msg: &str) {
105        send(LogLevel::Warn, msg.as_bytes());
106    }
107
108    /// Log an error message.
109    #[inline(always)]
110    pub fn error(msg: &str) {
111        send(LogLevel::Error, msg.as_bytes());
112    }
113
114    /// Log with formatting (slightly slower).
115    #[inline]
116    pub fn log_fmt(level: LogLevel, args: core::fmt::Arguments<'_>) {
117        let mut buf = LogBuf::new();
118        let _ = buf.write_fmt(args);
119        send(level, buf.as_slice());
120    }
121
122    /// Formatted info log.
123    #[inline]
124    pub fn info_fmt(args: core::fmt::Arguments<'_>) {
125        log_fmt(LogLevel::Info, args);
126    }
127
128    /// Formatted warn log.
129    #[inline]
130    pub fn warn_fmt(args: core::fmt::Arguments<'_>) {
131        log_fmt(LogLevel::Warn, args);
132    }
133
134    /// Formatted error log.
135    #[inline]
136    pub fn error_fmt(args: core::fmt::Arguments<'_>) {
137        log_fmt(LogLevel::Error, args);
138    }
139
140    /// Formatted debug log.
141    #[inline]
142    pub fn debug_fmt(args: core::fmt::Arguments<'_>) {
143        log_fmt(LogLevel::Debug, args);
144    }
145}