Add interface to access log files

The final goal is to use it to play logs directly from memory.

Change-Id: I82349b9542fd83c92014ceec37f11a832189bdd6
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 9f31b15..cec39dd 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -100,6 +100,8 @@
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//visibility:public"],
     deps = [
+        ":buffer_encoder",
+        ":file_operations",
         "//aos/time",
         "//aos/util:file",
         "@com_github_google_glog//:glog",
diff --git a/aos/events/logging/log_backend.cc b/aos/events/logging/log_backend.cc
index 918a6e6..ad97f7d 100644
--- a/aos/events/logging/log_backend.cc
+++ b/aos/events/logging/log_backend.cc
@@ -4,9 +4,11 @@
 
 #include <filesystem>
 
+#include "absl/strings/match.h"
 #include "absl/strings/str_cat.h"
 #include "glog/logging.h"
 
+#include "aos/events/logging/file_operations.h"
 #include "aos/util/file.h"
 
 DEFINE_bool(direct, false,
@@ -334,6 +336,31 @@
   return std::make_unique<FileHandler>(filename);
 }
 
+std::vector<std::string> 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;
+  operations.FindLogs(&files);
+
+  std::vector<std::string> names;
+  const std::string prefix = absl::StrCat(base_name_, separator_);
+  for (const auto &file : files) {
+    CHECK(absl::StartsWith(file, prefix));
+    names.push_back(file.substr(prefix.size()));
+  }
+  return names;
+}
+
+std::unique_ptr<DataDecoder> FileBackend::GetDecoder(
+    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);
+}
+
 RenamableFileBackend::RenamableFileBackend(std::string_view base_name)
     : base_name_(base_name), separator_(base_name_.back() == '/' ? "" : "_") {}
 
diff --git a/aos/events/logging/log_backend.h b/aos/events/logging/log_backend.h
index 6f79bc3..193c54f 100644
--- a/aos/events/logging/log_backend.h
+++ b/aos/events/logging/log_backend.h
@@ -11,6 +11,7 @@
 
 #include "absl/types/span.h"
 
+#include "aos/events/logging/buffer_encoder.h"
 #include "aos/time/time.h"
 
 namespace aos::logger {
@@ -237,7 +238,7 @@
   int flags_ = 0;
 };
 
-// Class that decouples log writing and media (file system or memory). It is
+// Interface to decouple log writing and media (file system or memory). It is
 // handy to use for tests.
 class LogBackend {
  public:
@@ -249,8 +250,21 @@
   virtual std::unique_ptr<LogSink> RequestFile(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;
+};
+
 // Implements requests log files from file system.
-class FileBackend : public LogBackend {
+class FileBackend : public LogBackend, public LogSource {
  public:
   // base_name is the path to the folder where log files are.
   explicit FileBackend(std::string_view base_name);
@@ -259,6 +273,12 @@
   // Request file from a file system. It is not open yet.
   std::unique_ptr<LogSink> RequestFile(std::string_view id) override;
 
+  // List all files that looks like log files under base_name.
+  std::vector<std::string> ListFiles() const override;
+
+  // Open decoder to read the content of the file.
+  std::unique_ptr<DataDecoder> GetDecoder(std::string_view id) const override;
+
  private:
   const std::string base_name_;
   const std::string_view separator_;
diff --git a/aos/events/logging/log_backend_test.cc b/aos/events/logging/log_backend_test.cc
index 4e6987f..2720e46 100644
--- a/aos/events/logging/log_backend_test.cc
+++ b/aos/events/logging/log_backend_test.cc
@@ -27,14 +27,26 @@
 
 TEST(LogBackendTest, CreateSimpleFile) {
   const std::string logevent = aos::testing::TestTmpDir() + "/logevent/";
+  const std::string filename = "test.bfbs";
   FileBackend backend(logevent);
-  auto file = backend.RequestFile("test.log");
+  auto file = backend.RequestFile(filename);
   ASSERT_EQ(file->OpenForWrite(), WriteCode::kOk);
   auto result = Write(file.get(), "test");
   EXPECT_EQ(result.code, WriteCode::kOk);
   EXPECT_EQ(result.messages_written, 1);
   EXPECT_EQ(file->Close(), WriteCode::kOk);
-  EXPECT_TRUE(std::filesystem::exists(logevent + "test.log"));
+  EXPECT_TRUE(std::filesystem::exists(logevent + filename));
+
+  EXPECT_THAT(backend.ListFiles(), ::testing::ElementsAre(filename));
+
+  auto decoder = backend.GetDecoder(filename);
+  std::vector<uint8_t> buffer;
+  buffer.resize(10);
+  const auto count = decoder->Read(buffer.data(), buffer.data() + 10);
+  ASSERT_EQ(count, 4);
+  buffer.resize(4);
+  std::string view(buffer.begin(), buffer.end());
+  EXPECT_EQ(view, "test");
 }
 
 TEST(LogBackendTest, CreateRenamableFile) {