Skip to content

Commit

Permalink
Refine logging config
Browse files Browse the repository at this point in the history
  • Loading branch information
cjdsellers committed Jan 5, 2024
1 parent c5a6eb3 commit 37828d9
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 49 deletions.
55 changes: 15 additions & 40 deletions nautilus_core/common/src/ffi/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,17 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------

use std::{
env,
ffi::{c_char, CStr},
};
use std::ffi::{c_char, CStr};

use log::{
debug, error, info,
kv::{ToValue, Value},
warn,
};
use nautilus_core::{
ffi::string::{cstr_to_string, cstr_to_ustr, optional_cstr_to_string},
uuid::UUID4,
};
use nautilus_model::identifiers::trader_id::TraderId;
use tracing_subscriber::EnvFilter;

use crate::{
enums::{LogColor, LogLevel},
logging::{FileWriterConfig, Logger, LoggerConfig},
logging::{self},
};

/// Initialize tracing.
Expand All @@ -47,14 +38,7 @@ use crate::{
/// beginning of the run.
#[no_mangle]
pub extern "C" fn tracing_init() {
// Skip tracing initialization if `RUST_LOG` is not set
if let Ok(v) = env::var("RUST_LOG") {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::new(v.clone()))
.try_init()
.unwrap_or_else(|e| eprintln!("Cannot set tracing subscriber because of error: {e}"));
println!("Initialized tracing logs with RUST_LOG={v}");
}
logging::init_tracing();
}

/// Initialize logging.
Expand All @@ -70,27 +54,32 @@ pub extern "C" fn tracing_init() {
/// beginning of the run.
///
/// - Assume `config_spec_ptr` is a valid C string pointer.
/// - Assume `file_directory_ptr` is either NULL or a valid C string pointer.
/// - Assume `directory_ptr` is either NULL or a valid C string pointer.
/// - Assume `file_name_ptr` is either NULL or a valid C string pointer.
/// - Assume `file_format_ptr` is either NULL or a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn logging_init(
trader_id: TraderId,
instance_id: UUID4,
config_spec_ptr: *const c_char,
file_directory_ptr: *const c_char,
directory_ptr: *const c_char,
file_name_ptr: *const c_char,
file_format_ptr: *const c_char,
) {
let config_spec = cstr_to_string(config_spec_ptr);
let config = LoggerConfig::from_spec(&config_spec);

let directory = optional_cstr_to_string(file_directory_ptr);
let directory = optional_cstr_to_string(directory_ptr);
let file_name = optional_cstr_to_string(file_name_ptr);
let file_format = optional_cstr_to_string(file_format_ptr);
let file_writer_config = FileWriterConfig::new(directory, file_name, file_format);

Logger::init_with_config(trader_id, instance_id, file_writer_config, config);
logging::init_logging(
trader_id,
instance_id,
config_spec,
directory,
file_name,
file_format,
);
}

/// Create a new log event.
Expand All @@ -108,23 +97,9 @@ pub unsafe extern "C" fn logger_log(
message_ptr: *const c_char,
) {
let component = cstr_to_ustr(component_ptr);
let color = Value::from(color as u8);
let message = CStr::from_ptr(message_ptr).to_string_lossy();

match level {
LogLevel::Debug => {
debug!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
LogLevel::Info => {
info!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
LogLevel::Warning => {
warn!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
LogLevel::Error => {
error!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
}
logging::log(timestamp_ns, level, color, component, message);
}

/// Flush logger buffers.
Expand Down
81 changes: 79 additions & 2 deletions nautilus_core/common/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// -------------------------------------------------------------------------------------------------

use std::{
borrow::Cow,
collections::HashMap,
env, fmt,
fs::{create_dir_all, File},
Expand All @@ -25,13 +26,18 @@ use std::{
};

use chrono::{prelude::*, Utc};
use log::{kv::Key, set_boxed_logger, set_max_level, Level, LevelFilter, Log, STATIC_MAX_LEVEL};
use log::{
debug, error, info,
kv::{Key, ToValue, Value},
set_boxed_logger, set_max_level, warn, Level, LevelFilter, Log, STATIC_MAX_LEVEL,
};
use nautilus_core::{datetime::unix_nanos_to_iso8601, time::UnixNanos, uuid::UUID4};
use nautilus_model::identifiers::trader_id::TraderId;
use serde::{Deserialize, Serialize};
use tracing_subscriber::EnvFilter;
use ustr::Ustr;

use crate::enums::LogColor;
use crate::enums::{LogColor, LogLevel};

#[cfg_attr(
feature = "python",
Expand Down Expand Up @@ -141,6 +147,52 @@ impl FileWriterConfig {
}
}

/// Initialize tracing.
///
/// Tracing is meant to be used to trace/debug async Rust code. It can be
/// configured to filter modules and write up to a specific level only using
/// by passing a configuration using the `RUST_LOG` environment variable.
///
/// # Safety
///
/// Should only be called once during an applications run, ideally at the
/// beginning of the run.
pub fn init_tracing() {
// Skip tracing initialization if `RUST_LOG` is not set
if let Ok(v) = env::var("RUST_LOG") {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::new(v.clone()))
.try_init()
.unwrap_or_else(|e| eprintln!("Cannot set tracing subscriber because of error: {e}"));
println!("Initialized tracing logs with RUST_LOG={v}");
}
}

/// Initialize logging.
///
/// Logging should be used for Python and sync Rust logic which is most of
/// the components in the main `nautilus_trader` package.
/// Logging can be configured to filter components and write up to a specific level only
/// by passing a configuration using the `NAUTILUS_LOG` environment variable.
///
/// # Safety
///
/// Should only be called once during an applications run, ideally at the
/// beginning of the run.
pub fn init_logging(
trader_id: TraderId,
instance_id: UUID4,
config_spec: String,
directory: Option<String>,
file_name: Option<String>,
file_format: Option<String>,
) {
let config = LoggerConfig::from_spec(&config_spec);
let file_writer_config = FileWriterConfig::new(directory, file_name, file_format);

Logger::init_with_config(trader_id, instance_id, file_writer_config, config);
}

/// Provides a high-performance logger utilizing a MPSC channel under the hood.
///
/// A separate thead is spawned at initialization which receives [`LogEvent`] structs over the
Expand Down Expand Up @@ -557,6 +609,31 @@ impl Logger {
}
}

pub fn log(
timestamp_ns: UnixNanos,
level: LogLevel,
color: LogColor,
component: Ustr,
message: Cow<'_, str>,
) {
let color = Value::from(color as u8);

match level {
LogLevel::Debug => {
debug!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
LogLevel::Info => {
info!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
LogLevel::Warning => {
warn!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
LogLevel::Error => {
error!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
}
}
}

////////////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////////////
Expand Down
113 changes: 113 additions & 0 deletions nautilus_core/common/src/python/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// -------------------------------------------------------------------------------------------------
// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------

use std::borrow::Cow;

use nautilus_core::{time::UnixNanos, uuid::UUID4};
use nautilus_model::identifiers::trader_id::TraderId;
use pyo3::prelude::*;
use ustr::Ustr;

use crate::{
enums::{LogColor, LogLevel},
logging::{self, FileWriterConfig, LoggerConfig},
};

/// Initialize tracing.
///
/// Tracing is meant to be used to trace/debug async Rust code. It can be
/// configured to filter modules and write up to a specific level only using
/// by passing a configuration using the `RUST_LOG` environment variable.
///
/// # Safety
///
/// Should only be called once during an applications run, ideally at the
/// beginning of the run.
#[pyfunction()]
#[pyo3(name = "init_tracing")]
pub fn py_init_tracing() {
logging::init_tracing();
}

/// Initialize logging.
///
/// Logging should be used for Python and sync Rust logic which is most of
/// the components in the main `nautilus_trader` package.
/// Logging can be configured to filter components and write up to a specific level only
/// by passing a configuration using the `NAUTILUS_LOG` environment variable.
///
/// # Safety
///
/// Should only be called once during an applications run, ideally at the
/// beginning of the run.
#[pyfunction]
#[pyo3(name = "init_logging")]
pub fn py_init_logging(
trader_id: TraderId,
instance_id: UUID4,
config_spec: String,
directory: Option<String>,
file_name: Option<String>,
file_format: Option<String>,
) {
logging::init_logging(
trader_id,
instance_id,
config_spec,
directory,
file_name,
file_format,
);
}

/// Create a new log event.
#[pyfunction]
#[pyo3(name = "logger_log")]
pub fn py_logger_log(
timestamp_ns: UnixNanos,
level: LogLevel,
color: LogColor,
component: String,
message: String,
) {
logging::log(
timestamp_ns,
level,
color,
Ustr::from(&component),
Cow::from(message),
);
}

#[pymethods]
impl FileWriterConfig {
#[new]
pub fn py_new(
directory: Option<String>,
file_name: Option<String>,
file_format: Option<String>,
) -> Self {
Self::new(directory, file_name, file_format)
}
}

#[pymethods]
impl LoggerConfig {
#[staticmethod]
#[pyo3(name = "from_spec")]
pub fn py_from_spec(spec: String) -> Self {
LoggerConfig::from_spec(&spec)
}
}
4 changes: 4 additions & 0 deletions nautilus_core/common/src/python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------

pub mod logging;
pub mod timer;

use pyo3::prelude::*;
Expand All @@ -32,6 +33,9 @@ pub fn common(_: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<enums::LogFormat>()?;
m.add_class::<LoggerConfig>()?;
m.add_class::<FileWriterConfig>()?;
m.add_function(wrap_pyfunction!(logging::py_init_tracing, m)?)?;
m.add_function(wrap_pyfunction!(logging::py_init_logging, m)?)?;
m.add_function(wrap_pyfunction!(logging::py_logger_log, m)?)?;

Ok(())
}
10 changes: 9 additions & 1 deletion nautilus_trader/common/logging.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ cpdef void init_logging(
TraderId trader_id,
UUID4 instance_id,
str config_spec,
str file_directory,
str directory,
str file_name,
str file_format,
)
Expand Down Expand Up @@ -69,6 +69,14 @@ cdef class Logger:
str message,
)

# cdef void log(
# self,
# level,
# color,
# str component,
# str message,
# )

cpdef void change_clock(self, Clock clock)
cpdef void flush(self)

Expand Down
Loading

0 comments on commit 37828d9

Please sign in to comment.