Support Logger using a second config for logging

There are use cases when rewriting a log file where you want to
add channels to the event_loop_->configuration(), ie remap things to
/original, and then log with the config without /original.  To support
this, logger needs to be able to accept a configuration to log, and to
use to list all the channels.

Change-Id: I0a74610f4a30ccc7f83dd806f5be8543a9ad2114
diff --git a/aos/events/logging/logger.cc b/aos/events/logging/logger.cc
index 85809b1..2109206 100644
--- a/aos/events/logging/logger.cc
+++ b/aos/events/logging/logger.cc
@@ -88,14 +88,26 @@
 
 Logger::Logger(std::string_view base_name, EventLoop *event_loop,
                std::chrono::milliseconds polling_period)
+    : Logger(base_name, event_loop, event_loop->configuration(),
+             polling_period) {}
+Logger::Logger(std::string_view base_name, EventLoop *event_loop,
+               const Configuration *configuration,
+               std::chrono::milliseconds polling_period)
     : Logger(std::make_unique<LocalLogNamer>(base_name, event_loop->node()),
-             event_loop, polling_period) {}
+             event_loop, configuration, polling_period) {}
+Logger::Logger(std::unique_ptr<LogNamer> log_namer, EventLoop *event_loop,
+               std::chrono::milliseconds polling_period)
+    : Logger(std::move(log_namer), event_loop, event_loop->configuration(),
+             polling_period) {}
 
 Logger::Logger(std::unique_ptr<LogNamer> log_namer, EventLoop *event_loop,
+               const Configuration *configuration,
                std::chrono::milliseconds polling_period)
     : event_loop_(event_loop),
       uuid_(UUID::Random()),
       log_namer_(std::move(log_namer)),
+      configuration_(configuration),
+      name_(network::GetHostname()),
       timer_handler_(event_loop_->AddTimer([this]() { DoLogData(); })),
       polling_period_(polling_period),
       server_statistics_fetcher_(
@@ -108,14 +120,14 @@
 
   // Find all the nodes which are logging timestamps on our node.
   std::set<const Node *> timestamp_logger_nodes;
-  for (const Channel *channel : *event_loop_->configuration()->channels()) {
+  for (const Channel *channel : *configuration_->channels()) {
     if (!configuration::ChannelIsSendableOnNode(channel, event_loop_->node()) ||
         !channel->has_destination_nodes()) {
       continue;
     }
     for (const Connection *connection : *channel->destination_nodes()) {
       const Node *other_node = configuration::GetNode(
-          event_loop_->configuration(), connection->name()->string_view());
+          configuration_, connection->name()->string_view());
 
       if (configuration::ConnectionDeliveryTimeIsLoggedOnNode(
               connection, event_loop_->node())) {
@@ -132,7 +144,7 @@
   // for them.
   for (const Node *node : timestamp_logger_nodes) {
     const Channel *channel = configuration::GetChannel(
-        event_loop_->configuration(),
+        configuration_,
         absl::StrCat("/aos/remote_timestamps/", node->name()->string_view()),
         logger::MessageHeader::GetFullyQualifiedName(), event_loop_->name(),
         event_loop_->node());
@@ -146,9 +158,16 @@
   }
 
   const size_t our_node_index = configuration::GetNodeIndex(
-      event_loop_->configuration(), event_loop_->node());
+      configuration_, event_loop_->node());
 
-  for (const Channel *channel : *event_loop_->configuration()->channels()) {
+  for (const Channel *config_channel : *configuration_->channels()) {
+    // The MakeRawFetcher method needs a channel which is in the event loop
+    // configuration() object, not the configuration_ object.  Go look that up
+    // from the config.
+    const Channel *channel = aos::configuration::GetChannel(
+        event_loop_->configuration(), config_channel->name()->string_view(),
+        config_channel->type()->string_view(), "", event_loop_->node());
+
     FetcherStruct fs;
     fs.node_index = our_node_index;
     const bool is_local =
@@ -195,8 +214,8 @@
                 << configuration::CleanedChannelToString(channel);
         fs.contents_writer =
             log_namer_->MakeForwardedTimestampWriter(channel, timestamp_node);
-        fs.node_index = configuration::GetNodeIndex(
-            event_loop_->configuration(), timestamp_node);
+        fs.node_index =
+            configuration::GetNodeIndex(configuration_, timestamp_node);
       }
       fs.channel_index = channel_index;
       fs.written = false;
@@ -205,13 +224,13 @@
     ++channel_index;
   }
 
-  node_state_.resize(configuration::MultiNode(event_loop_->configuration())
-                         ? event_loop_->configuration()->nodes()->size()
+  node_state_.resize(configuration::MultiNode(configuration_)
+                         ? configuration_->nodes()->size()
                          : 1u);
 
   for (const Node *node : log_namer_->nodes()) {
     const int node_index =
-        configuration::GetNodeIndex(event_loop_->configuration(), node);
+        configuration::GetNodeIndex(configuration_, node);
 
     node_state_[node_index].log_file_header = MakeHeader(node);
   }
@@ -222,6 +241,14 @@
   event_loop_->OnRun([this]() { StartLogging(); });
 }
 
+Logger::~Logger() {
+  // If we are replaying a log file, or in simulation, we want to force the last
+  // bit of data to be logged.  The easiest way to deal with this is to poll
+  // everything as we go to destroy the class, ie, shut down the logger, and
+  // write it to disk.
+  DoLogData();
+}
+
 void Logger::StartLogging() {
   // Grab data from each channel right before we declare the log file started
   // so we can capture the latest message on each channel.  This lets us have
@@ -245,7 +272,7 @@
 }
 
 void Logger::WriteHeader() {
-  if (configuration::MultiNode(event_loop_->configuration())) {
+  if (configuration::MultiNode(configuration_)) {
     server_statistics_fetcher_.Fetch();
   }
 
@@ -262,7 +289,7 @@
 
   for (const Node *node : log_namer_->nodes()) {
     const int node_index =
-        configuration::GetNodeIndex(event_loop_->configuration(), node);
+        configuration::GetNodeIndex(configuration_, node);
     MaybeUpdateTimestamp(node, node_index, monotonic_start_time,
                          realtime_start_time);
     log_namer_->WriteHeader(&node_state_[node_index].log_file_header, node);
@@ -270,7 +297,7 @@
 }
 
 void Logger::WriteMissingTimestamps() {
-  if (configuration::MultiNode(event_loop_->configuration())) {
+  if (configuration::MultiNode(configuration_)) {
     server_statistics_fetcher_.Fetch();
   } else {
     return;
@@ -282,7 +309,7 @@
 
   for (const Node *node : log_namer_->nodes()) {
     const int node_index =
-        configuration::GetNodeIndex(event_loop_->configuration(), node);
+        configuration::GetNodeIndex(configuration_, node);
     if (MaybeUpdateTimestamp(
             node, node_index,
             server_statistics_fetcher_.context().monotonic_event_time,
@@ -324,7 +351,7 @@
       monotonic_clock::min_time) {
     return false;
   }
-  if (configuration::MultiNode(event_loop_->configuration())) {
+  if (configuration::MultiNode(configuration_)) {
     if (event_loop_->node() == node) {
       // There are no offsets to compute for ourself, so always succeed.
       SetStartTime(node_index, monotonic_start_time, realtime_start_time);
@@ -378,10 +405,10 @@
   // TODO(austin): Compress this much more efficiently.  There are a bunch of
   // duplicated schemas.
   flatbuffers::Offset<aos::Configuration> configuration_offset =
-      CopyFlatBuffer(event_loop_->configuration(), &fbb);
+      CopyFlatBuffer(configuration_, &fbb);
 
   flatbuffers::Offset<flatbuffers::String> name_offset =
-      fbb.CreateString(network::GetHostname());
+      fbb.CreateString(name_);
 
   flatbuffers::Offset<flatbuffers::String> logger_uuid_offset =
       fbb.CreateString(uuid_.string_view());
@@ -391,7 +418,7 @@
 
   flatbuffers::Offset<Node> node_offset;
 
-  if (configuration::MultiNode(event_loop_->configuration())) {
+  if (configuration::MultiNode(configuration_)) {
     node_offset = CopyFlatBuffer(node, &fbb);
   }
 
@@ -437,7 +464,7 @@
 void Logger::Rotate() {
   for (const Node *node : log_namer_->nodes()) {
     const int node_index =
-        configuration::GetNodeIndex(event_loop_->configuration(), node);
+        configuration::GetNodeIndex(configuration_, node);
     log_namer_->Rotate(node, &node_state_[node_index].log_file_header);
   }
 }
diff --git a/aos/events/logging/logger.h b/aos/events/logging/logger.h
index d4ed786..3756f83 100644
--- a/aos/events/logging/logger.h
+++ b/aos/events/logging/logger.h
@@ -285,12 +285,33 @@
 // configuration that is sent rately on a channel and would affect execution.
 class Logger {
  public:
+  // Constructs a logger.
+  //   base_name/log_namer: Object used to write data to disk in one or more log
+  //     files.  If a base_name is passed in, a LocalLogNamer is wrapped
+  //     around it.
+  //   event_loop: The event loop used to read the messages.
+  //   polling_period: The period used to poll the data.
+  //   configuration: When provided, this is the configuration to log, and the
+  //     configuration to use for the channel list to log.  If not provided,
+  //     this becomes the configuration from the event loop.
   Logger(std::string_view base_name, EventLoop *event_loop,
          std::chrono::milliseconds polling_period =
              std::chrono::milliseconds(100));
+  Logger(std::string_view base_name, EventLoop *event_loop,
+         const Configuration *configuration,
+         std::chrono::milliseconds polling_period =
+             std::chrono::milliseconds(100));
   Logger(std::unique_ptr<LogNamer> log_namer, EventLoop *event_loop,
          std::chrono::milliseconds polling_period =
              std::chrono::milliseconds(100));
+  Logger(std::unique_ptr<LogNamer> log_namer, EventLoop *event_loop,
+         const Configuration *configuration,
+         std::chrono::milliseconds polling_period =
+             std::chrono::milliseconds(100));
+  ~Logger();
+
+  // Overrides the name in the log file header.
+  void set_name(std::string_view name) { name_ = name; }
 
   // Rotates the log file(s), triggering new part files to be written for each
   // log file.
@@ -319,6 +340,12 @@
   const UUID uuid_;
   std::unique_ptr<LogNamer> log_namer_;
 
+  // The configuration to place at the top of the log file.
+  const Configuration *configuration_;
+
+  // Name to save in the log file.  Defaults to hostname.
+  std::string name_;
+
   // Structure to track both a fetcher, and if the data fetched has been
   // written.  We may want to delay writing data to disk so that we don't let
   // data get too far out of order when written to disk so we can avoid making
@@ -450,7 +477,11 @@
   // gets called.
   void Deregister();
 
-  // Returns the configuration from the log file.
+  // Returns the configuration being used for replay from the log file.
+  // Note that this may be different from the configuration actually used for
+  // handling events. You should generally only use this to create a
+  // SimulatedEventLoopFactory, and then get the configuration from there for
+  // everything else.
   const Configuration *logged_configuration() const;
   // Returns the configuration being used for replay.
   // The pointer is invalidated whenever RemapLoggedChannel is called.
@@ -492,6 +523,10 @@
     return &log_file_header_.message();
   }
 
+  std::string_view name() const {
+    return log_file_header()->name()->string_view();
+  }
+
  private:
   const Channel *RemapChannel(const EventLoop *event_loop,
                               const Channel *channel);