Adds functionality to write logs to memory.

This makes it possible to save log outputs from scenario tests to
either files or memory.

Bug: webrtc:9510
Change-Id: I883bd8240ab712d31d54118adf979041bd83481a
Reviewed-on: https://webrtc-review.googlesource.com/c/116321
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26284}
diff --git a/api/rtc_event_log_output.h b/api/rtc_event_log_output.h
index 4a46324..92fb9e8 100644
--- a/api/rtc_event_log_output.h
+++ b/api/rtc_event_log_output.h
@@ -32,6 +32,9 @@
   // after the first time |false| is returned. Write() may not be called on
   // an inactive output sink.
   virtual bool Write(const std::string& output) = 0;
+
+  // Indicates that buffers should be written to disk if applicable.
+  virtual void Flush() {}
 };
 
 }  // namespace webrtc
diff --git a/modules/congestion_controller/BUILD.gn b/modules/congestion_controller/BUILD.gn
index 6857b5f..4cbe732 100644
--- a/modules/congestion_controller/BUILD.gn
+++ b/modules/congestion_controller/BUILD.gn
@@ -101,6 +101,7 @@
       "../../api/units:time_delta",
       "../../api/units:timestamp",
       "../../rtc_base:checks",
+      "../../test/logging:log_writer",
       "//third_party/abseil-cpp/absl/types:optional",
     ]
   }
diff --git a/modules/congestion_controller/bbr/BUILD.gn b/modules/congestion_controller/bbr/BUILD.gn
index 0430a45..a6183be 100644
--- a/modules/congestion_controller/bbr/BUILD.gn
+++ b/modules/congestion_controller/bbr/BUILD.gn
@@ -128,6 +128,7 @@
       "..:test_controller_printer",
       "../../../api/transport:network_control",
       "../../../api/units:timestamp",
+      "../../../logging:rtc_event_log_api",
       "../../../rtc_base:checks",
     ]
   }
diff --git a/modules/congestion_controller/bbr/test/bbr_printer.cc b/modules/congestion_controller/bbr/test/bbr_printer.cc
index c2eb578..3249493 100644
--- a/modules/congestion_controller/bbr/test/bbr_printer.cc
+++ b/modules/congestion_controller/bbr/test/bbr_printer.cc
@@ -24,15 +24,17 @@
   return controller_ != nullptr;
 }
 
-void BbrStatePrinter::PrintHeaders(FILE* out) {
-  fprintf(out, "bbr_mode bbr_recovery_state round_trip_count gain_cycle_index");
+void BbrStatePrinter::PrintHeaders(RtcEventLogOutput* out) {
+  LogWriteFormat(
+      out, "bbr_mode bbr_recovery_state round_trip_count gain_cycle_index");
 }
 
-void BbrStatePrinter::PrintValues(FILE* out) {
+void BbrStatePrinter::PrintValues(RtcEventLogOutput* out) {
   RTC_CHECK(controller_);
   bbr::BbrNetworkController::DebugState debug(*controller_);
-  fprintf(out, "%i %i %i %i", debug.mode, debug.recovery_state,
-          static_cast<int>(debug.round_trip_count), debug.gain_cycle_index);
+  LogWriteFormat(out, "%i %i %i %i", debug.mode, debug.recovery_state,
+                 static_cast<int>(debug.round_trip_count),
+                 debug.gain_cycle_index);
 }
 
 NetworkControlUpdate BbrStatePrinter::GetState(Timestamp at_time) const {
diff --git a/modules/congestion_controller/bbr/test/bbr_printer.h b/modules/congestion_controller/bbr/test/bbr_printer.h
index 84a765e..c2b2843 100644
--- a/modules/congestion_controller/bbr/test/bbr_printer.h
+++ b/modules/congestion_controller/bbr/test/bbr_printer.h
@@ -16,6 +16,7 @@
 #include "api/transport/network_control.h"
 #include "api/transport/network_types.h"
 #include "api/units/timestamp.h"
+#include "logging/rtc_event_log/rtc_event_log.h"
 #include "modules/congestion_controller/bbr/bbr_factory.h"
 #include "modules/congestion_controller/bbr/bbr_network_controller.h"
 #include "modules/congestion_controller/test/controller_printer.h"
@@ -28,8 +29,8 @@
   void Attach(bbr::BbrNetworkController*);
   bool Attached() const override;
 
-  void PrintHeaders(FILE* out) override;
-  void PrintValues(FILE* out) override;
+  void PrintHeaders(RtcEventLogOutput* out) override;
+  void PrintValues(RtcEventLogOutput* out) override;
 
   NetworkControlUpdate GetState(Timestamp at_time) const override;
 
diff --git a/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc b/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc
index 717f554..c67f7e4 100644
--- a/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc
+++ b/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc
@@ -31,25 +31,26 @@
   return controller_ != nullptr;
 }
 
-void GoogCcStatePrinter::PrintHeaders(FILE* out) {
-  fprintf(out,
-          "rate_control_state rate_control_region alr_state"
-          " trendline trendline_modified_offset trendline_offset_threshold");
+void GoogCcStatePrinter::PrintHeaders(RtcEventLogOutput* out) {
+  out->Write(
+      "rate_control_state rate_control_region alr_state"
+      " trendline trendline_modified_offset trendline_offset_threshold");
 }
 
-void GoogCcStatePrinter::PrintValues(FILE* out) {
+void GoogCcStatePrinter::PrintValues(RtcEventLogOutput* out) {
   RTC_CHECK(controller_);
   auto* detector = controller_->delay_based_bwe_->delay_detector_.get();
   auto* trendline_estimator = reinterpret_cast<TrendlineEstimator*>(detector);
-  fprintf(out, "%i %f %i %.6lf %.6lf %.6lf",
-          controller_->delay_based_bwe_->rate_control_.rate_control_state_,
-          controller_->delay_based_bwe_->rate_control_.link_capacity_
-                  .estimate_kbps_.value_or(NAN) *
-              1000 / 8,
-          controller_->alr_detector_->alr_started_time_ms_.has_value(),
-          trendline_estimator->prev_trend_,
-          trendline_estimator->prev_modified_trend_,
-          trendline_estimator->threshold_);
+  LogWriteFormat(
+      out, "%i %f %i %.6lf %.6lf %.6lf",
+      controller_->delay_based_bwe_->rate_control_.rate_control_state_,
+      controller_->delay_based_bwe_->rate_control_.link_capacity_.estimate_kbps_
+              .value_or(NAN) *
+          1000 / 8,
+      controller_->alr_detector_->alr_started_time_ms_.has_value(),
+      trendline_estimator->prev_trend_,
+      trendline_estimator->prev_modified_trend_,
+      trendline_estimator->threshold_);
 }
 
 NetworkControlUpdate GoogCcStatePrinter::GetState(Timestamp at_time) const {
diff --git a/modules/congestion_controller/goog_cc/test/goog_cc_printer.h b/modules/congestion_controller/goog_cc/test/goog_cc_printer.h
index b254db4..173c6c3 100644
--- a/modules/congestion_controller/goog_cc/test/goog_cc_printer.h
+++ b/modules/congestion_controller/goog_cc/test/goog_cc_printer.h
@@ -29,8 +29,8 @@
   void Attach(GoogCcNetworkController*);
   bool Attached() const override;
 
-  void PrintHeaders(FILE* out) override;
-  void PrintValues(FILE* out) override;
+  void PrintHeaders(RtcEventLogOutput* out) override;
+  void PrintValues(RtcEventLogOutput* out) override;
 
   NetworkControlUpdate GetState(Timestamp at_time) const override;
 
diff --git a/modules/congestion_controller/test/controller_printer.cc b/modules/congestion_controller/test/controller_printer.cc
index a23a5ab..9b3cb59 100644
--- a/modules/congestion_controller/test/controller_printer.cc
+++ b/modules/congestion_controller/test/controller_printer.cc
@@ -20,20 +20,20 @@
 namespace webrtc {
 
 ControlStatePrinter::ControlStatePrinter(
-    FILE* output,
+    std::unique_ptr<RtcEventLogOutput> output,
     std::unique_ptr<DebugStatePrinter> debug_printer)
-    : output_(output), debug_printer_(std::move(debug_printer)) {}
+    : output_(std::move(output)), debug_printer_(std::move(debug_printer)) {}
 
 ControlStatePrinter::~ControlStatePrinter() = default;
 
 void ControlStatePrinter::PrintHeaders() {
-  fprintf(output_, "time bandwidth rtt target pacing padding window");
+  output_->Write("time bandwidth rtt target pacing padding window");
   if (debug_printer_) {
-    fprintf(output_, " ");
-    debug_printer_->PrintHeaders(output_);
+    output_->Write(" ");
+    debug_printer_->PrintHeaders(output_.get());
   }
-  fprintf(output_, "\n");
-  fflush(output_);
+  output_->Write("\n");
+  output_->Flush();
 }
 
 void ControlStatePrinter::PrintState(const Timestamp time,
@@ -48,16 +48,16 @@
   double congestion_window = state.congestion_window
                                  ? state.congestion_window->bytes<double>()
                                  : std::numeric_limits<double>::infinity();
-
-  fprintf(output_, "%f %f %f %f %f %f %f", timestamp, bandwidth, rtt,
-          target_rate, pacing_rate, padding_rate, congestion_window);
+  LogWriteFormat(output_.get(), "%f %f %f %f %f %f %f", timestamp, bandwidth,
+                 rtt, target_rate, pacing_rate, padding_rate,
+                 congestion_window);
 
   if (debug_printer_) {
-    fprintf(output_, " ");
-    debug_printer_->PrintValues(output_);
+    output_->Write(" ");
+    debug_printer_->PrintValues(output_.get());
   }
-  fprintf(output_, "\n");
-  fflush(output_);
+  output_->Write("\n");
+  output_->Flush();
 }
 
 void ControlStatePrinter::PrintState(const Timestamp time) {
diff --git a/modules/congestion_controller/test/controller_printer.h b/modules/congestion_controller/test/controller_printer.h
index d619157..f88d50b 100644
--- a/modules/congestion_controller/test/controller_printer.h
+++ b/modules/congestion_controller/test/controller_printer.h
@@ -10,25 +10,25 @@
 #ifndef MODULES_CONGESTION_CONTROLLER_TEST_CONTROLLER_PRINTER_H_
 #define MODULES_CONGESTION_CONTROLLER_TEST_CONTROLLER_PRINTER_H_
 
-#include <cstdio>
 #include <memory>
 
 #include "api/transport/network_types.h"
 #include "api/units/timestamp.h"
+#include "test/logging/log_writer.h"
 
 namespace webrtc {
 class DebugStatePrinter {
  public:
   virtual bool Attached() const = 0;
-  virtual void PrintHeaders(FILE* out) = 0;
-  virtual void PrintValues(FILE* out) = 0;
+  virtual void PrintHeaders(RtcEventLogOutput* out) = 0;
+  virtual void PrintValues(RtcEventLogOutput* out) = 0;
   virtual NetworkControlUpdate GetState(Timestamp at_time) const = 0;
   virtual ~DebugStatePrinter() = default;
 };
 
 class ControlStatePrinter {
  public:
-  ControlStatePrinter(FILE* output,
+  ControlStatePrinter(std::unique_ptr<RtcEventLogOutput> output,
                       std::unique_ptr<DebugStatePrinter> debug_printer);
   ~ControlStatePrinter();
   void PrintHeaders();
@@ -36,7 +36,7 @@
   void PrintState(const Timestamp time);
 
  private:
-  FILE* output_;
+  std::unique_ptr<RtcEventLogOutput> output_;
   std::unique_ptr<DebugStatePrinter> debug_printer_;
 };
 }  // namespace webrtc
diff --git a/test/logging/BUILD.gn b/test/logging/BUILD.gn
new file mode 100644
index 0000000..f588925
--- /dev/null
+++ b/test/logging/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import("../../webrtc.gni")
+
+rtc_source_set("log_writer") {
+  testonly = true
+  visibility = [ "*" ]
+  sources = [
+    "file_log_writer.cc",
+    "file_log_writer.h",
+    "log_writer.cc",
+    "log_writer.h",
+    "memory_log_writer.cc",
+    "memory_log_writer.h",
+  ]
+
+  deps = [
+    "../../api:libjingle_logging_api",
+    "../../rtc_base:checks",
+    "../../rtc_base:logging",
+    "../../rtc_base:rtc_base_tests_utils",
+    "../../rtc_base:stringutils",
+    "../../test:fileutils",
+    "//third_party/abseil-cpp/absl/memory",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+}
diff --git a/test/logging/file_log_writer.cc b/test/logging/file_log_writer.cc
new file mode 100644
index 0000000..f8c32f4
--- /dev/null
+++ b/test/logging/file_log_writer.cc
@@ -0,0 +1,62 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "test/logging/file_log_writer.h"
+
+#include "absl/memory/memory.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "test/testsupport/fileutils.h"
+
+namespace webrtc {
+namespace webrtc_impl {
+
+FileLogWriter::FileLogWriter(std::string file_path)
+    : out_(std::fopen(file_path.c_str(), "wb")) {
+  RTC_CHECK(out_ != nullptr)
+      << "Failed to open file: '" << file_path << "' for writing.";
+}
+
+FileLogWriter::~FileLogWriter() {
+  std::fclose(out_);
+}
+
+bool FileLogWriter::IsActive() const {
+  return true;
+}
+
+bool FileLogWriter::Write(const std::string& value) {
+  // We don't expect the write to fail. If it does, we don't want to risk
+  // silently ignoring it.
+  RTC_CHECK_EQ(std::fwrite(value.data(), 1, value.size(), out_), value.size())
+      << "fwrite failed unexpectedly: " << errno;
+  return true;
+}
+
+void FileLogWriter::Flush() {
+  RTC_CHECK_EQ(fflush(out_), 0) << "fflush failed unexpectedly: " << errno;
+}
+
+}  // namespace webrtc_impl
+
+FileLogWriterFactory::FileLogWriterFactory(std::string base_path)
+    : base_path_(base_path) {
+  for (size_t i = 0; i < base_path.size(); ++i) {
+    if (base_path[i] == '/')
+      test::CreateDir(base_path.substr(0, i));
+  }
+}
+
+FileLogWriterFactory::~FileLogWriterFactory() {}
+
+std::unique_ptr<RtcEventLogOutput> FileLogWriterFactory::Create(
+    std::string filename) {
+  return absl::make_unique<webrtc_impl::FileLogWriter>(base_path_ + filename);
+}
+}  // namespace webrtc
diff --git a/test/logging/file_log_writer.h b/test/logging/file_log_writer.h
new file mode 100644
index 0000000..e3c0cf6
--- /dev/null
+++ b/test/logging/file_log_writer.h
@@ -0,0 +1,48 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef TEST_LOGGING_FILE_LOG_WRITER_H_
+#define TEST_LOGGING_FILE_LOG_WRITER_H_
+
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "test/logging/log_writer.h"
+
+namespace webrtc {
+namespace webrtc_impl {
+class FileLogWriter final : public RtcEventLogOutput {
+ public:
+  explicit FileLogWriter(std::string file_path);
+  ~FileLogWriter() final;
+  bool IsActive() const override;
+  bool Write(const std::string& value) override;
+  void Flush() override;
+
+ private:
+  std::FILE* const out_;
+};
+}  // namespace webrtc_impl
+class FileLogWriterFactory final : public LogWriterFactoryInterface {
+ public:
+  explicit FileLogWriterFactory(std::string base_path);
+  ~FileLogWriterFactory() final;
+
+  std::unique_ptr<RtcEventLogOutput> Create(std::string filename) override;
+
+ private:
+  const std::string base_path_;
+  std::vector<std::unique_ptr<webrtc_impl::FileLogWriter>> writers_;
+};
+
+}  // namespace webrtc
+
+#endif  // TEST_LOGGING_FILE_LOG_WRITER_H_
diff --git a/test/logging/log_writer.cc b/test/logging/log_writer.cc
new file mode 100644
index 0000000..a20d026
--- /dev/null
+++ b/test/logging/log_writer.cc
@@ -0,0 +1,24 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "test/logging/log_writer.h"
+
+namespace webrtc {
+
+LogWriterFactoryAddPrefix::LogWriterFactoryAddPrefix(
+    LogWriterFactoryInterface* base,
+    std::string prefix)
+    : base_factory_(base), prefix_(prefix) {}
+
+std::unique_ptr<RtcEventLogOutput> LogWriterFactoryAddPrefix::Create(
+    std::string filename) {
+  return base_factory_->Create(prefix_ + filename);
+}
+
+}  // namespace webrtc
diff --git a/test/logging/log_writer.h b/test/logging/log_writer.h
new file mode 100644
index 0000000..8946cec
--- /dev/null
+++ b/test/logging/log_writer.h
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef TEST_LOGGING_LOG_WRITER_H_
+#define TEST_LOGGING_LOG_WRITER_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "api/rtceventlogoutput.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+template <class... Args>
+inline void LogWriteFormat(RtcEventLogOutput* out_, const char* fmt, ...) {
+  va_list args, copy;
+  va_start(args, fmt);
+  va_copy(copy, args);
+  const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy);
+  va_end(copy);
+
+  RTC_DCHECK_GE(predicted_length, 0);
+  std::string out_str(predicted_length, '\0');
+  if (predicted_length > 0) {
+    // Pass "+ 1" to vsnprintf to include space for the '\0'.
+    const int actual_length =
+        std::vsnprintf(&out_str.front(), predicted_length + 1, fmt, args);
+    RTC_DCHECK_GE(actual_length, 0);
+  }
+  va_end(args);
+  out_->Write(out_str);
+}
+
+class LogWriterFactoryInterface {
+ public:
+  virtual std::unique_ptr<RtcEventLogOutput> Create(std::string filename) = 0;
+  virtual ~LogWriterFactoryInterface() = default;
+};
+
+class LogWriterFactoryAddPrefix : public LogWriterFactoryInterface {
+ public:
+  LogWriterFactoryAddPrefix(LogWriterFactoryInterface* base,
+                            std::string prefix);
+  std::unique_ptr<RtcEventLogOutput> Create(std::string filename) override;
+
+ private:
+  LogWriterFactoryInterface* const base_factory_;
+  const std::string prefix_;
+};
+
+}  // namespace webrtc
+
+#endif  // TEST_LOGGING_LOG_WRITER_H_
diff --git a/test/logging/memory_log_writer.cc b/test/logging/memory_log_writer.cc
new file mode 100644
index 0000000..92945ed
--- /dev/null
+++ b/test/logging/memory_log_writer.cc
@@ -0,0 +1,66 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "test/logging/memory_log_writer.h"
+
+#include "absl/memory/memory.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+namespace webrtc {
+namespace {
+class MemoryLogWriter final : public RtcEventLogOutput {
+ public:
+  explicit MemoryLogWriter(std::map<std::string, std::string>* target,
+                           std::string filename)
+      : target_(target), filename_(filename) {}
+  ~MemoryLogWriter() final {
+    size_t size;
+    buffer_.GetSize(&size);
+    target_->insert({filename_, std::string(buffer_.GetBuffer(), size)});
+  }
+  bool IsActive() const override { return true; }
+  bool Write(const std::string& value) override {
+    size_t written;
+    int error;
+    return buffer_.Write(value.data(), value.size(), &written, &error) ==
+           rtc::SR_SUCCESS;
+  }
+  void Flush() override {}
+
+ private:
+  std::map<std::string, std::string>* const target_;
+  const std::string filename_;
+  rtc::MemoryStream buffer_;
+};
+
+class MemoryLogWriterFactory : public LogWriterFactoryInterface {
+ public:
+  explicit MemoryLogWriterFactory(std::map<std::string, std::string>* target)
+      : target_(target) {}
+  ~MemoryLogWriterFactory() final {}
+  std::unique_ptr<RtcEventLogOutput> Create(std::string filename) override {
+    return absl::make_unique<MemoryLogWriter>(target_, filename);
+  }
+
+ private:
+  std::map<std::string, std::string>* const target_;
+};
+
+}  // namespace
+
+MemoryLogStorage::MemoryLogStorage() {}
+
+MemoryLogStorage::~MemoryLogStorage() {}
+
+std::unique_ptr<LogWriterFactoryInterface> MemoryLogStorage::CreateFactory() {
+  return absl::make_unique<MemoryLogWriterFactory>(&logs_);
+}
+
+// namespace webrtc_impl
+}  // namespace webrtc
diff --git a/test/logging/memory_log_writer.h b/test/logging/memory_log_writer.h
new file mode 100644
index 0000000..daef297
--- /dev/null
+++ b/test/logging/memory_log_writer.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef TEST_LOGGING_MEMORY_LOG_WRITER_H_
+#define TEST_LOGGING_MEMORY_LOG_WRITER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "rtc_base/memory_stream.h"
+#include "test/logging/log_writer.h"
+
+namespace webrtc {
+
+// Allows creating log writer factories that creates log writers that saves
+// their content to memory. When the log writers are destroyed, their content is
+// saved to the logs_ member of this class. The intended usage is to keep this
+// class alive after the created factories and writers have been destroyed and
+// then use logs() to access all the saved logs.
+class MemoryLogStorage {
+ public:
+  MemoryLogStorage();
+  ~MemoryLogStorage();
+  std::unique_ptr<LogWriterFactoryInterface> CreateFactory();
+  const std::map<std::string, std::string>& logs() { return logs_; }
+
+ private:
+  std::map<std::string, std::string> logs_;
+};
+
+}  // namespace webrtc
+
+#endif  // TEST_LOGGING_MEMORY_LOG_WRITER_H_
diff --git a/test/scenario/BUILD.gn b/test/scenario/BUILD.gn
index feb09d4..ff7ada6 100644
--- a/test/scenario/BUILD.gn
+++ b/test/scenario/BUILD.gn
@@ -95,6 +95,7 @@
       "../../system_wrappers",
       "../../system_wrappers:field_trial",
       "../../video",
+      "../logging:log_writer",
       "network:emulated_network",
       "//third_party/abseil-cpp/absl/memory",
       "//third_party/abseil-cpp/absl/types:optional",
diff --git a/test/scenario/call_client.cc b/test/scenario/call_client.cc
index 866df06..feb50b0 100644
--- a/test/scenario/call_client.cc
+++ b/test/scenario/call_client.cc
@@ -12,7 +12,6 @@
 #include <utility>
 
 #include "absl/memory/memory.h"
-#include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
 #include "modules/audio_mixer/audio_mixer_impl.h"
 #include "modules/congestion_controller/goog_cc/test/goog_cc_printer.h"
 #include "test/call_test.h"
@@ -57,39 +56,38 @@
 }
 
 LoggingNetworkControllerFactory::LoggingNetworkControllerFactory(
-    std::string filename,
+    LogWriterFactoryInterface* log_writer_factory,
     TransportControllerConfig config) {
-  if (filename.empty()) {
+  std::unique_ptr<RtcEventLogOutput> cc_out;
+  if (!log_writer_factory) {
     event_log_ = RtcEventLog::CreateNull();
   } else {
     event_log_ = RtcEventLog::Create(RtcEventLog::EncodingType::Legacy);
     bool success = event_log_->StartLogging(
-        absl::make_unique<RtcEventLogOutputFile>(filename + ".rtc.dat",
-                                                 RtcEventLog::kUnlimitedOutput),
-        RtcEventLog::kImmediateOutput);
+        log_writer_factory->Create(".rtc.dat"), RtcEventLog::kImmediateOutput);
     RTC_CHECK(success);
-    cc_out_ = fopen((filename + ".cc_state.txt").c_str(), "w");
+    cc_out = log_writer_factory->Create(".cc_state.txt");
   }
   switch (config.cc) {
     case TransportControllerConfig::CongestionController::kGoogCc:
-      if (cc_out_) {
+      if (cc_out) {
         auto goog_printer = absl::make_unique<GoogCcStatePrinter>();
         owned_cc_factory_.reset(
             new GoogCcDebugFactory(event_log_.get(), goog_printer.get()));
-        cc_printer_.reset(
-            new ControlStatePrinter(cc_out_, std::move(goog_printer)));
+        cc_printer_.reset(new ControlStatePrinter(std::move(cc_out),
+                                                  std::move(goog_printer)));
       } else {
         owned_cc_factory_.reset(
             new GoogCcNetworkControllerFactory(event_log_.get()));
       }
       break;
     case TransportControllerConfig::CongestionController::kGoogCcFeedback:
-      if (cc_out_) {
+      if (cc_out) {
         auto goog_printer = absl::make_unique<GoogCcStatePrinter>();
         owned_cc_factory_.reset(new GoogCcFeedbackDebugFactory(
             event_log_.get(), goog_printer.get()));
-        cc_printer_.reset(
-            new ControlStatePrinter(cc_out_, std::move(goog_printer)));
+        cc_printer_.reset(new ControlStatePrinter(std::move(cc_out),
+                                                  std::move(goog_printer)));
       } else {
         owned_cc_factory_.reset(
             new GoogCcFeedbackNetworkControllerFactory(event_log_.get()));
@@ -97,7 +95,7 @@
       break;
     case TransportControllerConfig::CongestionController::kInjected:
       cc_factory_ = config.cc_factory;
-      if (cc_out_)
+      if (cc_out)
         RTC_LOG(LS_WARNING)
             << "Can't log controller state for injected network controllers";
       break;
@@ -111,8 +109,6 @@
 }
 
 LoggingNetworkControllerFactory::~LoggingNetworkControllerFactory() {
-  if (cc_out_)
-    fclose(cc_out_);
 }
 
 void LoggingNetworkControllerFactory::LogCongestionControllerStats(
@@ -134,13 +130,13 @@
   return cc_factory_->GetProcessInterval();
 }
 
-CallClient::CallClient(Clock* clock,
-                       std::string name,
-                       std::string log_filename,
-                       CallClientConfig config)
+CallClient::CallClient(
+    Clock* clock,
+    std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
+    CallClientConfig config)
     : clock_(clock),
-      name_(name),
-      network_controller_factory_(log_filename, config.transport),
+      log_writer_factory_(std::move(log_writer_factory)),
+      network_controller_factory_(log_writer_factory_.get(), config.transport),
       fake_audio_setup_(InitAudio()),
       call_(CreateCall(config,
                        &network_controller_factory_,
@@ -189,6 +185,12 @@
                                    packet.arrival_time.us());
 }
 
+std::unique_ptr<RtcEventLogOutput> CallClient::GetLogWriter(std::string name) {
+  if (!log_writer_factory_ || name.empty())
+    return nullptr;
+  return log_writer_factory_->Create(name);
+}
+
 uint32_t CallClient::GetNextVideoSsrc() {
   RTC_CHECK_LT(next_video_ssrc_index_, CallTest::kNumSsrcs);
   return CallTest::kVideoSendSsrcs[next_video_ssrc_index_++];
diff --git a/test/scenario/call_client.h b/test/scenario/call_client.h
index ace6366..793e16d 100644
--- a/test/scenario/call_client.h
+++ b/test/scenario/call_client.h
@@ -19,6 +19,7 @@
 #include "modules/congestion_controller/test/controller_printer.h"
 #include "modules/rtp_rtcp/include/rtp_header_parser.h"
 #include "rtc_base/constructor_magic.h"
+#include "test/logging/log_writer.h"
 #include "test/scenario/column_printer.h"
 #include "test/scenario/network/network_emulation.h"
 #include "test/scenario/network_node.h"
@@ -30,7 +31,7 @@
 class LoggingNetworkControllerFactory
     : public NetworkControllerFactoryInterface {
  public:
-  LoggingNetworkControllerFactory(std::string filename,
+  LoggingNetworkControllerFactory(LogWriterFactoryInterface* log_writer_factory,
                                   TransportControllerConfig config);
   RTC_DISALLOW_COPY_AND_ASSIGN(LoggingNetworkControllerFactory);
   ~LoggingNetworkControllerFactory();
@@ -46,7 +47,6 @@
   std::unique_ptr<NetworkControllerFactoryInterface> owned_cc_factory_;
   NetworkControllerFactoryInterface* cc_factory_ = nullptr;
   std::unique_ptr<ControlStatePrinter> cc_printer_;
-  FILE* cc_out_ = nullptr;
 };
 
 struct CallClientFakeAudio {
@@ -60,8 +60,7 @@
 class CallClient : public EmulatedNetworkReceiverInterface {
  public:
   CallClient(Clock* clock,
-             std::string name,
-             std::string log_filename,
+             std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
              CallClientConfig config);
   RTC_DISALLOW_COPY_AND_ASSIGN(CallClient);
 
@@ -73,6 +72,7 @@
   }
 
   void OnPacketReceived(EmulatedIpPacket packet) override;
+  std::unique_ptr<RtcEventLogOutput> GetLogWriter(std::string name);
 
  private:
   friend class Scenario;
@@ -91,7 +91,7 @@
   void AddExtensions(std::vector<RtpExtension> extensions);
 
   Clock* clock_;
-  const std::string name_;
+  const std::unique_ptr<LogWriterFactoryInterface> log_writer_factory_;
   LoggingNetworkControllerFactory network_controller_factory_;
   CallClientFakeAudio fake_audio_setup_;
   std::unique_ptr<Call> call_;
diff --git a/test/scenario/column_printer.cc b/test/scenario/column_printer.cc
index 234c919..ab70af9 100644
--- a/test/scenario/column_printer.cc
+++ b/test/scenario/column_printer.cc
@@ -34,38 +34,26 @@
   return ColumnPrinter(headers, printer, max_length);
 }
 
-StatesPrinter::StatesPrinter(std::string filename,
+StatesPrinter::StatesPrinter(std::unique_ptr<RtcEventLogOutput> writer,
                              std::vector<ColumnPrinter> printers)
-    : StatesPrinter(printers) {
-  if (!filename.empty()) {
-    output_file_ = fopen(filename.c_str(), "w");
-    RTC_CHECK(output_file_);
-    output_ = output_file_;
-  }
-}
-
-StatesPrinter::StatesPrinter(std::vector<ColumnPrinter> printers)
-    : printers_(printers) {
-  output_ = stdout;
+    : writer_(std::move(writer)), printers_(printers) {
   RTC_CHECK(!printers_.empty());
   for (auto& printer : printers_)
     buffer_size_ += printer.max_length_ + 1;
   buffer_.resize(buffer_size_);
 }
 
-StatesPrinter::~StatesPrinter() {
-  if (output_file_)
-    fclose(output_file_);
-}
+StatesPrinter::~StatesPrinter() = default;
 
 void StatesPrinter::PrintHeaders() {
-  if (!output_file_)
+  if (!writer_)
     return;
-  fprintf(output_, "%s", printers_[0].headers_);
+  writer_->Write(printers_[0].headers_);
   for (size_t i = 1; i < printers_.size(); ++i) {
-    fprintf(output_, " %s", printers_[i].headers_);
+    writer_->Write(" ");
+    writer_->Write(printers_[i].headers_);
   }
-  fputs("\n", output_);
+  writer_->Write("\n");
 }
 
 void StatesPrinter::PrintRow() {
@@ -77,9 +65,9 @@
     sb << ' ';
     printers_[i].printer_(sb);
   }
-  sb << "\n\0";
-  if (output_file_)
-    fputs(&buffer_.front(), output_);
+  sb << "\n";
+  if (writer_)
+    writer_->Write(std::string(sb.str(), sb.size()));
 }
 }  // namespace test
 }  // namespace webrtc
diff --git a/test/scenario/column_printer.h b/test/scenario/column_printer.h
index 5ac9a1b..4ace50d 100644
--- a/test/scenario/column_printer.h
+++ b/test/scenario/column_printer.h
@@ -16,6 +16,7 @@
 
 #include "rtc_base/constructor_magic.h"
 #include "rtc_base/strings/string_builder.h"
+#include "test/logging/log_writer.h"
 
 namespace webrtc {
 namespace test {
@@ -43,19 +44,18 @@
 
 class StatesPrinter {
  public:
-  StatesPrinter(std::string filename, std::vector<ColumnPrinter> printers);
-  explicit StatesPrinter(std::vector<ColumnPrinter> printers);
+  StatesPrinter(std::unique_ptr<RtcEventLogOutput> writer,
+                std::vector<ColumnPrinter> printers);
   RTC_DISALLOW_COPY_AND_ASSIGN(StatesPrinter);
   ~StatesPrinter();
   void PrintHeaders();
   void PrintRow();
 
  private:
+  const std::unique_ptr<RtcEventLogOutput> writer_;
   const std::vector<ColumnPrinter> printers_;
   size_t buffer_size_ = 0;
   std::vector<char> buffer_;
-  FILE* output_file_ = nullptr;
-  FILE* output_ = nullptr;
 };
 }  // namespace test
 }  // namespace webrtc
diff --git a/test/scenario/quality_stats.cc b/test/scenario/quality_stats.cc
index 2c73437..863cf1f 100644
--- a/test/scenario/quality_stats.cc
+++ b/test/scenario/quality_stats.cc
@@ -19,12 +19,10 @@
 namespace test {
 
 VideoQualityAnalyzer::VideoQualityAnalyzer(
-    std::string filename_or_empty,
+    std::unique_ptr<RtcEventLogOutput> writer,
     std::function<void(const VideoFrameQualityInfo&)> frame_info_handler)
-    : task_queue_("VideoAnalyzer") {
-  if (!filename_or_empty.empty()) {
-    output_file_ = fopen(filename_or_empty.c_str(), "w");
-    RTC_CHECK(output_file_);
+    : writer_(std::move(writer)), task_queue_("VideoAnalyzer") {
+  if (writer_) {
     PrintHeaders();
     frame_info_handlers_.push_back(
         [this](const VideoFrameQualityInfo& info) { PrintFrameInfo(info); });
@@ -37,8 +35,6 @@
   rtc::Event event;
   task_queue_.PostTask([&event] { event.Set(); });
   event.Wait(rtc::Event::kForever);
-  if (output_file_)
-    fclose(output_file_);
 }
 
 void VideoQualityAnalyzer::OnCapturedFrame(const VideoFrame& frame) {
@@ -114,15 +110,15 @@
 }
 
 void VideoQualityAnalyzer::PrintHeaders() {
-  fprintf(output_file_, "capt recv_capt render width height psnr\n");
+  writer_->Write("capt recv_capt render width height psnr\n");
 }
 
 void VideoQualityAnalyzer::PrintFrameInfo(const VideoFrameQualityInfo& sample) {
-  fprintf(output_file_, "%.3f %.3f %.3f %i %i %.3f\n",
-          sample.capture_time.seconds<double>(),
-          sample.received_capture_time.seconds<double>(),
-          sample.render_time.seconds<double>(), sample.width, sample.height,
-          sample.psnr);
+  LogWriteFormat(writer_.get(), "%.3f %.3f %.3f %i %i %.3f\n",
+                 sample.capture_time.seconds<double>(),
+                 sample.received_capture_time.seconds<double>(),
+                 sample.render_time.seconds<double>(), sample.width,
+                 sample.height, sample.psnr);
 }
 
 void VideoQualityStats::HandleFrameInfo(VideoFrameQualityInfo sample) {
diff --git a/test/scenario/quality_stats.h b/test/scenario/quality_stats.h
index 34702ac..18dc4f75 100644
--- a/test/scenario/quality_stats.h
+++ b/test/scenario/quality_stats.h
@@ -11,6 +11,7 @@
 #define TEST_SCENARIO_QUALITY_STATS_H_
 
 #include <deque>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -22,6 +23,7 @@
 #include "rtc_base/task_queue.h"
 #include "rtc_base/time_utils.h"
 #include "system_wrappers/include/clock.h"
+#include "test/logging/log_writer.h"
 #include "test/scenario/quality_info.h"
 #include "test/scenario/scenario_config.h"
 #include "test/statistics.h"
@@ -32,7 +34,7 @@
 class VideoQualityAnalyzer {
  public:
   VideoQualityAnalyzer(
-      std::string filename_or_empty,
+      std::unique_ptr<RtcEventLogOutput> writer,
       std::function<void(const VideoFrameQualityInfo&)> frame_info_handler);
   ~VideoQualityAnalyzer();
   void OnCapturedFrame(const VideoFrame& frame);
@@ -46,12 +48,12 @@
   int64_t CapturedFrameCaptureTimeOffsetMs(const VideoFrame& captured) const;
   void PrintHeaders();
   void PrintFrameInfo(const VideoFrameQualityInfo& sample);
+  const std::unique_ptr<RtcEventLogOutput> writer_;
   std::vector<std::function<void(const VideoFrameQualityInfo&)>>
       frame_info_handlers_;
   std::deque<VideoFrame> captured_frames_;
   absl::optional<int64_t> first_capture_ntp_time_ms_;
   absl::optional<uint32_t> first_decode_rtp_timestamp_;
-  FILE* output_file_ = nullptr;
   rtc::TaskQueue task_queue_;
 };
 
diff --git a/test/scenario/scenario.cc b/test/scenario/scenario.cc
index 0efe80c..49125a0 100644
--- a/test/scenario/scenario.cc
+++ b/test/scenario/scenario.cc
@@ -11,10 +11,12 @@
 
 #include <algorithm>
 
+#include "absl/memory/memory.h"
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
 #include "rtc_base/flags.h"
 #include "rtc_base/socket_address.h"
+#include "test/logging/file_log_writer.h"
 #include "test/scenario/network/network_emulation.h"
 #include "test/testsupport/file_utils.h"
 
@@ -27,6 +29,19 @@
 namespace test {
 namespace {
 int64_t kMicrosPerSec = 1000000;
+std::unique_ptr<FileLogWriterFactory> GetScenarioLogManager(
+    std::string file_name) {
+  if (FLAG_scenario_logs && !file_name.empty()) {
+    std::string output_root = FLAG_out_root;
+    if (output_root.empty())
+      output_root = OutputPath() + "output_data/";
+
+    auto base_filename = output_root + file_name + ".";
+    RTC_LOG(LS_INFO) << "Saving scenario logs to: " << base_filename;
+    return absl::make_unique<FileLogWriterFactory>(base_filename);
+  }
+  return nullptr;
+}
 }
 
 RepeatedActivity::RepeatedActivity(TimeDelta interval,
@@ -54,30 +69,24 @@
   return last_update_ + interval_;
 }
 
-Scenario::Scenario() : Scenario("", true) {}
+Scenario::Scenario()
+    : Scenario(std::unique_ptr<LogWriterFactoryInterface>(), true) {}
 
 Scenario::Scenario(std::string file_name) : Scenario(file_name, true) {}
 
 Scenario::Scenario(std::string file_name, bool real_time)
-    : real_time_mode_(real_time),
+    : Scenario(GetScenarioLogManager(file_name), real_time) {}
+
+Scenario::Scenario(
+    std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
+    bool real_time)
+    : log_writer_factory_(std::move(log_writer_factory)),
+      real_time_mode_(real_time),
       sim_clock_(100000 * kMicrosPerSec),
       clock_(real_time ? Clock::GetRealTimeClock() : &sim_clock_),
       audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()),
       audio_encoder_factory_(CreateBuiltinAudioEncoderFactory()) {
-  if (FLAG_scenario_logs && !file_name.empty()) {
-    std::string output_root = FLAG_out_root;
-    if (output_root.empty())
-      output_root = OutputPath() + "output_data/";
-    if (output_root.back() == '/')
-      CreateDir(output_root);
-    for (size_t i = 0; i < file_name.size(); ++i) {
-      if (file_name[i] == '/')
-        CreateDir(output_root + file_name.substr(0, i));
-    }
-    base_filename_ = output_root + file_name;
-    RTC_LOG(LS_INFO) << "Saving scenario logs to: " << base_filename_;
-  }
-  if (!real_time_mode_ && !base_filename_.empty()) {
+  if (!real_time_mode_ && log_writer_factory_) {
     rtc::SetClockForTesting(&event_log_fake_clock_);
     event_log_fake_clock_.SetTimeNanos(sim_clock_.TimeInMicroseconds() * 1000);
   }
@@ -105,8 +114,7 @@
   std::vector<ColumnPrinter> all_printers{TimePrinter()};
   for (auto& printer : printers)
     all_printers.push_back(printer);
-  StatesPrinter* printer =
-      new StatesPrinter(GetFullPathOrEmpty(name), all_printers);
+  StatesPrinter* printer = new StatesPrinter(GetLogWriter(name), all_printers);
   printers_.emplace_back(printer);
   printer->PrintHeaders();
   if (interval.IsFinite())
@@ -117,7 +125,7 @@
 CallClient* Scenario::CreateClient(std::string name, CallClientConfig config) {
   RTC_DCHECK(real_time_mode_);
   CallClient* client =
-      new CallClient(clock_, name, GetFullPathOrEmpty(name), config);
+      new CallClient(clock_, GetLogWriterFactory(name), config);
   if (config.transport.state_log_interval.IsFinite()) {
     Every(config.transport.state_log_interval, [this, client]() {
       client->network_controller_factory_.LogCongestionControllerStats(Now());
@@ -182,9 +190,9 @@
   uint64_t send_id = next_route_id_++;
   uint64_t return_id = next_route_id_++;
   SimulatedTimeClient* client = new SimulatedTimeClient(
-      GetFullPathOrEmpty(name), config, stream_configs, send_link, return_link,
+      GetLogWriterFactory(name), config, stream_configs, send_link, return_link,
       send_id, return_id, Now());
-  if (!base_filename_.empty() && !name.empty() &&
+  if (log_writer_factory_ && !name.empty() &&
       config.transport.state_log_interval.IsFinite()) {
     Every(config.transport.state_log_interval, [this, client]() {
       client->network_controller_factory_.LogCongestionControllerStats(Now());
@@ -283,12 +291,11 @@
 VideoStreamPair* Scenario::CreateVideoStream(
     std::pair<CallClient*, CallClient*> clients,
     VideoStreamConfig config) {
-  std::string quality_log_file_name;
+  std::unique_ptr<RtcEventLogOutput> quality_logger;
   if (config.analyzer.log_to_file)
-    quality_log_file_name =
-        GetFullPathOrEmpty(clients.first->name_ + ".video_quality.txt");
+    quality_logger = clients.first->GetLogWriter(".video_quality.txt");
   video_streams_.emplace_back(new VideoStreamPair(
-      clients.first, clients.second, config, quality_log_file_name));
+      clients.first, clients.second, config, std::move(quality_logger)));
   return video_streams_.back().get();
 }
 
@@ -364,7 +371,7 @@
       sim_clock_.AdvanceTimeMicroseconds(wait_time.us());
       // The fake clock is quite slow to update, we only update it if logging is
       // turned on to save time.
-      if (!base_filename_.empty())
+      if (!log_writer_factory_)
         event_log_fake_clock_.SetTimeNanos(sim_clock_.TimeInMicroseconds() *
                                            1000);
     }
diff --git a/test/scenario/scenario.h b/test/scenario/scenario.h
index f566840..c3fc877 100644
--- a/test/scenario/scenario.h
+++ b/test/scenario/scenario.h
@@ -14,8 +14,10 @@
 #include <utility>
 #include <vector>
 
+#include "absl/memory/memory.h"
 #include "rtc_base/constructor_magic.h"
 #include "rtc_base/fake_clock.h"
+#include "test/logging/log_writer.h"
 #include "test/scenario/audio_stream.h"
 #include "test/scenario/call_client.h"
 #include "test/scenario/column_printer.h"
@@ -63,6 +65,8 @@
   Scenario();
   explicit Scenario(std::string file_name);
   Scenario(std::string file_name, bool real_time);
+  Scenario(std::unique_ptr<LogWriterFactoryInterface> log_writer_manager,
+           bool real_time);
   RTC_DISALLOW_COPY_AND_ASSIGN(Scenario);
   ~Scenario();
 
@@ -163,15 +167,22 @@
   // Return the duration of the current session so far.
   TimeDelta Duration();
 
-  std::string GetFullPathOrEmpty(std::string name) const {
-    if (base_filename_.empty() || name.empty())
-      return std::string();
-    return base_filename_ + "." + name;
+  std::unique_ptr<RtcEventLogOutput> GetLogWriter(std::string name) {
+    if (!log_writer_factory_ || name.empty())
+      return nullptr;
+    return log_writer_factory_->Create(name);
+  }
+  std::unique_ptr<LogWriterFactoryInterface> GetLogWriterFactory(
+      std::string name) {
+    if (!log_writer_factory_ || name.empty())
+      return nullptr;
+    return absl::make_unique<LogWriterFactoryAddPrefix>(
+        log_writer_factory_.get(), name);
   }
 
  private:
   NullReceiver null_receiver_;
-  std::string base_filename_;
+  std::unique_ptr<LogWriterFactoryInterface> log_writer_factory_;
   const bool real_time_mode_;
   SimulatedClock sim_clock_;
   Clock* clock_;
diff --git a/test/scenario/simulated_time.cc b/test/scenario/simulated_time.cc
index 18dff32..af4acc3 100644
--- a/test/scenario/simulated_time.cc
+++ b/test/scenario/simulated_time.cc
@@ -245,7 +245,7 @@
 }
 
 SimulatedTimeClient::SimulatedTimeClient(
-    std::string log_filename,
+    std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
     SimulatedTimeClientConfig config,
     std::vector<PacketStreamConfig> stream_configs,
     std::vector<EmulatedNetworkNode*> send_link,
@@ -253,7 +253,8 @@
     uint64_t send_receiver_id,
     uint64_t return_receiver_id,
     Timestamp at_time)
-    : network_controller_factory_(log_filename, config.transport),
+    : log_writer_factory_(std::move(log_writer_factory)),
+      network_controller_factory_(log_writer_factory_.get(), config.transport),
       send_link_(send_link),
       return_link_(return_link),
       sender_(send_link.front(), send_receiver_id),
@@ -274,11 +275,10 @@
 
   CongestionProcess(at_time);
   network_controller_factory_.LogCongestionControllerStats(at_time);
-  if (!log_filename.empty()) {
-    std::string packet_log_name = log_filename + ".packets.txt";
-    packet_log_ = fopen(packet_log_name.c_str(), "w");
-    fprintf(packet_log_,
-            "transport_seq packet_size send_time recv_time feed_time\n");
+  if (log_writer_factory_) {
+    packet_log_ = log_writer_factory_->Create(".packets.txt");
+    packet_log_->Write(
+        "transport_seq packet_size send_time recv_time feed_time\n");
   }
 }
 
@@ -289,18 +289,17 @@
                                            packet.arrival_time);
   for (PacketResult& feedback : report.packet_feedbacks) {
     if (packet_log_)
-      fprintf(packet_log_, "%" PRId64 " %" PRId64 " %.3lf %.3lf %.3lf\n",
-              feedback.sent_packet.sequence_number,
-              feedback.sent_packet.size.bytes(),
-              feedback.sent_packet.send_time.seconds<double>(),
-              feedback.receive_time.seconds<double>(),
-              packet.arrival_time.seconds<double>());
+      LogWriteFormat(packet_log_.get(),
+                     "%" PRId64 " %" PRId64 " %.3lf %.3lf %.3lf\n",
+                     feedback.sent_packet.sequence_number,
+                     feedback.sent_packet.size.bytes(),
+                     feedback.sent_packet.send_time.seconds<double>(),
+                     feedback.receive_time.seconds<double>(),
+                     packet.arrival_time.seconds<double>());
   }
   Update(congestion_controller_->OnTransportPacketsFeedback(report));
 }
 SimulatedTimeClient::~SimulatedTimeClient() {
-  if (packet_log_)
-    fclose(packet_log_);
 }
 
 void SimulatedTimeClient::Update(NetworkControlUpdate update) {
diff --git a/test/scenario/simulated_time.h b/test/scenario/simulated_time.h
index a4cb70a..762748d 100644
--- a/test/scenario/simulated_time.h
+++ b/test/scenario/simulated_time.h
@@ -11,7 +11,6 @@
 #define TEST_SCENARIO_SIMULATED_TIME_H_
 
 #include <stdint.h>
-#include <stdio.h>
 #include <deque>
 #include <map>
 #include <memory>
@@ -25,6 +24,7 @@
 #include "api/units/time_delta.h"
 #include "api/units/timestamp.h"
 #include "rtc_base/copy_on_write_buffer.h"
+#include "test/logging/log_writer.h"
 #include "test/scenario/call_client.h"
 #include "test/scenario/network_node.h"
 #include "test/scenario/scenario_config.h"
@@ -120,14 +120,15 @@
 // a more accurate simulation, use the real time only CallClient.
 class SimulatedTimeClient : EmulatedNetworkReceiverInterface {
  public:
-  SimulatedTimeClient(std::string log_filename,
-                      SimulatedTimeClientConfig config,
-                      std::vector<PacketStreamConfig> stream_configs,
-                      std::vector<EmulatedNetworkNode*> send_link,
-                      std::vector<EmulatedNetworkNode*> return_link,
-                      uint64_t send_receiver_id,
-                      uint64_t return_receiver_id,
-                      Timestamp at_time);
+  SimulatedTimeClient(
+      std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
+      SimulatedTimeClientConfig config,
+      std::vector<PacketStreamConfig> stream_configs,
+      std::vector<EmulatedNetworkNode*> send_link,
+      std::vector<EmulatedNetworkNode*> return_link,
+      uint64_t send_receiver_id,
+      uint64_t return_receiver_id,
+      Timestamp at_time);
   SimulatedTimeClient(const SimulatedTimeClient&) = delete;
   ~SimulatedTimeClient();
   void Update(NetworkControlUpdate update);
@@ -144,6 +145,7 @@
 
  private:
   friend class Scenario;
+  std::unique_ptr<LogWriterFactoryInterface> log_writer_factory_;
   LoggingNetworkControllerFactory network_controller_factory_;
   std::unique_ptr<NetworkControllerInterface> congestion_controller_;
   std::vector<EmulatedNetworkNode*> send_link_;
@@ -153,7 +155,7 @@
   TargetRateConstraints current_contraints_;
   DataRate target_rate_ = DataRate::Infinity();
   DataRate link_capacity_ = DataRate::Infinity();
-  FILE* packet_log_ = nullptr;
+  std::unique_ptr<RtcEventLogOutput> packet_log_;
 
   std::vector<std::unique_ptr<PacketStream>> packet_streams_;
 };
diff --git a/test/scenario/video_stream.cc b/test/scenario/video_stream.cc
index d427a90..429c494 100644
--- a/test/scenario/video_stream.cc
+++ b/test/scenario/video_stream.cc
@@ -399,12 +399,14 @@
 
 VideoStreamPair::~VideoStreamPair() = default;
 
-VideoStreamPair::VideoStreamPair(CallClient* sender,
-                                 CallClient* receiver,
-                                 VideoStreamConfig config,
-                                 std::string quality_log_file_name)
+VideoStreamPair::VideoStreamPair(
+    CallClient* sender,
+    CallClient* receiver,
+    VideoStreamConfig config,
+    std::unique_ptr<RtcEventLogOutput> quality_writer)
     : config_(config),
-      analyzer_(quality_log_file_name, config.analyzer.frame_quality_handler),
+      analyzer_(std::move(quality_writer),
+                config.analyzer.frame_quality_handler),
       send_stream_(sender, config, &sender->transport_, &analyzer_),
       receive_stream_(receiver,
                       config,
diff --git a/test/scenario/video_stream.h b/test/scenario/video_stream.h
index 23994be..e38e30e 100644
--- a/test/scenario/video_stream.h
+++ b/test/scenario/video_stream.h
@@ -16,6 +16,7 @@
 #include "rtc_base/constructor_magic.h"
 #include "test/fake_encoder.h"
 #include "test/frame_generator_capturer.h"
+#include "test/logging/log_writer.h"
 #include "test/scenario/call_client.h"
 #include "test/scenario/column_printer.h"
 #include "test/scenario/network_node.h"
@@ -104,7 +105,7 @@
   VideoStreamPair(CallClient* sender,
                   CallClient* receiver,
                   VideoStreamConfig config,
-                  std::string quality_log_file_name);
+                  std::unique_ptr<RtcEventLogOutput> quality_writer);
 
   const VideoStreamConfig config_;