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?
+    });
 }