1use freeswitch_sys::{switch_log_level_t, switch_log_printf, switch_text_channel_t};
2use log::Log;
3use std::{ffi::CString, ptr::null};
4
5use crate::{
6 core::{Session, SessionExt},
7 utils::FSNewType,
8};
9
10#[repr(transparent)]
11pub struct FSTextChannel(switch_text_channel_t);
12
13macro_rules! logging_macro {
14 ($name:ident, $t:expr, $fn:expr) => {
15 #[macro_export]
16 macro_rules! $name {
17 ($e:expr) => {
18 $crate::fslog::FSLoggerWithData(($fn)($e), $t)
19 };
20 () => {
21 $crate::fslog::FSLoggerWithData(($fn)($e), $t)
22 };
23 }
24 pub use $name;
25 };
26}
27
28#[doc(hidden)]
29pub fn convert_session<T: SessionExt>(s: &T) -> *const ::std::os::raw::c_char {
30 s.as_ptr().cast()
31}
32
33#[doc(hidden)]
34pub fn convert_session_clean<T: SessionExt>(s: &T) -> *const ::std::os::raw::c_char {
35 s.get_uuid().as_ptr().cast()
36}
37
38logging_macro!(channel_log, SWITCH_CHANNEL_ID_LOG, || null().cast());
39logging_macro!(channel_log_clean, SWITCH_CHANNEL_ID_LOG_CLEAN, || null()
40 .cast());
41logging_macro!(
42 session_log,
43 SWITCH_CHANNEL_ID_SESSION,
44 freeswitch_rs::fslog::convert_session
45);
46logging_macro!(
47 session_log_clean,
48 SWITCH_CHANNEL_ID_SESSION,
49 freeswitch_rs::fslog::convert_session_clean
50);
51
52pub const SWITCH_CHANNEL_ID_LOG: FSTextChannel =
53 FSTextChannel(switch_text_channel_t::SWITCH_CHANNEL_ID_LOG);
54pub const SWITCH_CHANNEL_ID_LOG_CLEAN: FSTextChannel =
55 FSTextChannel(switch_text_channel_t::SWITCH_CHANNEL_ID_LOG_CLEAN);
56pub const SWITCH_CHANNEL_ID_EVENT: FSTextChannel =
57 FSTextChannel(switch_text_channel_t::SWITCH_CHANNEL_ID_EVENT);
58pub const SWITCH_CHANNEL_ID_SESSION: FSTextChannel =
59 FSTextChannel(switch_text_channel_t::SWITCH_CHANNEL_ID_SESSION);
60
61pub struct FSLoggerWithData(pub *const ::std::os::raw::c_char, pub FSTextChannel);
63unsafe impl Send for FSLoggerWithData {}
65unsafe impl Sync for FSLoggerWithData {}
66
67impl Log for FSLoggerWithData {
68 fn enabled(&self, metadata: &log::Metadata) -> bool {
69 FSLogger.enabled(metadata)
70 }
71 fn log(&self, record: &log::Record) {
72 FSLogger.log_with_userdata(record, SWITCH_CHANNEL_ID_SESSION, self.0.cast());
73 }
74 fn flush(&self) {
75 FSLogger.flush();
76 }
77}
78
79pub struct FSLogger;
82
83impl FSLogger {
84 fn log_with_userdata(
85 &self,
86 record: &log::Record,
87 text_channel: FSTextChannel,
88 userdata: *const ::std::os::raw::c_char,
89 ) {
90 if self.enabled(record.metadata()) {
91 let file = record
92 .file()
93 .and_then(|s| CString::new(s).ok())
94 .map(|s| s.into_raw());
95 let line = record.line().unwrap_or(0);
96 let func = std::ptr::null();
97 let level = match record.metadata().level() {
98 log::Level::Warn => switch_log_level_t::SWITCH_LOG_WARNING,
99 log::Level::Info => switch_log_level_t::SWITCH_LOG_INFO,
100 log::Level::Error => switch_log_level_t::SWITCH_LOG_ERROR,
101 log::Level::Debug => switch_log_level_t::SWITCH_LOG_DEBUG,
102 log::Level::Trace => switch_log_level_t::SWITCH_LOG_DEBUG10,
103 };
104 let fmt = format!("{}\n", record.args());
105 let fmt_c = CString::new(fmt).unwrap();
106
107 unsafe {
108 switch_log_printf(
109 text_channel.0,
110 file.unwrap_or(std::ptr::null_mut()),
111 func,
112 line.try_into().unwrap_or(0),
113 userdata,
114 level,
115 fmt_c.into_raw(),
116 )
117 }
118 }
119 }
120}
121
122impl Log for FSLogger {
123 fn enabled(&self, _metadata: &log::Metadata) -> bool {
124 true
125 }
126
127 fn log(&self, record: &log::Record) {
128 self.log_with_userdata(record, SWITCH_CHANNEL_ID_LOG, null());
129 }
130
131 fn flush(&self) {}
132}