nautilus_common/ffi/
logging.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{
17    ffi::c_char,
18    ops::{Deref, DerefMut},
19};
20
21use nautilus_core::{
22    ffi::{
23        parsing::{optional_bytes_to_json, u8_as_bool},
24        string::{cstr_as_str, cstr_to_ustr, optional_cstr_to_str},
25    },
26    UUID4,
27};
28use nautilus_model::identifiers::TraderId;
29
30use crate::{
31    enums::{LogColor, LogLevel},
32    logging::{
33        self, headers,
34        logger::{self, LogGuard, LoggerConfig},
35        logging_set_bypass, map_log_level_to_filter, parse_component_levels,
36        writer::FileWriterConfig,
37    },
38};
39
40/// C compatible Foreign Function Interface (FFI) for an underlying [`LogGuard`].
41///
42/// This struct wraps `LogGuard` in a way that makes it compatible with C function
43/// calls, enabling interaction with `LogGuard` in a C environment.
44///
45/// It implements the `Deref` trait, allowing instances of `LogGuard_API` to be
46/// dereferenced to `LogGuard`, providing access to `LogGuard`'s methods without
47/// having to manually access the underlying `LogGuard` instance.
48#[repr(C)]
49#[allow(non_camel_case_types)]
50pub struct LogGuard_API(Box<LogGuard>);
51
52impl Deref for LogGuard_API {
53    type Target = LogGuard;
54
55    fn deref(&self) -> &Self::Target {
56        &self.0
57    }
58}
59
60impl DerefMut for LogGuard_API {
61    fn deref_mut(&mut self) -> &mut Self::Target {
62        &mut self.0
63    }
64}
65
66/// Initializes logging.
67///
68/// Logging should be used for Python and sync Rust logic which is most of
69/// the components in the main `nautilus_trader` package.
70/// Logging can be configured to filter components and write up to a specific level only
71/// by passing a configuration using the `NAUTILUS_LOG` environment variable.
72///
73/// # Safety
74///
75/// Should only be called once during an applications run, ideally at the
76/// beginning of the run.
77///
78/// - Assume `directory_ptr` is either NULL or a valid C string pointer.
79/// - Assume `file_name_ptr` is either NULL or a valid C string pointer.
80/// - Assume `file_format_ptr` is either NULL or a valid C string pointer.
81/// - Assume `component_level_ptr` is either NULL or a valid C string pointer.
82#[no_mangle]
83pub unsafe extern "C" fn logging_init(
84    trader_id: TraderId,
85    instance_id: UUID4,
86    level_stdout: LogLevel,
87    level_file: LogLevel,
88    directory_ptr: *const c_char,
89    file_name_ptr: *const c_char,
90    file_format_ptr: *const c_char,
91    component_levels_ptr: *const c_char,
92    is_colored: u8,
93    is_bypassed: u8,
94    print_config: u8,
95) -> LogGuard_API {
96    let level_stdout = map_log_level_to_filter(level_stdout);
97    let level_file = map_log_level_to_filter(level_file);
98
99    let component_levels_json = optional_bytes_to_json(component_levels_ptr);
100    let component_levels = parse_component_levels(component_levels_json);
101
102    let config = LoggerConfig::new(
103        level_stdout,
104        level_file,
105        component_levels,
106        u8_as_bool(is_colored),
107        u8_as_bool(print_config),
108    );
109
110    let directory = optional_cstr_to_str(directory_ptr).map(std::string::ToString::to_string);
111    let file_name = optional_cstr_to_str(file_name_ptr).map(std::string::ToString::to_string);
112    let file_format = optional_cstr_to_str(file_format_ptr).map(std::string::ToString::to_string);
113    let file_config = FileWriterConfig::new(directory, file_name, file_format);
114
115    if u8_as_bool(is_bypassed) {
116        logging_set_bypass();
117    }
118
119    LogGuard_API(Box::new(logging::init_logging(
120        trader_id,
121        instance_id,
122        config,
123        file_config,
124    )))
125}
126
127/// Creates a new log event.
128///
129/// # Safety
130///
131/// - Assumes `component_ptr` is a valid C string pointer.
132/// - Assumes `message_ptr` is a valid C string pointer.
133#[no_mangle]
134pub unsafe extern "C" fn logger_log(
135    level: LogLevel,
136    color: LogColor,
137    component_ptr: *const c_char,
138    message_ptr: *const c_char,
139) {
140    let component = cstr_to_ustr(component_ptr);
141    let message = cstr_as_str(message_ptr);
142
143    logger::log(level, color, component, message);
144}
145
146/// Logs the Nautilus system header.
147///
148/// # Safety
149///
150/// - Assumes `machine_id_ptr` is a valid C string pointer.
151/// - Assumes `component_ptr` is a valid C string pointer.
152#[no_mangle]
153pub unsafe extern "C" fn logging_log_header(
154    trader_id: TraderId,
155    machine_id_ptr: *const c_char,
156    instance_id: UUID4,
157    component_ptr: *const c_char,
158) {
159    let component = cstr_to_ustr(component_ptr);
160    let machine_id = cstr_as_str(machine_id_ptr);
161    headers::log_header(trader_id, machine_id, instance_id, component);
162}
163
164/// Logs system information.
165///
166/// # Safety
167///
168/// - Assumes `component_ptr` is a valid C string pointer.
169#[no_mangle]
170pub unsafe extern "C" fn logging_log_sysinfo(component_ptr: *const c_char) {
171    let component = cstr_to_ustr(component_ptr);
172    headers::log_sysinfo(component);
173}
174
175/// Flushes global logger buffers of any records.
176#[no_mangle]
177pub extern "C" fn logger_drop(log_guard: LogGuard_API) {
178    drop(log_guard);
179}