Expose file size from log file reader classes

Change-Id: Ib826c4405ba78e9e1ab37dfd1893d67fc08a7522
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 4e58923..fa2f8ae 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -88,7 +88,7 @@
     srcs = ["s3_file_operations.cc"],
     hdrs = ["s3_file_operations.h"],
     deps = [
-        ":file_operations",
+        ":log_backend",
         ":s3_fetcher",
     ],
 )
diff --git a/aos/events/logging/file_operations.cc b/aos/events/logging/file_operations.cc
index 75910e5..04e695e 100644
--- a/aos/events/logging/file_operations.cc
+++ b/aos/events/logging/file_operations.cc
@@ -11,13 +11,16 @@
          absl::EndsWith(filename, ".bfbs.sz");
 }
 
-void LocalFileOperations::FindLogs(std::vector<std::string> *files) {
-  auto MaybeAddFile = [&files](std::string_view filename) {
+void LocalFileOperations::FindLogs(std::vector<File> *files) {
+  auto MaybeAddFile = [&files](std::string_view filename, size_t size) {
     if (!IsValidFilename(filename)) {
       VLOG(1) << "Ignoring " << filename << " with invalid extension.";
     } else {
       VLOG(1) << "Found log " << filename;
-      files->emplace_back(filename);
+      files->emplace_back(File{
+          .name = std::string(filename),
+          .size = size,
+      });
     }
   };
   if (std::filesystem::is_directory(filename_)) {
@@ -28,10 +31,10 @@
         VLOG(1) << file << " is not file.";
         continue;
       }
-      MaybeAddFile(file.path().string());
+      MaybeAddFile(file.path().string(), file.file_size());
     }
   } else {
-    MaybeAddFile(filename_);
+    MaybeAddFile(filename_, std::filesystem::file_size(filename_));
   }
 }
 
diff --git a/aos/events/logging/file_operations.h b/aos/events/logging/file_operations.h
index faf63cb..538ae60 100644
--- a/aos/events/logging/file_operations.h
+++ b/aos/events/logging/file_operations.h
@@ -14,10 +14,15 @@
 // associated with either a single file or directory that contains log files.
 class FileOperations {
  public:
+  struct File {
+    std::string name;
+    size_t size;
+  };
+
   virtual ~FileOperations() = default;
 
   virtual bool Exists() = 0;
-  virtual void FindLogs(std::vector<std::string> *files) = 0;
+  virtual void FindLogs(std::vector<File> *files) = 0;
 };
 
 // Implements FileOperations with standard POSIX filesystem APIs. These work on
@@ -29,7 +34,7 @@
 
   bool Exists() override { return std::filesystem::exists(filename_); }
 
-  void FindLogs(std::vector<std::string> *files) override;
+  void FindLogs(std::vector<File> *files) override;
 
  private:
   std::string filename_;
diff --git a/aos/events/logging/log_backend.cc b/aos/events/logging/log_backend.cc
index 6cd9a7d..f8a6846 100644
--- a/aos/events/logging/log_backend.cc
+++ b/aos/events/logging/log_backend.cc
@@ -334,32 +334,35 @@
       base_name_(base_name),
       separator_(base_name_.back() == '/' ? "" : "_") {}
 
-std::unique_ptr<LogSink> FileBackend::RequestFile(std::string_view id) {
+std::unique_ptr<LogSink> FileBackend::RequestFile(const std::string_view id) {
   const std::string filename = absl::StrCat(base_name_, separator_, id);
   return std::make_unique<FileHandler>(filename, supports_odirect_);
 }
 
-std::vector<std::string> FileBackend::ListFiles() const {
+std::vector<FileBackend::File> FileBackend::ListFiles() const {
   std::filesystem::path directory(base_name_);
   if (!is_directory(directory)) {
     directory = directory.parent_path();
   }
   internal::LocalFileOperations operations(directory.string());
-  std::vector<std::string> files;
+  std::vector<internal::FileOperations::File> files;
   operations.FindLogs(&files);
 
-  std::vector<std::string> names;
+  std::vector<File> names;
   const std::string prefix = absl::StrCat(base_name_, separator_);
   for (const auto &file : files) {
-    CHECK(absl::StartsWith(file, prefix))
-        << ": File " << file << ", prefix " << prefix;
-    names.push_back(file.substr(prefix.size()));
+    CHECK(absl::StartsWith(file.name, prefix))
+        << ": File " << file.name << ", prefix " << prefix;
+    names.emplace_back(File{
+        .name = file.name.substr(prefix.size()),
+        .size = file.size,
+    });
   }
   return names;
 }
 
 std::unique_ptr<DataDecoder> FileBackend::GetDecoder(
-    std::string_view id) const {
+    const std::string_view id) const {
   const std::string filename = absl::StrCat(base_name_, separator_, id);
   CHECK(std::filesystem::exists(filename));
   return std::make_unique<DummyDecoder>(filename);
@@ -372,7 +375,7 @@
       separator_(base_name_.back() == '/' ? "" : "_") {}
 
 std::unique_ptr<LogSink> RenamableFileBackend::RequestFile(
-    std::string_view id) {
+    const std::string_view id) {
   const std::string filename =
       absl::StrCat(base_name_, separator_, id, temp_suffix_);
   return std::make_unique<RenamableFileHandler>(this, filename,
diff --git a/aos/events/logging/log_backend.h b/aos/events/logging/log_backend.h
index e75b632..3a47f42 100644
--- a/aos/events/logging/log_backend.h
+++ b/aos/events/logging/log_backend.h
@@ -238,6 +238,27 @@
   int flags_ = 0;
 };
 
+// Interface to decouple reading of logs and media (file system, memory or S3).
+class LogSource {
+ public:
+  struct File {
+    std::string name;
+    size_t size;
+  };
+
+  virtual ~LogSource() = default;
+
+  // Provides a list of readable sources for log reading.
+  virtual std::vector<File> ListFiles() const = 0;
+
+  // Entry point for reading the content of log file.
+  virtual std::unique_ptr<DataDecoder> GetDecoder(
+      const std::string_view id) const = 0;
+  std::unique_ptr<DataDecoder> GetDecoder(const LogSource::File &id) const {
+    return GetDecoder(id.name);
+  }
+};
+
 // Interface to decouple log writing and media (file system or memory). It is
 // handy to use for tests.
 class LogBackend {
@@ -247,20 +268,11 @@
   // Request file-like object from the log backend. It maybe a file on a disk or
   // in memory. id is usually generated by log namer and looks like name of the
   // file within a log folder.
-  virtual std::unique_ptr<LogSink> RequestFile(std::string_view id) = 0;
-};
+  virtual std::unique_ptr<LogSink> RequestFile(const std::string_view id) = 0;
 
-// Interface to decouple reading of logs and media (file system, memory or S3).
-class LogSource {
- public:
-  virtual ~LogSource() = default;
-
-  // Provides a list of readable sources for log reading.
-  virtual std::vector<std::string> ListFiles() const = 0;
-
-  // Entry point for reading the content of log file.
-  virtual std::unique_ptr<DataDecoder> GetDecoder(
-      std::string_view id) const = 0;
+  std::unique_ptr<LogSink> RequestFile(const LogSource::File &id) {
+    return RequestFile(id.name);
+  }
 };
 
 // Implements requests log files from file system.
@@ -271,13 +283,14 @@
   ~FileBackend() override = default;
 
   // Request file from a file system. It is not open yet.
-  std::unique_ptr<LogSink> RequestFile(std::string_view id) override;
+  std::unique_ptr<LogSink> RequestFile(const std::string_view id) override;
 
   // List all files that looks like log files under base_name.
-  std::vector<std::string> ListFiles() const override;
+  std::vector<File> ListFiles() const override;
 
   // Open decoder to read the content of the file.
-  std::unique_ptr<DataDecoder> GetDecoder(std::string_view id) const override;
+  std::unique_ptr<DataDecoder> GetDecoder(
+      const std::string_view id) const override;
 
  private:
   const bool supports_odirect_;
@@ -309,7 +322,7 @@
   ~RenamableFileBackend() = default;
 
   // Request file from a file system. It is not open yet.
-  std::unique_ptr<LogSink> RequestFile(std::string_view id) override;
+  std::unique_ptr<LogSink> RequestFile(const std::string_view id) override;
 
   // TODO (Alexei): it is called by Logger, and left here for compatibility.
   // Logger should not call it.
diff --git a/aos/events/logging/log_backend_test.cc b/aos/events/logging/log_backend_test.cc
index 08b6f1e..d3c83cc 100644
--- a/aos/events/logging/log_backend_test.cc
+++ b/aos/events/logging/log_backend_test.cc
@@ -25,6 +25,8 @@
 }
 }  // namespace
 
+MATCHER_P(FileEq, o, "") { return arg.name == o.name && arg.size == o.size; }
+
 TEST(LogBackendTest, CreateSimpleFile) {
   const std::string logevent = aos::testing::TestTmpDir() + "/logevent/";
   const std::string filename = "test.bfbs";
@@ -37,7 +39,11 @@
   EXPECT_EQ(file->Close(), WriteCode::kOk);
   EXPECT_TRUE(std::filesystem::exists(logevent + filename));
 
-  EXPECT_THAT(backend.ListFiles(), ::testing::ElementsAre(filename));
+  EXPECT_THAT(backend.ListFiles(),
+              ::testing::ElementsAre(FileEq(LogSource::File{
+                  .name = filename,
+                  .size = 4,
+              })));
 
   auto decoder = backend.GetDecoder(filename);
   std::vector<uint8_t> buffer;
diff --git a/aos/events/logging/log_reader_utils_test.cc b/aos/events/logging/log_reader_utils_test.cc
index 911c4eb..b61c9de 100644
--- a/aos/events/logging/log_reader_utils_test.cc
+++ b/aos/events/logging/log_reader_utils_test.cc
@@ -134,10 +134,10 @@
   util::WriteStringToFileOrDie(log_file, "test");
   internal::LocalFileOperations file_op(log_file);
   EXPECT_TRUE(file_op.Exists());
-  std::vector<std::string> logs;
+  std::vector<internal::LocalFileOperations::File> logs;
   file_op.FindLogs(&logs);
   ASSERT_EQ(logs.size(), 1);
-  EXPECT_EQ(logs.front(), log_file);
+  EXPECT_EQ(logs.front().name, log_file);
 }
 
 // Verify that it is OK to list folder with log file.
@@ -148,10 +148,10 @@
   util::WriteStringToFileOrDie(log_file, "test");
   internal::LocalFileOperations file_op(log_folder);
   EXPECT_TRUE(file_op.Exists());
-  std::vector<std::string> logs;
+  std::vector<internal::LocalFileOperations::File> logs;
   file_op.FindLogs(&logs);
   ASSERT_EQ(logs.size(), 1);
-  EXPECT_EQ(logs.front(), log_file);
+  EXPECT_EQ(logs.front().name, log_file);
 }
 
 }  // namespace aos::logger::testing
diff --git a/aos/events/logging/log_stats.cc b/aos/events/logging/log_stats.cc
index e6913c1..bbed18f 100644
--- a/aos/events/logging/log_stats.cc
+++ b/aos/events/logging/log_stats.cc
@@ -287,16 +287,8 @@
     LOG(FATAL) << "Expected at least 1 logfile as an argument.";
   }
 
-  // find logfiles
-  std::vector<std::string> unsorted_logfiles =
-      aos::logger::FindLogs(argc, argv);
-
-  // sort logfiles
-  const std::vector<aos::logger::LogFile> logfiles =
-      aos::logger::SortParts(unsorted_logfiles);
-
-  // open logfiles
-  aos::logger::LogReader reader(logfiles);
+  aos::logger::LogReader reader(
+      aos::logger::SortParts(aos::logger::FindLogs(argc, argv)));
 
   LogfileStats logfile_stats;
   std::vector<ChannelStats> channel_stats;
diff --git a/aos/events/logging/logfile_sorting.cc b/aos/events/logging/logfile_sorting.cc
index a343356..59d18d0 100644
--- a/aos/events/logging/logfile_sorting.cc
+++ b/aos/events/logging/logfile_sorting.cc
@@ -110,18 +110,19 @@
 
 }  // namespace
 
-void FindLogs(std::vector<std::string> *files, std::string filename) {
+void FindLogs(std::vector<internal::FileOperations::File> *files,
+              std::string filename) {
   MakeFileOperations(filename)->FindLogs(files);
 }
 
-std::vector<std::string> FindLogs(std::string filename) {
-  std::vector<std::string> files;
+std::vector<internal::FileOperations::File> FindLogs(std::string filename) {
+  std::vector<internal::FileOperations::File> files;
   FindLogs(&files, filename);
   return files;
 }
 
-std::vector<std::string> FindLogs(int argc, char **argv) {
-  std::vector<std::string> found_logfiles;
+std::vector<internal::FileOperations::File> FindLogs(int argc, char **argv) {
+  std::vector<internal::FileOperations::File> found_logfiles;
 
   for (int i = 1; i < argc; i++) {
     std::string filename = argv[i];
@@ -320,8 +321,9 @@
   std::vector<UnsortedOldParts> old_parts;
 
   // Populates the class's datastructures from the input file list.
-  void PopulateFromFiles(ReadersPool *readers,
-                         const std::vector<std::string> &parts);
+  void PopulateFromFiles(
+      ReadersPool *readers,
+      const std::vector<internal::FileOperations::File> &parts);
 
   // Wrangles everything into a map of boot uuids -> boot counts.
   MapBoots ComputeBootCounts();
@@ -342,18 +344,19 @@
   std::vector<LogFile> SortParts();
 };
 
-void PartsSorter::PopulateFromFiles(ReadersPool *readers,
-                                    const std::vector<std::string> &parts) {
+void PartsSorter::PopulateFromFiles(
+    ReadersPool *readers,
+    const std::vector<internal::FileOperations::File> &parts) {
   // Now extract everything into our datastructures above for sorting.
-  for (const std::string &part : parts) {
-    SpanReader *reader = readers->BorrowReader(part);
+  for (const internal::FileOperations::File &part : parts) {
+    SpanReader *reader = readers->BorrowReader(part.name);
     std::optional<SizePrefixedFlatbufferVector<LogFileHeader>> log_header =
         ReadHeader(reader);
     if (!log_header) {
       if (!FLAGS_quiet_sorting) {
-        LOG(WARNING) << "Skipping " << part << " without a header";
+        LOG(WARNING) << "Skipping " << part.name << " without a header";
       }
-      corrupted.emplace_back(part);
+      corrupted.emplace_back(part.name);
       continue;
     }
 
@@ -433,11 +436,12 @@
       continue;
     }
 
-    VLOG(1) << "Header " << FlatbufferToJson(log_header.value()) << " " << part;
+    VLOG(1) << "Header " << FlatbufferToJson(log_header.value()) << " "
+            << part.name;
 
     if (configuration_sha256.empty()) {
       CHECK(log_header->message().has_configuration())
-          << ": Failed to find header on " << part;
+          << ": Failed to find header on " << part.name;
       // If we don't have a configuration_sha256, we need to have a
       // configuration directly inside the header.  This ends up being a bit
       // unwieldy to deal with, so let's instead copy the configuration, hash
@@ -464,7 +468,7 @@
       configuration_sha256 = std::move(config_copy_sha256);
     } else {
       CHECK(!log_header->message().has_configuration())
-          << ": Found header where one shouldn't be on " << part;
+          << ": Found header where one shouldn't be on " << part.name;
       config_sha256_list.emplace(configuration_sha256);
     }
 
@@ -475,12 +479,12 @@
         !log_header->message().has_parts_index() &&
         !log_header->message().has_node()) {
       std::optional<SizePrefixedFlatbufferVector<MessageHeader>> first_message =
-          ReadNthMessage(part, 0);
+          ReadNthMessage(part.name, 0);
       if (!first_message) {
         if (!FLAGS_quiet_sorting) {
-          LOG(WARNING) << "Skipping " << part << " without any messages";
+          LOG(WARNING) << "Skipping " << part.name << " without any messages";
         }
-        corrupted.emplace_back(part);
+        corrupted.emplace_back(part.name);
         continue;
       }
       const monotonic_clock::time_point first_message_time(
@@ -500,11 +504,11 @@
         old_parts.back().parts.realtime_start_time = realtime_start_time;
         old_parts.back().parts.config_sha256 = configuration_sha256;
         old_parts.back().unsorted_parts.emplace_back(
-            std::make_pair(first_message_time, part));
+            std::make_pair(first_message_time, part.name));
         old_parts.back().name = name;
       } else {
         result->unsorted_parts.emplace_back(
-            std::make_pair(first_message_time, part));
+            std::make_pair(first_message_time, part.name));
         CHECK_EQ(result->name, name);
         CHECK_EQ(result->parts.config_sha256, configuration_sha256);
       }
@@ -890,7 +894,7 @@
       CHECK_EQ(it->second.realtime_start_time, realtime_start_time);
     }
 
-    it->second.parts.emplace_back(std::make_pair(part, parts_index));
+    it->second.parts.emplace_back(std::make_pair(part.name, parts_index));
   }
 }
 
@@ -1985,16 +1989,39 @@
 }
 
 std::vector<LogFile> SortParts(const std::vector<std::string> &parts) {
-  LogReadersPool readers;
-  PartsSorter sorter;
-  sorter.PopulateFromFiles(&readers, parts);
-  return sorter.SortParts();
+  std::vector<internal::FileOperations::File> full_parts;
+  full_parts.reserve(parts.size());
+  for (const auto &p : parts) {
+    full_parts.emplace_back(internal::FileOperations::File{
+        .name = p,
+        .size = 0u,
+    });
+  }
+  return SortParts(full_parts);
 }
 
 std::vector<LogFile> SortParts(const LogSource &log_source) {
   LogReadersPool readers(&log_source);
   PartsSorter sorter;
-  sorter.PopulateFromFiles(&readers, log_source.ListFiles());
+  std::vector<LogSource::File> files = log_source.ListFiles();
+  std::vector<internal::FileOperations::File> arg;
+  arg.reserve(files.size());
+  for (LogSource::File &f : files) {
+    arg.emplace_back(internal::FileOperations::File{
+        .name = std::move(f.name),
+        .size = f.size,
+    });
+  }
+  sorter.PopulateFromFiles(&readers, arg);
+  return sorter.SortParts();
+}
+
+std::vector<LogFile> SortParts(
+    const std::vector<internal::FileOperations::File> &files) {
+  LogReadersPool readers;
+  PartsSorter sorter;
+
+  sorter.PopulateFromFiles(&readers, files);
   return sorter.SortParts();
 }
 
diff --git a/aos/events/logging/logfile_sorting.h b/aos/events/logging/logfile_sorting.h
index 7461e89..b7f8297 100644
--- a/aos/events/logging/logfile_sorting.h
+++ b/aos/events/logging/logfile_sorting.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "aos/configuration.h"
+#include "aos/events/logging/file_operations.h"
 #include "aos/events/logging/log_backend.h"
 #include "aos/time/time.h"
 #include "aos/uuid.h"
@@ -136,20 +137,23 @@
 
 // Takes a bunch of parts and sorts them based on part_uuid and part_index.
 std::vector<LogFile> SortParts(const std::vector<std::string> &parts);
+std::vector<LogFile> SortParts(
+    const std::vector<internal::FileOperations::File> &parts);
 
 // Sort parts of a single log.
 std::vector<LogFile> SortParts(const LogSource &log_source);
 
 // Recursively searches the file/folder for .bfbs and .bfbs.xz files and adds
 // them to the vector.
-void FindLogs(std::vector<std::string> *files, std::string filename);
+void FindLogs(std::vector<internal::FileOperations::File> *files,
+              std::string filename);
 
 // Recursively searches the file/folder for .bfbs and .bfbs.xz files and returns
 // them in a vector.
-std::vector<std::string> FindLogs(std::string filename);
+std::vector<internal::FileOperations::File> FindLogs(std::string filename);
 
 // Recursively searches for logfiles in argv[1] and onward.
-std::vector<std::string> FindLogs(int argc, char **argv);
+std::vector<internal::FileOperations::File> FindLogs(int argc, char **argv);
 
 // Proxy container to bind log parts with log source. It helps with reading logs
 // from virtual media such as memory or S3.
diff --git a/aos/events/logging/s3_fetcher.cc b/aos/events/logging/s3_fetcher.cc
index f44a20b..4207286 100644
--- a/aos/events/logging/s3_fetcher.cc
+++ b/aos/events/logging/s3_fetcher.cc
@@ -179,22 +179,25 @@
   get_next_chunk_ = GetS3Client().GetObjectCallable(get_request);
 }
 
-std::vector<std::string> ListS3Objects(std::string_view url) {
+std::vector<std::pair<std::string, size_t>> ListS3Objects(
+    std::string_view url) {
   Aws::S3::Model::ListObjectsV2Request list_request;
   const ObjectName object_name = ParseUrl(url);
   list_request.SetBucket(object_name.bucket);
   list_request.SetPrefix(object_name.key);
   Aws::S3::Model::ListObjectsV2Outcome list_outcome =
       GetS3Client().ListObjectsV2(list_request);
-  std::vector<std::string> result;
+  std::vector<std::pair<std::string, size_t>> result;
   while (true) {
     CHECK(list_outcome.IsSuccess()) << ": Listing objects for " << url
                                     << " failed: " << list_outcome.GetError();
     auto &list_result = list_outcome.GetResult();
     for (const Aws::S3::Model::Object &object : list_result.GetContents()) {
-      result.push_back(absl::StrCat("s3://", list_outcome.GetResult().GetName(),
-                                    "/", object.GetKey()));
-      VLOG(2) << "got " << result.back();
+      result.emplace_back(
+          absl::StrCat("s3://", list_outcome.GetResult().GetName(), "/",
+                       object.GetKey()),
+          object.GetSize());
+      VLOG(2) << "got " << result.back().first;
     }
     if (!list_result.GetIsTruncated()) {
       break;
diff --git a/aos/events/logging/s3_fetcher.h b/aos/events/logging/s3_fetcher.h
index 41bc114..73584cb 100644
--- a/aos/events/logging/s3_fetcher.h
+++ b/aos/events/logging/s3_fetcher.h
@@ -54,8 +54,8 @@
 ObjectName ParseUrl(std::string_view url);
 
 // Does an S3 object listing with the given URL prefix. Returns the URLs for all
-// the objects under it.
-std::vector<std::string> ListS3Objects(std::string_view url);
+// the objects under it, and the size.
+std::vector<std::pair<std::string, size_t>> ListS3Objects(std::string_view url);
 
 }  // namespace aos::logger
 
diff --git a/aos/events/logging/s3_file_operations.cc b/aos/events/logging/s3_file_operations.cc
index 8764d4a..129b7e2 100644
--- a/aos/events/logging/s3_file_operations.cc
+++ b/aos/events/logging/s3_file_operations.cc
@@ -4,17 +4,30 @@
 
 namespace aos::logger::internal {
 
-S3FileOperations::S3FileOperations(std::string_view url)
-    : object_urls_(ListS3Objects(url)) {}
+std::vector<FileOperations::File> Convert(
+    std::vector<std::pair<std::string, size_t>> &&input) {
+  std::vector<FileOperations::File> result;
+  result.reserve(input.size());
+  for (std::pair<std::string, size_t> &i : input) {
+    result.emplace_back(FileOperations::File{
+        .name = std::move(i.first),
+        .size = i.second,
+    });
+  }
+  return result;
+}
 
-void S3FileOperations::FindLogs(std::vector<std::string> *files) {
+S3FileOperations::S3FileOperations(std::string_view url)
+    : object_urls_(Convert(ListS3Objects(url))) {}
+
+void S3FileOperations::FindLogs(std::vector<File> *files) {
   // We already have a recursive listing, so just grab all the objects from
   // there.
-  for (const std::string &object_url : object_urls_) {
-    if (IsValidFilename(object_url)) {
+  for (const File &object_url : object_urls_) {
+    if (IsValidFilename(object_url.name)) {
       files->push_back(object_url);
     }
   }
 }
 
-}  // namespace aos::logger::internal
\ No newline at end of file
+}  // namespace aos::logger::internal
diff --git a/aos/events/logging/s3_file_operations.h b/aos/events/logging/s3_file_operations.h
index ae94f62..e75e56b 100644
--- a/aos/events/logging/s3_file_operations.h
+++ b/aos/events/logging/s3_file_operations.h
@@ -11,10 +11,10 @@
 
   bool Exists() final { return !object_urls_.empty(); }
 
-  void FindLogs(std::vector<std::string> *files) final;
+  void FindLogs(std::vector<File> *files) final;
 
  private:
-  const std::vector<std::string> object_urls_;
+  const std::vector<File> object_urls_;
 };
 
 }  // namespace aos::logger::internal
diff --git a/aos/events/logging/single_node_merge.cc b/aos/events/logging/single_node_merge.cc
index afb88af..aee2c5e 100644
--- a/aos/events/logging/single_node_merge.cc
+++ b/aos/events/logging/single_node_merge.cc
@@ -20,8 +20,7 @@
 namespace chrono = std::chrono;
 
 int Main(int argc, char **argv) {
-  const std::vector<std::string> unsorted_logfiles = FindLogs(argc, argv);
-  const LogFilesContainer log_files(SortParts(unsorted_logfiles));
+  const LogFilesContainer log_files(SortParts(FindLogs(argc, argv)));
   const Configuration *config = log_files.config();
 
   // Haven't tested this on a single node log, and don't really see a need to
diff --git a/aos/events/logging/timestamp_extractor.cc b/aos/events/logging/timestamp_extractor.cc
index 72f9de8..0b22f93 100644
--- a/aos/events/logging/timestamp_extractor.cc
+++ b/aos/events/logging/timestamp_extractor.cc
@@ -18,8 +18,7 @@
 namespace chrono = std::chrono;
 
 int Main(int argc, char **argv) {
-  const std::vector<std::string> unsorted_logfiles = FindLogs(argc, argv);
-  const LogFilesContainer log_files(SortParts(unsorted_logfiles));
+  const LogFilesContainer log_files(SortParts(FindLogs(argc, argv)));
   const Configuration *config = log_files.config();
 
   CHECK(configuration::MultiNode(config))
diff --git a/aos/network/log_web_proxy_main.cc b/aos/network/log_web_proxy_main.cc
index f93c5a2..12276c3 100644
--- a/aos/network/log_web_proxy_main.cc
+++ b/aos/network/log_web_proxy_main.cc
@@ -25,11 +25,8 @@
 int main(int argc, char **argv) {
   aos::InitGoogle(&argc, &argv);
 
-  const std::vector<std::string> unsorted_logfiles =
-      aos::logger::FindLogs(argc, argv);
-
   const std::vector<aos::logger::LogFile> logfiles =
-      aos::logger::SortParts(unsorted_logfiles);
+      aos::logger::SortParts(aos::logger::FindLogs(argc, argv));
 
   aos::logger::LogReader reader(logfiles);