Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust: init logging only once #207

Merged
merged 2 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion everestrs/everestrs-build/jinja/module.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl Module {
{% endfor %}
) -> ::std::sync::Arc<Self> {
let runtime = ::everestrs::Runtime::new();
let connections = ::everestrs::get_module_connections();
let connections = runtime.get_module_connections();
let this = ::std::sync::Arc::new(Self {
on_ready,
{% for provide in provides %}
Expand Down
34 changes: 16 additions & 18 deletions everestrs/everestrs/src/everestrs_sys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ std::unique_ptr<Everest::Everest> create_everest_instance(const std::string& mod
rs->mqtt_external_prefix, rs->telemetry_prefix, rs->telemetry_enabled);
}

std::unique_ptr<Everest::Config> create_config_instance(std::shared_ptr<Everest::RuntimeSettings> rs) {
// FIXME (aw): where to initialize the logger?
Everest::Logging::init(rs->logging_config_file);
return std::make_unique<Everest::Config>(rs);
}

JsonBlob json2blob(const json& j) {
// I did not find a way to not copy the data at least once here.
const std::string dumped = j.dump();
Expand Down Expand Up @@ -83,7 +77,7 @@ inline ConfigField get_config_field(const std::string& _name, int _value) {
Module::Module(const std::string& module_id, const std::string& prefix, const std::string& config_file) :
module_id_(module_id),
rs_(std::make_shared<Everest::RuntimeSettings>(prefix, config_file)),
config_(create_config_instance(rs_)),
config_(std::make_unique<Everest::Config>(rs_)),
handle_(create_everest_instance(module_id, rs_, *config_)) {
}

Expand Down Expand Up @@ -161,11 +155,8 @@ rust::Vec<RsModuleConfig> get_module_configs(rust::Str module_id, rust::Str pref
return out;
}

rust::Vec<RsModuleConnections> get_module_connections(rust::Str module_id, rust::Str prefix, rust::Str config_file) {
const auto rs = std::make_shared<Everest::RuntimeSettings>(std::string(prefix), std::string(config_file));
Everest::Config config{rs};

const auto connections = config.get_main_config().at(std::string(module_id))["connections"];
rust::Vec<RsModuleConnections> Module::get_module_connections() const {
const auto connections = config_->get_main_config().at(std::string(module_id_))["connections"];

// Iterate over the connections block.
rust::Vec<RsModuleConnections> out;
Expand All @@ -176,18 +167,25 @@ rust::Vec<RsModuleConnections> get_module_connections(rust::Str module_id, rust:
return out;
}

int Module::get_log_level() const {
int init_logging(rust::Str module_id, rust::Str prefix, rust::Str config_file) {
using namespace boost::log;
using namespace Everest::Logging;

const std::string module_id_cpp{module_id};
const std::string prefix_cpp{prefix};
const std::string config_file_cpp{config_file};

// Init the CPP logger.
Everest::RuntimeSettings rs{prefix_cpp, config_file_cpp};
init(rs.logging_config_file, module_id_cpp);

// Below is something really ugly. Boost's log filter rules may actually be
// quite "complex" but the library does not expose any way to check the
// already installed filters. We therefore reopen the config and construct
// or own filter - and feed it with dummy values to determine its filtering
// behaviour (the lowest severity which is accepted by the filter)
std::filesystem::path logging_path{rs_->logging_config_file};
std::filesystem::path logging_path{rs.logging_config_file};
std::ifstream logging_config(logging_path.c_str());

using namespace boost::log;
using namespace Everest::Logging;

if (!logging_config.is_open()) {
return info;
}
Expand Down
5 changes: 3 additions & 2 deletions everestrs/everestrs/src/everestrs_sys.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Module {
JsonBlob call_command(rust::Str implementation_id, size_t index, rust::Str name, JsonBlob args) const;
void subscribe_variable(const Runtime& rt, rust::String implementation_id, size_t index, rust::String name) const;
void publish_variable(rust::Str implementation_id, rust::Str name, JsonBlob blob) const;
int get_log_level() const;
rust::Vec<RsModuleConnections> get_module_connections() const;

private:
const std::string module_id_;
Expand All @@ -39,5 +39,6 @@ class Module {
std::unique_ptr<Module> create_module(rust::Str module_name, rust::Str prefix, rust::Str conf);

rust::Vec<RsModuleConfig> get_module_configs(rust::Str module_name, rust::Str prefix, rust::Str conf);
rust::Vec<RsModuleConnections> get_module_connections(rust::Str module_name, rust::Str prefix, rust::Str conf);

int init_logging(rust::Str module_name, rust::Str prefix, rust::Str conf);
void log2cxx(int level, int line, rust::Str file, rust::Str message);
89 changes: 49 additions & 40 deletions everestrs/everestrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ use std::convert::TryFrom;
use std::path::PathBuf;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::Once;
use std::sync::RwLock;
use std::sync::Weak;
use thiserror::Error;

/// Prevent calling the init of loggers more than once.
static INIT_LOGGER_ONCE: Once = Once::new();

// Reexport everything so the clients can use it.
pub use serde;
pub use serde_json;
Expand Down Expand Up @@ -155,22 +159,18 @@ mod ffi {
name: String,
);

/// Returns the `connections` block defined in the `config.yaml` for
/// the current module.
fn get_module_connections(self: &Module) -> Vec<RsModuleConnections>;

/// Publishes the given `blob` under the `implementation_id` and `name`.
fn publish_variable(self: &Module, implementation_id: &str, name: &str, blob: JsonBlob);

/// Returns the severity for the cxx logger.
fn get_log_level(self: &Module) -> i32;

/// Returns the module config from cpp.
fn get_module_configs(module_id: &str, prefix: &str, conf: &str) -> Vec<RsModuleConfig>;

/// Returns the `connections` block defined in the `config.yaml` for
/// the current module.
fn get_module_connections(
module_id: &str,
prefix: &str,
conf: &str,
) -> Vec<RsModuleConnections>;
/// Call this once.
fn init_logging(module_id: &str, prefix: &str, conf: &str) -> i32;

/// Logging sink for the EVerest module.
fn log2cxx(level: i32, line: i32, file: &str, message: &str);
Expand All @@ -195,6 +195,7 @@ impl ffi::JsonBlob {
/// Very simple logger to use by the Rust modules.
mod logger {
use super::ffi;
use crate::INIT_LOGGER_ONCE;

pub(crate) struct Logger {
level: log::Level,
Expand Down Expand Up @@ -237,19 +238,21 @@ mod logger {
/// Init the logger for everest.
///
/// Don't do this on your own as we must also control some cxx code.
pub(crate) fn init_logger(module: &ffi::Module) {
let level = match module.get_log_level() {
0 => log::Level::Trace,
1 => log::Level::Debug,
2 => log::Level::Info,
3 => log::Level::Warn,
4 => log::Level::Error,
_ => log::Level::Info,
};
pub(crate) fn init_logger(module_name: &str, prefix: &str, conf: &str) {
INIT_LOGGER_ONCE.call_once(|| {
let level = match ffi::init_logging(module_name, prefix, conf) {
0 => log::Level::Trace,
1 => log::Level::Debug,
2 => log::Level::Info,
3 => log::Level::Warn,
4 => log::Level::Error,
_ => log::Level::Info,
};

let logger = Self { level };
log::set_boxed_logger(Box::new(logger)).unwrap();
log::set_max_level(level.to_level_filter());
let logger = Self { level };
log::set_boxed_logger(Box::new(logger)).unwrap();
log::set_max_level(level.to_level_filter());
});
}
}
}
Expand Down Expand Up @@ -395,13 +398,17 @@ impl Runtime {
// TODO(hrapp): This function could use some error handling.
pub fn new() -> Pin<Arc<Self>> {
let args: Args = argh::from_env();
let cpp_module = ffi::create_module(
logger::Logger::init_logger(
&args.module,
&args.prefix.to_string_lossy(),
&args.conf.to_string_lossy(),
);

logger::Logger::init_logger(&cpp_module);
let cpp_module = ffi::create_module(
&args.module,
&args.prefix.to_string_lossy(),
&args.conf.to_string_lossy(),
);

Arc::pin(Self {
cpp_module,
Expand All @@ -428,7 +435,7 @@ impl Runtime {
}
}

let connections = get_module_connections();
let connections = self.get_module_connections();

// Subscribe to all variables that might be of interest.
for (implementation_id, requires) in manifest.requires {
Expand All @@ -452,6 +459,15 @@ impl Runtime {
// again.
(self.cpp_module).as_ref().unwrap().signal_ready(self);
}

/// The interface for fetching the module connections though the C++ runtime.
pub fn get_module_connections(&self) -> HashMap<String, usize> {
let raw_connections = self.cpp_module.as_ref().unwrap().get_module_connections();
raw_connections
.into_iter()
.map(|connection| (connection.implementation_id, connection.slots))
.collect()
}
}

/// A store for our config values. The type is closely related to
Expand Down Expand Up @@ -505,8 +521,16 @@ impl TryFrom<&Config> for i64 {
}

/// Interface for fetching the configurations through the C++ runtime.
///
/// This is separetated from the module since the user might need the config
/// to create the [Runtime].
pub fn get_module_configs() -> HashMap<String, HashMap<String, Config>> {
let args: Args = argh::from_env();
logger::Logger::init_logger(
&args.module,
&args.prefix.to_string_lossy(),
&args.conf.to_string_lossy(),
);
let raw_config = ffi::get_module_configs(
&args.module,
&args.prefix.to_string_lossy(),
Expand Down Expand Up @@ -541,18 +565,3 @@ pub fn get_module_configs() -> HashMap<String, HashMap<String, Config>> {

out
}

/// The interface for fetching the module connections though the C++ runtime.
pub fn get_module_connections() -> HashMap<String, usize> {
let args: Args = argh::from_env();
let raw_connections = ffi::get_module_connections(
&args.module,
&args.prefix.to_string_lossy(),
&args.conf.to_string_lossy(),
);

raw_connections
.into_iter()
.map(|connection| (connection.implementation_id, connection.slots))
.collect()
}
Loading