Add logging for rust
Change-Id: I0537c47817f050935c4319ccb97ce4efed866f7a
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/BUILD b/aos/BUILD
index 42a108a..67e5b29 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -212,6 +212,7 @@
visibility = ["//visibility:public"],
deps = [
"@crate_index//:clap",
+ "@crate_index//:env_logger",
],
)
@@ -228,6 +229,7 @@
visibility = ["//visibility:public"],
deps = [
":init_rs",
+ "@crate_index//:env_logger",
],
)
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 75b3bb9..e82fdde 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -282,6 +282,7 @@
"//aos:aos_rs",
"@com_github_google_flatbuffers//rust",
"@crate_index//:futures",
+ "@crate_index//:log",
],
)
@@ -313,6 +314,7 @@
"//aos:aos_rs",
"@com_github_google_flatbuffers//rust",
"@crate_index//:futures",
+ "@crate_index//:log",
],
)
diff --git a/aos/events/ping.rs b/aos/events/ping.rs
index c58b21b..b285860 100644
--- a/aos/events/ping.rs
+++ b/aos/events/ping.rs
@@ -1,13 +1,12 @@
use aos::configuration;
use aos::events::shm_event_loop::ShmEventLoop;
-use aos::init::WithCppFlags;
+use aos::init::Init;
use clap::Parser;
use ping_lib::PingTask;
use std::path::Path;
/// Ping portion of a ping/pong system.
#[derive(Parser, Debug)]
-#[command(name = "ping")]
struct App {
/// Time to sleep between pings.
#[arg(long, default_value_t = 10000, value_name = "MICROS")]
@@ -15,8 +14,7 @@
}
fn main() {
- let app = App::parse_with_cpp_flags();
- aos::init::init();
+ let app = App::init();
let config = configuration::read_config_from(Path::new("pingpong_config.json")).unwrap();
let ping = PingTask::new();
ShmEventLoop::new(&config).run_with(|runtime| {
diff --git a/aos/events/ping_lib.rs b/aos/events/ping_lib.rs
index 9303a3b..ce4d844 100644
--- a/aos/events/ping_lib.rs
+++ b/aos/events/ping_lib.rs
@@ -40,6 +40,7 @@
let mut builder = ping_sender.make_builder();
let mut ping = ping::PingBuilder::new(builder.fbb());
let iter = self.counter.get();
+ log::trace!("Ping: {iter}");
ping.add_value(iter);
ping.add_send_time(event_loop.monotonic_now().into());
let ping = ping.finish();
@@ -55,11 +56,9 @@
on_run.borrow().await;
loop {
let pong = pong_watcher.next().await;
- assert_eq!(
- pong.message().unwrap().value(),
- self.counter.get(),
- "Missed a reply"
- );
+ let pong = pong.message().unwrap();
+ log::trace!("Got pong: {}", pong.value());
+ assert_eq!(pong.value(), self.counter.get(), "Missed a reply");
}
}
}
diff --git a/aos/events/pong.rs b/aos/events/pong.rs
index d20eac6..c2c316b 100644
--- a/aos/events/pong.rs
+++ b/aos/events/pong.rs
@@ -1,19 +1,12 @@
use aos::configuration;
use aos::events::shm_event_loop::ShmEventLoop;
-use aos::init::WithCppFlags;
-use clap::Parser;
+use aos::init::{DefaultApp, Init};
use std::path::Path;
use pong_lib::pong;
-/// Pong portion of a ping/pong system.
-#[derive(Parser, Debug)]
-#[command(name = "pong")]
-struct App {}
-
fn main() {
- let _app = App::parse_with_cpp_flags();
- aos::init::init();
+ let _ = DefaultApp::init();
let config = configuration::read_config_from(Path::new("pingpong_config.json")).unwrap();
ShmEventLoop::new(&config).run_with(|runtime| {
let task = pong(*runtime);
diff --git a/aos/events/pong_lib.rs b/aos/events/pong_lib.rs
index 6451b7b..8a0587a 100644
--- a/aos/events/pong_lib.rs
+++ b/aos/events/pong_lib.rs
@@ -17,10 +17,12 @@
on_run.borrow().await;
loop {
let ping = ping_watcher.next().await;
+ let ping = ping.message().unwrap();
+ log::info!("Got ping: {}", ping.value());
let mut builder = pong_sender.make_builder();
let mut pong = pong::PongBuilder::new(builder.fbb());
- pong.add_value(ping.message().unwrap().value());
+ pong.add_value(ping.value());
pong.add_initial_send_time(event_loop.monotonic_now().into());
let pong = pong.finish();
builder.send(pong).expect("Can't send pong reponse");
diff --git a/aos/init.rs b/aos/init.rs
index 331e74d..fe0408d 100644
--- a/aos/init.rs
+++ b/aos/init.rs
@@ -1,3 +1,44 @@
+//! AOS Initialization
+//!
+//! This module "links" the C++ library and the Rust application together.
+//! In particular it provides the [`Init`] trait which is implemented for
+//! any struct that implements [`clap::Parser`]. The reason for this is that
+//! an important part of initializing the C++ library involves setting the
+//! gFlags which get resolved dynamically thanks to their reflection API.
+//!
+//! # Examples
+//!
+//! ```no_run
+//! use aos_init::Init;
+//! use clap::Parser;
+//!
+//! #[derive(Parser, Debug)]
+//! struct App {
+//! /// Time to sleep between pings.
+//! #[arg(long, default_value_t = 10000, value_name = "MICROS")]
+//! sleep: u64,
+//! }
+//!
+//! fn main() {
+//! // Initializes AOS and returns the App struct with the parsed CLI flags
+//! let app: App = App::init();
+//! // At this point your flags are parsed and AOS is initialized.
+//! }
+//! ```
+//! You can also use [`DefaultApp`] to avoid having to specify your own CLI options if you don't
+//! need them. For example:
+//!
+//! ```no_run
+//! use aos_init::{DefaultApp, Init};
+//! use clap::Parser;
+//!
+//! fn main() {
+//! // Initializes AOS. DefaultApp doesn't have any flags to parse.
+//! let _ = DefaultApp::init();
+//! // At this point AOS is initialized and you can create event loop.
+//! }
+//!```
+
use std::{
env,
ffi::{CString, OsStr, OsString},
@@ -22,32 +63,61 @@
generate!("aos::GetCommandLineOption")
);
-/// Initializes AOS.
-pub fn init() {
- static ONCE: Once = Once::new();
- ONCE.call_once(|| {
- // We leak the `CString` with `into_raw`. It's not sound for C++ to free
- // it but `InitGoogleLogging` requries that it is long-lived.
- let argv0 = std::env::args()
- .map(|arg| CString::new(arg).expect("Arg may not have NUL"))
- .next()
- .expect("Missing argv[0]?")
- .into_raw();
- // SAFETY: argv0 is a well-defined CString.
- unsafe {
- ffi::aos::InitFromRust(argv0);
- }
- });
+// Intended to be used only from here and test_init. Don't use it anywhere else please.
+#[doc(hidden)]
+pub mod internal {
+ use super::*;
+ /// Generic initialization for production and tests.
+ ///
+ /// Sets up the C++ side of things. Notably, it doesn't setup the command line flags.
+ pub fn init() {
+ static ONCE: Once = Once::new();
+ ONCE.call_once(|| {
+ // We leak the `CString` with `into_raw`. It's not sound for C++ to free
+ // it but `InitGoogleLogging` requries that it is long-lived.
+ let argv0 = std::env::args()
+ .map(|arg| CString::new(arg).expect("Arg may not have NUL"))
+ .next()
+ .expect("Missing argv[0]?")
+ .into_raw();
+ // SAFETY: argv0 is a well-defined CString.
+ unsafe {
+ ffi::aos::InitFromRust(argv0);
+ }
+ });
+ }
}
+/// An application that doesn't need custom command line flags.
+///
+/// If you need your own command line flags, use any struct that derives [`clap::Parser`] instead.
+#[derive(Parser, Debug)]
+pub struct DefaultApp {}
+
/// Trait used to append C++ gFlags to a clap CLI.
-pub trait WithCppFlags: Parser {
+pub trait Init: Parser {
+ /// Initializes an AOS application.
+ ///
+ /// Parses the command line flags and runs the initialization logic.
+ fn init() -> Self {
+ let this = Self::parse_with_cpp_flags();
+ // Rust logs to stderr by default. Make that true for C++ as that will be easier than
+ // managing one or multiple files across FFI. We can pipe the stderr to a file to get
+ // a log file if we want.
+ CxxFlag::set_option("logtostderr", "true".as_ref())
+ .expect("Error setting C++ flag: logtostderr");
+ internal::init();
+ // Non-test initialization below
+ env_logger::init();
+ this
+ }
+
/// Parses the comannd line arguments while also setting the C++ gFlags.
fn parse_with_cpp_flags() -> Self {
Self::parse_with_cpp_flags_from(env::args_os())
}
- /// Like [`WithCppFlags::parse_with_cpp_flags`] but read from an iterator.
+ /// Like [`Init::parse_with_cpp_flags`] but read from an iterator.
fn parse_with_cpp_flags_from<I, T>(itr: I) -> Self
where
I: IntoIterator<Item = T>,
@@ -100,7 +170,7 @@
}
}
-impl<T: Parser> WithCppFlags for T {}
+impl<T: Parser> Init for T {}
#[derive(Clone)]
#[allow(unused)]
@@ -112,13 +182,19 @@
filename: String,
}
+#[derive(Debug)]
struct SetFlagError;
impl CxxFlag {
/// Sets the command gFlag to the specified value.
fn set(&self, value: &OsStr) -> Result<(), SetFlagError> {
+ Self::set_option(&self.name, value)
+ }
+
+ /// Sets the command gFlag to the specified value.
+ fn set_option(name: &str, value: &OsStr) -> Result<(), SetFlagError> {
unsafe {
- let name = CString::new(self.name.clone()).expect("Flag name may not have NUL");
+ let name = CString::new(name.clone()).expect("Flag name may not have NUL");
let value = CString::new(value.as_bytes()).expect("Arg may not have NUL");
if ffi::aos::SetCommandLineOption(name.as_ptr(), value.as_ptr()) {
Ok(())
diff --git a/aos/test_init.rs b/aos/test_init.rs
index a8380e2..d17d2ef 100644
--- a/aos/test_init.rs
+++ b/aos/test_init.rs
@@ -1,4 +1,4 @@
-use aos_init::init;
+use std::sync::Once;
autocxx::include_cpp! (
#include "aos/testing/tmpdir.h"
@@ -15,7 +15,11 @@
///
/// Panics if non-test initialization has already been performed.
pub fn test_init() {
- init();
- ffi::aos::testing::SetTestShmBase();
- // TODO(Brian): Do we want any of the other stuff that `:gtest_main` has?
+ static ONCE: Once = Once::new();
+ ONCE.call_once(|| {
+ aos_init::internal::init();
+ ffi::aos::testing::SetTestShmBase();
+ env_logger::builder().is_test(true).init();
+ // TODO(Brian): Do we want any of the other stuff that `:gtest_main` has?
+ });
}