1use cfg_if::cfg_if;
2use std::fmt;
3
4cfg_if! {
5 if #[cfg(target_arch = "bpf")] {
6 pub use workflow_log::levels::{ Level, LevelFilter };
7 } else {
8 use std::sync::Arc;
9 pub use log::{ Level, LevelFilter };
10 use downcast::{ downcast_sync, AnySync };
11 pub use hexplay::{self, HexViewBuilder};
12 pub use termcolor::Buffer;
13 pub struct ColorHexView<'a>{
16 pub builder: HexViewBuilder<'a>,
17 pub color_start: usize
18 }
19 impl<'a> ColorHexView<'a>{
20 pub fn new(builder:HexViewBuilder<'a>, colors:Vec<(&'a str, usize)>)->Self{
21 Self{
22 builder,
23 color_start:0
24 }.add_colors(colors)
25 }
26
27 pub fn add_colors(mut self, colors:Vec<(&'a str, usize)>)->Self{
28 let mut builder = self.builder;
29 for (color, len) in colors{
30 let end = self.color_start+len;
31 let range = self.color_start..end;
32 self.color_start = end;
33 builder = builder.add_color(color, range);
34 }
35 self.builder = builder;
36 self
37 }
38
39 pub fn add_colors_with_range(mut self, colors:Vec<(&'a str, std::ops::Range<usize>)>)->Self{
40 let mut builder = self.builder;
41 for (color, range) in colors{
42 builder = builder.add_color(color, range);
43 }
44 self.builder = builder;
45 self
46 }
47
48 pub fn try_print(self)->std::result::Result<(), String>{
49 let mut buf = Buffer::ansi();
50 match self.builder.finish().fmt(&mut buf){
51 Ok(()) => {
52 match String::from_utf8(buf.as_slice().to_vec()){
53 Ok(str)=>{
54 log_trace!("{}", str);
55 }
56 Err(_)=>{
57 return Err("Unable to convert HexView to string".to_string());
58 }
59 }
60 },
61 Err(_) => {
62 return Err("Unable to format HexView".to_string());
63 }
64 }
65 Ok(())
66 }
67 }
68
69 pub trait Sink : AnySync {
72 fn write(&self, target: Option<&str>, level : Level, args : &fmt::Arguments<'_>) -> bool;
73 }
74
75 struct SinkHandler {
76 sink : Arc<dyn Sink>, }
79
80 downcast_sync!(dyn Sink);
81 }
82}
83
84cfg_if! {
85 if #[cfg(target_arch = "bpf")] {
86 #[inline(always)]
87 pub fn log_level_enabled(_level: Level) -> bool {
88 true
89 }
90 } else if #[cfg(target_arch = "wasm32")] {
91 use wasm_bindgen::prelude::*;
92
93 static mut LEVEL_FILTER : LevelFilter = LevelFilter::Info;
94 #[inline(always)]
95 pub fn log_level_enabled(level: Level) -> bool {
96 unsafe { LEVEL_FILTER >= level }
97 }
98 pub fn set_log_level(level: LevelFilter) {
99 unsafe { LEVEL_FILTER = level };
100 }
101
102 #[wasm_bindgen]
103 extern "C" {
104 #[wasm_bindgen(typescript_type = r###""off" | "error" | "warn" | "info" | "debug" | "trace""###)]
105 #[derive(Debug)]
106 pub type LogLevelT;
107 }
108
109 #[doc="Set the logger log level using a string representation."]
110 #[doc="Available variants are: 'off', 'error', 'warn', 'info', 'debug', 'trace'"]
111 #[doc="@category General"]
112 #[wasm_bindgen(js_name = "setLogLevel")]
113 pub fn set_log_level_wasm(level: LogLevelT) {
115 if let Some(level) = level.as_string() {
116 let level = match level.as_str() {
117 "off" => LevelFilter::Off,
118 "error" => LevelFilter::Error,
119 "warn" => LevelFilter::Warn,
120 "info" => LevelFilter::Info,
121 "debug" => LevelFilter::Debug,
122 "trace" => LevelFilter::Trace,
123 _ => panic!("Invalid log level: {level}"),
124 };
125 set_log_level(level);
126 } else {
127 panic!("log level must be a string, received: {level:?}");
128 }
129 }
130
131 cfg_if! {
132 if #[cfg(feature = "sink")] {
133 use std::sync::Mutex;
134 static SINK : Mutex<Option<SinkHandler>> = Mutex::new(None);
135 pub fn pipe(sink : Option<Arc<dyn Sink>>) {
137 match sink {
138 Some(sink) => { *SINK.lock().unwrap() = Some(SinkHandler { sink }); },
139 None => { *SINK.lock().unwrap() = None; }
140 }
141 }
142 #[inline(always)]
143 fn to_sink(target: Option<&str>, level : Level, args : &fmt::Arguments<'_>) -> bool {
144 match SINK.lock().unwrap().as_ref() {
145 Some(handler) => {
146 handler.sink.write(target, level, args)
147 },
148 None => { false }
149 }
150 }
151 }
152 }
153
154 } else {
155 use std::sync::Mutex;
156
157 lazy_static::lazy_static! {
158 static ref LEVEL_FILTER : Mutex<LevelFilter> = Mutex::new(LevelFilter::Info);
159 }
160 #[inline(always)]
161 pub fn log_level_enabled(level: Level) -> bool {
164 *LEVEL_FILTER.lock().unwrap() >= level
165 }
166 pub fn set_log_level(level: LevelFilter) {
168 *LEVEL_FILTER.lock().unwrap() = level;
169 }
170 cfg_if! {
171 if #[cfg(feature = "sink")] {
172 lazy_static::lazy_static! {
173 static ref SINK : Mutex<Option<SinkHandler>> = Mutex::new(None);
174 }
175 pub fn pipe(sink : Option<Arc<dyn Sink>>) {
180 match sink {
181 Some(sink) => { *SINK.lock().unwrap() = Some(SinkHandler { sink }); },
182 None => { *SINK.lock().unwrap() = None; }
183 }
184
185 }
186 #[inline(always)]
187 fn to_sink(target : Option<&str>, level : Level, args : &fmt::Arguments<'_>) -> bool {
188 match SINK.lock().unwrap().as_ref() {
189 Some(handler) => {
190 handler.sink.write(target, level, args)
191 },
192 None => { false }
193 }
194 }
195 }
196 }
197
198 #[cfg(feature = "external-logger")]
199 mod workflow_logger {
200 use log::{ Level, LevelFilter, Record, Metadata, SetLoggerError };
201
202 pub struct WorkflowLogger;
203
204 impl log::Log for WorkflowLogger {
205 fn enabled(&self, metadata: &Metadata) -> bool {
206 super::log_level_enabled(metadata.level())
207 }
208
209 fn log(&self, record: &Record) {
210 if self.enabled(record.metadata()) {
211 match record.metadata().level() {
212 Level::Error => { super::error_impl(record.args()); },
213 Level::Warn => { super::warn_impl(record.args()); },
214 Level::Info => { super::info_impl(record.args()); },
215 Level::Debug => { super::debug_impl(record.args()); },
216 Level::Trace => { super::trace_impl(record.args()); },
217 }
218 }
219 }
220
221 fn flush(&self) {}
222 }
223
224 static LOGGER: WorkflowLogger = WorkflowLogger;
225
226 pub fn init() -> Result<(), SetLoggerError> {
227 log::set_logger(&LOGGER)
228 .map(|()| log::set_max_level(LevelFilter::Trace))
229 }
230 }
231
232 #[cfg(feature = "external-logger")]
233 pub fn init() -> Result<(), log::SetLoggerError> {
234 workflow_logger::init()
235 }
236
237 }
238}
239
240#[cfg(target_arch = "wasm32")]
241pub mod wasm_log {
242 use wasm_bindgen::prelude::*;
243
244 #[wasm_bindgen]
245 extern "C" {
246 #[wasm_bindgen(js_namespace = console)]
247 pub fn log(s: &str);
248 #[wasm_bindgen(js_namespace = console)]
249 pub fn warn(s: &str);
250 #[wasm_bindgen(js_namespace = console)]
251 pub fn error(s: &str);
252 }
253}
254
255pub mod impls {
256 use super::*;
257
258 #[inline(always)]
259 #[allow(unused_variables)]
260 pub fn error_impl(target: Option<&str>, args: &fmt::Arguments<'_>) {
261 if log_level_enabled(Level::Error) {
262 #[cfg(all(not(target_arch = "bpf"), feature = "sink"))]
263 {
264 if to_sink(target, Level::Error, args) {
265 return;
266 }
267 }
268 cfg_if! {
269 if #[cfg(target_arch = "wasm32")] {
270 workflow_log::wasm_log::error(&args.to_string());
271 } else if #[cfg(target_arch = "bpf")] {
272 solana_program::log::sol_log(&args.to_string());
273 } else {
274 println!("{args}");
275 }
276 }
277 }
278 }
279
280 #[inline(always)]
281 #[allow(unused_variables)]
282 pub fn warn_impl(target: Option<&str>, args: &fmt::Arguments<'_>) {
283 if log_level_enabled(Level::Warn) {
284 #[cfg(all(not(target_arch = "bpf"), feature = "sink"))]
285 {
286 if to_sink(target, Level::Warn, args) {
287 return;
288 }
289 }
290 cfg_if! {
291 if #[cfg(target_arch = "wasm32")] {
292 workflow_log::wasm_log::warn(&args.to_string());
293 } else if #[cfg(target_arch = "bpf")] {
294 solana_program::log::sol_log(&args.to_string());
295 } else {
296 println!("{args}");
297 }
298 }
299 }
300 }
301
302 #[inline(always)]
303 #[allow(unused_variables)]
304 pub fn info_impl(target: Option<&str>, args: &fmt::Arguments<'_>) {
305 if log_level_enabled(Level::Info) {
306 #[cfg(all(not(target_arch = "bpf"), feature = "sink"))]
307 {
308 if to_sink(target, Level::Info, args) {
309 return;
310 }
311 }
312 cfg_if! {
313 if #[cfg(target_arch = "wasm32")] {
314 workflow_log::wasm_log::log(&args.to_string());
315 } else if #[cfg(target_arch = "bpf")] {
316 solana_program::log::sol_log(&args.to_string());
317 } else {
318 println!("{args}");
319 }
320 }
321 }
322 }
323
324 #[inline(always)]
325 #[allow(unused_variables)]
326 pub fn debug_impl(target: Option<&str>, args: &fmt::Arguments<'_>) {
327 if log_level_enabled(Level::Debug) {
328 #[cfg(all(not(target_arch = "bpf"), feature = "sink"))]
329 {
330 if to_sink(target, Level::Debug, args) {
331 return;
332 }
333 }
334 cfg_if! {
335 if #[cfg(target_arch = "wasm32")] {
336 workflow_log::wasm_log::log(&args.to_string());
337 } else if #[cfg(target_arch = "bpf")] {
338 solana_program::log::sol_log(&args.to_string());
339 } else {
340 println!("{args}");
341 }
342 }
343 }
344 }
345
346 #[inline(always)]
347 #[allow(unused_variables)]
348 pub fn trace_impl(target: Option<&str>, args: &fmt::Arguments<'_>) {
349 if log_level_enabled(Level::Trace) {
350 #[cfg(all(not(target_arch = "bpf"), feature = "sink"))]
351 {
352 if to_sink(target, Level::Trace, args) {
353 return;
354 }
355 }
356 cfg_if! {
357 if #[cfg(target_arch = "wasm32")] {
358 workflow_log::wasm_log::log(&args.to_string());
359 } else if #[cfg(target_arch = "bpf")] {
360 solana_program::log::sol_log(&args.to_string());
361 } else {
362 println!("{args}");
363 }
364 }
365 }
366 }
367}
368
369#[macro_export]
371macro_rules! log_error {
372 (target: $target:expr, $($arg:tt)+) => (
373 workflow_log::impls::error_impl(Some($target),&format_args!($($t)*))
374 );
375
376 ($($t:tt)*) => (
377 workflow_log::impls::error_impl(None,&format_args!($($t)*))
378 )
379}
380
381#[macro_export]
383macro_rules! log_warn {
384 (target: $target:expr, $($arg:tt)+) => (
385 workflow_log::impls::warn_impl(Some($target),&format_args!($($t)*))
386 );
387
388 ($($t:tt)*) => (
389 workflow_log::impls::warn_impl(None,&format_args!($($t)*))
390 )
391}
392
393#[macro_export]
395macro_rules! log_info {
396 (target: $target:expr, $($arg:tt)+) => (
397 workflow_log::impls::info_impl(Some($target),&format_args!($($t)*))
398 );
399
400 ($($t:tt)*) => (
401 workflow_log::impls::info_impl(None,&format_args!($($t)*))
402 )
403}
404
405#[macro_export]
407macro_rules! log_debug {
408 (target: $target:expr, $($arg:tt)+) => (
409 workflow_log::impls::debug_impl(Some($target),&format_args!($($t)*))
410 );
411
412 ($($t:tt)*) => (
413 workflow_log::impls::debug_impl(None,&format_args!($($t)*))
414 )
415}
416
417#[macro_export]
419macro_rules! log_trace {
420 (target: $target:expr, $($arg:tt)+) => (
421 workflow_log::impls::trace_impl(Some($target),&format_args!($($t)*))
422 );
423
424 ($($t:tt)*) => (
425 workflow_log::impls::trace_impl(None,&format_args!($($t)*))
426 )
427}
428
429pub use log_debug;
430pub use log_error;
431pub use log_info;
432pub use log_trace;
433pub use log_warn;
434
435#[cfg(not(target_arch = "bpf"))]
438pub fn trace_hex(data: &[u8]) {
439 let hex = format_hex(data);
440 log_trace!("{}", hex);
441}
442
443#[cfg(not(target_arch = "bpf"))]
446pub fn format_hex(data: &[u8]) -> String {
447 let view = hexplay::HexViewBuilder::new(data)
448 .address_offset(0)
449 .row_width(16)
450 .finish();
451
452 format!("{view}")
453}
454
455#[cfg(not(target_arch = "bpf"))]
457pub fn format_hex_with_colors<'a>(
458 data: &'a [u8],
459 colors: Vec<(&'a str, usize)>,
460) -> ColorHexView<'a> {
461 let view_builder = hexplay::HexViewBuilder::new(data)
462 .address_offset(0)
463 .row_width(16);
464
465 ColorHexView::new(view_builder, colors)
466}
467#[cfg(not(target_arch = "bpf"))]
468pub mod color_log {
469 use super::*;
470 type Index = usize;
471 type Length = usize;
472 type Color<'a> = &'a str;
473 type Result<T> = std::result::Result<T, String>;
474 pub trait ColoLogTrace {
475 fn log_data(&self) -> Vec<u8>;
476 fn log_index_length_color(&self) -> Option<Vec<(Index, Length, Color)>> {
477 None
478 }
479
480 fn log_trace(&self) -> Result<bool> {
481 let data_vec = self.log_data();
482 let mut view = format_hex_with_colors(&data_vec, vec![]);
483 if let Some(index_length_color) = self.log_index_length_color() {
484 let mut colors = Vec::new();
485 for (index, length, color) in index_length_color {
486 colors.push((color, index..index + length));
487 }
488 view = view.add_colors_with_range(colors);
489 }
490
491 if view.try_print().is_err() {
492 trace_hex(&data_vec);
493 return Ok(false);
494 }
495 Ok(true)
496 }
497 }
498}
499
500#[cfg(not(target_arch = "bpf"))]
501pub use color_log::*;