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// -------------------------------------------------------------------------------------------------
1516//! The logging framework for Nautilus systems.
1718pub mod headers;
19pub mod logger;
20pub mod writer;
2122use std::{
23 collections::HashMap,
24 env,
25 str::FromStr,
26 sync::atomic::{AtomicBool, Ordering},
27};
2829use log::LevelFilter;
30use nautilus_core::{UUID4, time::get_atomic_clock_static};
31use nautilus_model::identifiers::TraderId;
32use tracing_subscriber::EnvFilter;
33use ustr::Ustr;
3435use self::{
36 logger::{LogGuard, Logger, LoggerConfig},
37 writer::FileWriterConfig,
38};
39use crate::enums::LogLevel;
4041pub const RECV: &str = "<--";
42pub const SENT: &str = "-->";
43pub const CMD: &str = "[CMD]";
44pub const EVT: &str = "[EVT]";
45pub const DOC: &str = "[DOC]";
46pub const RPT: &str = "[RPT]";
47pub const REQ: &str = "[REQ]";
48pub const RES: &str = "[RES]";
4950static LOGGING_INITIALIZED: AtomicBool = AtomicBool::new(false);
51static LOGGING_BYPASSED: AtomicBool = AtomicBool::new(false);
52static LOGGING_REALTIME: AtomicBool = AtomicBool::new(true);
53static LOGGING_COLORED: AtomicBool = AtomicBool::new(true);
5455/// Returns whether the core logger is enabled.
56#[unsafe(no_mangle)]
57pub extern "C" fn logging_is_initialized() -> u8 {
58 u8::from(LOGGING_INITIALIZED.load(Ordering::Relaxed))
59}
6061/// Sets the logging system to bypass mode.
62#[unsafe(no_mangle)]
63pub extern "C" fn logging_set_bypass() {
64 LOGGING_BYPASSED.store(true, Ordering::Relaxed);
65}
6667/// Shuts down the logging system.
68#[unsafe(no_mangle)]
69pub extern "C" fn logging_shutdown() {
70todo!()
71}
7273/// Returns whether the core logger is using ANSI colors.
74#[unsafe(no_mangle)]
75pub extern "C" fn logging_is_colored() -> u8 {
76 u8::from(LOGGING_COLORED.load(Ordering::Relaxed))
77}
7879/// Sets the global logging clock to real-time mode.
80#[unsafe(no_mangle)]
81pub extern "C" fn logging_clock_set_realtime_mode() {
82 LOGGING_REALTIME.store(true, Ordering::Relaxed);
83}
8485/// Sets the global logging clock to static mode.
86#[unsafe(no_mangle)]
87pub extern "C" fn logging_clock_set_static_mode() {
88 LOGGING_REALTIME.store(false, Ordering::Relaxed);
89}
9091/// Sets the global logging clock static time with the given UNIX timestamp (nanoseconds).
92#[unsafe(no_mangle)]
93pub extern "C" fn logging_clock_set_static_time(time_ns: u64) {
94let clock = get_atomic_clock_static();
95 clock.set_time(time_ns.into());
96}
9798/// Initialize tracing.
99///
100/// Tracing is meant to be used to trace/debug async Rust code. It can be
101/// configured to filter modules and write up to a specific level by passing
102/// a configuration using the `RUST_LOG` environment variable.
103///
104/// # Safety
105///
106/// Should only be called once during an applications run, ideally at the
107/// beginning of the run.
108///
109/// # Errors
110///
111/// Returns an error if tracing subscriber fails to initialize.
112pub fn init_tracing() -> anyhow::Result<()> {
113// Skip tracing initialization if `RUST_LOG` is not set
114if let Ok(v) = env::var("RUST_LOG") {
115let env_filter = EnvFilter::new(v.clone());
116117 tracing_subscriber::fmt()
118 .with_env_filter(env_filter)
119 .try_init()
120 .map_err(|e| anyhow::anyhow!("Failed to initialize tracing subscriber: {e}"))?;
121122println!("Initialized tracing logs with RUST_LOG={v}");
123 }
124Ok(())
125}
126127/// Initialize logging.
128///
129/// Logging should be used for Python and sync Rust logic which is most of
130/// the components in the [nautilus_trader](https://pypi.org/project/nautilus_trader) package.
131/// Logging can be configured to filter components and write up to a specific level only
132/// by passing a configuration using the `NAUTILUS_LOG` environment variable.
133///
134/// # Safety
135///
136/// Should only be called once during an applications run, ideally at the
137/// beginning of the run.
138pub fn init_logging(
139 trader_id: TraderId,
140 instance_id: UUID4,
141 config: LoggerConfig,
142 file_config: FileWriterConfig,
143) -> anyhow::Result<LogGuard> {
144 LOGGING_INITIALIZED.store(true, Ordering::Relaxed);
145 LOGGING_COLORED.store(config.is_colored, Ordering::Relaxed);
146 Logger::init_with_config(trader_id, instance_id, config, file_config)
147}
148149#[must_use]
150pub const fn map_log_level_to_filter(log_level: LogLevel) -> LevelFilter {
151match log_level {
152 LogLevel::Off => LevelFilter::Off,
153 LogLevel::Trace => LevelFilter::Trace,
154 LogLevel::Debug => LevelFilter::Debug,
155 LogLevel::Info => LevelFilter::Info,
156 LogLevel::Warning => LevelFilter::Warn,
157 LogLevel::Error => LevelFilter::Error,
158 }
159}
160161#[must_use]
162pub fn parse_level_filter_str(s: &str) -> LevelFilter {
163let mut log_level_str = s.to_string().to_uppercase();
164if log_level_str == "WARNING" {
165 log_level_str = "WARN".to_string();
166 }
167 LevelFilter::from_str(&log_level_str)
168 .unwrap_or_else(|_| panic!("Invalid `LevelFilter` string, was {log_level_str}"))
169}
170171#[must_use]
172pub fn parse_component_levels(
173 original_map: Option<HashMap<String, serde_json::Value>>,
174) -> HashMap<Ustr, LevelFilter> {
175match original_map {
176Some(map) => {
177let mut new_map = HashMap::new();
178for (key, value) in map {
179let ustr_key = Ustr::from(&key);
180let value = parse_level_filter_str(value.as_str().unwrap());
181 new_map.insert(ustr_key, value);
182 }
183 new_map
184 }
185None => HashMap::new(),
186 }
187}