1use std::{
17 ffi::c_char,
18 ops::{Deref, DerefMut},
19};
20
21use ahash::AHashMap;
22use nautilus_core::{
23 UUID4,
24 ffi::{
25 parsing::{optional_bytes_to_json, u8_as_bool},
26 string::{cstr_as_str, cstr_to_ustr, optional_cstr_to_str},
27 },
28};
29use nautilus_model::identifiers::TraderId;
30
31use crate::{
32 enums::{LogColor, LogLevel},
33 logging::{
34 headers, init_logging,
35 logger::{self, LogGuard, LoggerConfig},
36 map_log_level_to_filter, parse_component_levels,
37 writer::FileWriterConfig,
38 },
39};
40
41#[repr(C)]
50#[derive(Debug)]
51#[allow(non_camel_case_types)]
52pub struct LogGuard_API(Box<LogGuard>);
53
54impl Deref for LogGuard_API {
55 type Target = LogGuard;
56
57 fn deref(&self) -> &Self::Target {
58 &self.0
59 }
60}
61
62impl DerefMut for LogGuard_API {
63 fn deref_mut(&mut self) -> &mut Self::Target {
64 &mut self.0
65 }
66}
67
68#[unsafe(no_mangle)]
90pub unsafe extern "C" fn logging_init(
91 trader_id: TraderId,
92 instance_id: UUID4,
93 level_stdout: LogLevel,
94 level_file: LogLevel,
95 directory_ptr: *const c_char,
96 file_name_ptr: *const c_char,
97 file_format_ptr: *const c_char,
98 component_levels_ptr: *const c_char,
99 is_colored: u8,
100 is_bypassed: u8,
101 print_config: u8,
102 log_components_only: u8,
103 max_file_size: u64,
104 max_backup_count: u32,
105) -> LogGuard_API {
106 unsafe {
107 logging_init_with_options(
108 trader_id,
109 instance_id,
110 level_stdout,
111 level_file,
112 directory_ptr,
113 file_name_ptr,
114 file_format_ptr,
115 component_levels_ptr,
116 is_colored,
117 is_bypassed,
118 print_config,
119 log_components_only,
120 max_file_size,
121 max_backup_count,
122 true.into(),
123 false.into(),
124 )
125 }
126}
127
128#[unsafe(no_mangle)]
138pub unsafe extern "C" fn logging_init_with_options(
139 trader_id: TraderId,
140 instance_id: UUID4,
141 level_stdout: LogLevel,
142 level_file: LogLevel,
143 directory_ptr: *const c_char,
144 file_name_ptr: *const c_char,
145 file_format_ptr: *const c_char,
146 component_levels_ptr: *const c_char,
147 is_colored: u8,
148 is_bypassed: u8,
149 print_config: u8,
150 log_components_only: u8,
151 max_file_size: u64,
152 max_backup_count: u32,
153 fileout_sync_on_flush: u8,
154 buffered_stdout: u8,
155) -> LogGuard_API {
156 let level_stdout = map_log_level_to_filter(level_stdout);
157 let level_file = map_log_level_to_filter(level_file);
158
159 let component_levels_json = unsafe { optional_bytes_to_json(component_levels_ptr) };
160 let component_levels = parse_component_levels(component_levels_json)
161 .expect("Failed to parse component log levels");
162
163 let mut config = LoggerConfig::new(
164 level_stdout,
165 level_file,
166 component_levels,
167 AHashMap::new(), u8_as_bool(log_components_only),
169 u8_as_bool(is_colored),
170 u8_as_bool(print_config),
171 false, u8_as_bool(is_bypassed),
173 None, false, );
176 config.fileout_sync_on_flush = u8_as_bool(fileout_sync_on_flush);
177 config.buffered_stdout = u8_as_bool(buffered_stdout);
178
179 let file_rotate = if max_file_size > 0 {
181 Some((max_file_size, max_backup_count))
182 } else {
183 None
184 };
185
186 let directory = unsafe { optional_cstr_to_str(directory_ptr).map(ToString::to_string) };
187 let file_name = unsafe { optional_cstr_to_str(file_name_ptr).map(ToString::to_string) };
188 let file_format = unsafe { optional_cstr_to_str(file_format_ptr).map(ToString::to_string) };
189
190 let file_config = FileWriterConfig::new(directory, file_name, file_format, file_rotate);
191
192 if u8_as_bool(is_bypassed) {
193 logging_set_bypass();
194 }
195
196 LogGuard_API(Box::new(
197 init_logging(trader_id, instance_id, config, file_config)
198 .expect("Failed to initialize logging"),
199 ))
200}
201
202#[unsafe(no_mangle)]
210pub unsafe extern "C" fn logger_log(
211 level: LogLevel,
212 color: LogColor,
213 component_ptr: *const c_char,
214 message_ptr: *const c_char,
215) {
216 let component = unsafe { cstr_to_ustr(component_ptr) };
217 let message = unsafe { cstr_as_str(message_ptr) };
218
219 logger::log(level, color, component, message);
220}
221
222#[unsafe(no_mangle)]
230pub unsafe extern "C" fn logging_log_header(
231 trader_id: TraderId,
232 machine_id_ptr: *const c_char,
233 instance_id: UUID4,
234 component_ptr: *const c_char,
235) {
236 let component = unsafe { cstr_to_ustr(component_ptr) };
237 let machine_id = unsafe { cstr_as_str(machine_id_ptr) };
238 headers::log_header(trader_id, machine_id, instance_id, component);
239}
240
241#[unsafe(no_mangle)]
247pub unsafe extern "C" fn logging_log_sysinfo(component_ptr: *const c_char) {
248 let component = unsafe { cstr_to_ustr(component_ptr) };
249 headers::log_sysinfo(component);
250}
251
252#[unsafe(no_mangle)]
254pub extern "C" fn logger_flush() {
255 log::logger().flush();
256}
257
258#[unsafe(no_mangle)]
260pub extern "C" fn logging_sync_to_disk() -> u8 {
261 u8::from(crate::logging::logging_sync_to_disk().is_ok())
262}
263
264#[unsafe(no_mangle)]
266pub extern "C" fn logger_drop(log_guard: LogGuard_API) {
267 drop(log_guard);
268}
269
270#[unsafe(no_mangle)]
271pub extern "C" fn logging_is_initialized() -> u8 {
272 u8::from(crate::logging::logging_is_initialized())
273}
274
275#[unsafe(no_mangle)]
276pub extern "C" fn logging_set_bypass() {
277 crate::logging::logging_set_bypass();
278}
279
280#[unsafe(no_mangle)]
281pub extern "C" fn logging_shutdown() {
282 crate::logging::logging_shutdown();
283}
284
285#[unsafe(no_mangle)]
286pub extern "C" fn logging_is_colored() -> u8 {
287 u8::from(crate::logging::logging_is_colored())
288}
289
290#[unsafe(no_mangle)]
291pub extern "C" fn logging_clock_set_realtime_mode() {
292 crate::logging::logging_clock_set_realtime_mode();
293}
294
295#[unsafe(no_mangle)]
296pub extern "C" fn logging_clock_set_static_mode() {
297 crate::logging::logging_clock_set_static_mode();
298}
299
300#[unsafe(no_mangle)]
301pub extern "C" fn logging_clock_set_static_time(time_ns: u64) {
302 crate::logging::logging_clock_set_static_time(time_ns);
303}