pw_log_sink: Switch backing sink to multisink

Change-Id: I954a4c777d5c874a5174fca80e16093ce393d468
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/38401
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Prashanth Swaminathan <prashanthsw@google.com>
diff --git a/pw_log_sink/BUILD b/pw_log_sink/BUILD
index 22334a7..19306ab 100644
--- a/pw_log_sink/BUILD
+++ b/pw_log_sink/BUILD
@@ -29,14 +29,16 @@
     deps = [
         "//pw_bytes",
         "//pw_log:facade",
-        "//pw_log_multisink:log_queue",
+        "//pw_multisink",
         "//pw_preprocessor",
         "//pw_status",
         "//pw_sync:interrupt_spin_lock",
     ],
     hdrs = [
         "public/pw_log_sink/log_sink.h",
-        "public_overrides/pw_log_backend/log_backend.h"
+        "public/pw_log_sink/multisink_adapter.h",
+        "public/pw_log_sink/sink.h",
+        "public_overrides/pw_log_backend/log_backend.h",
     ]
 )
 
diff --git a/pw_log_sink/BUILD.gn b/pw_log_sink/BUILD.gn
index cb2c330..d7c6485 100644
--- a/pw_log_sink/BUILD.gn
+++ b/pw_log_sink/BUILD.gn
@@ -34,13 +34,15 @@
   ]
   public = [
     "public/pw_log_sink/log_sink.h",
+    "public/pw_log_sink/multisink_adapter.h",
+    "public/pw_log_sink/sink.h",
     "public_overrides/pw_log_backend/log_backend.h",
   ]
   sources = [ "log_sink.cc" ]
   public_deps = [
     "$dir_pw_bytes",
     "$dir_pw_log:facade",
-    "$dir_pw_log_multisink:log_queue",
+    "$dir_pw_multisink",
     "$dir_pw_preprocessor",
     "$dir_pw_status",
   ]
diff --git a/pw_log_sink/log_sink_test.cc b/pw_log_sink/log_sink_test.cc
index 3c58303..52f8bbe 100644
--- a/pw_log_sink/log_sink_test.cc
+++ b/pw_log_sink/log_sink_test.cc
@@ -19,6 +19,9 @@
 #include "gtest/gtest.h"
 #include "pw_log/levels.h"
 #include "pw_log_proto/log.pwpb.h"
+#include "pw_log_sink/multisink_adapter.h"
+#include "pw_multisink/drain.h"
+#include "pw_multisink/multisink.h"
 #include "pw_protobuf/decoder.h"
 
 namespace pw::log_sink {
@@ -26,37 +29,19 @@
 constexpr size_t kMaxTokenizedMessageSize = 512;
 constexpr char kTokenizedMessage[] = "Test Message";
 
-class TestSink : public Sink {
- public:
-  void HandleEntry(ConstByteSpan message) {
-    pw::protobuf::Decoder log_decoder(message);
-    std::string_view log_entry_message;
+std::string LogMessageToString(ConstByteSpan message) {
+  pw::protobuf::Decoder log_decoder(message);
+  std::string_view log_entry_message;
 
-    EXPECT_TRUE(log_decoder.Next().ok());  // line_level - 2
-    EXPECT_TRUE(log_decoder.Next().ok());  // flags - 3
-    EXPECT_TRUE(log_decoder.Next().ok());  // timestamp - 5
-    EXPECT_TRUE(log_decoder.Next().ok());  // message_string - 16
-    EXPECT_EQ(16U, log_decoder.FieldNumber());
-    EXPECT_TRUE(log_decoder.ReadString(&log_entry_message).ok());
+  EXPECT_TRUE(log_decoder.Next().ok());  // line_level - 2
+  EXPECT_TRUE(log_decoder.Next().ok());  // flags - 3
+  EXPECT_TRUE(log_decoder.Next().ok());  // timestamp - 5
+  EXPECT_TRUE(log_decoder.Next().ok());  // message_string - 16
+  EXPECT_EQ(16U, log_decoder.FieldNumber());
+  EXPECT_TRUE(log_decoder.ReadString(&log_entry_message).ok());
 
-    last_message_string_ = log_entry_message;
-    message_count_++;
-  }
-
-  void HandleDropped(size_t count) { drop_count_ += count; }
-
-  void VerifyMessage(std::string tokenized_message) {
-    EXPECT_EQ(tokenized_message, last_message_string_);
-  }
-
-  size_t GetMessageCount() { return message_count_; }
-  size_t GetDropCount() { return drop_count_; }
-
- private:
-  std::string last_message_string_;
-  size_t message_count_ = 0;
-  size_t drop_count_ = 0;
-};
+  return std::string(log_entry_message);
+}
 
 void LogMessageForTest(const char* message) {
   pw_LogSink_Log(0, 0, nullptr, nullptr, 0, nullptr, "%s", message);
@@ -70,6 +55,28 @@
   pw_LogSink_Log(0, 0, nullptr, nullptr, 0, nullptr, "%s", long_message);
 }
 
+class TestSink final : public Sink {
+ public:
+  void HandleEntry(ConstByteSpan message) final {
+    last_message_string_ = LogMessageToString(message);
+    message_count_++;
+  }
+
+  void HandleDropped(uint32_t count) final { drop_count_ += count; }
+
+  void VerifyMessage(std::string tokenized_message) {
+    EXPECT_EQ(tokenized_message, last_message_string_);
+  }
+
+  uint32_t GetMessageCount() { return message_count_; }
+  uint32_t GetDropCount() { return drop_count_; }
+
+ private:
+  std::string last_message_string_;
+  uint32_t message_count_ = 0;
+  uint32_t drop_count_ = 0;
+};
+
 }  // namespace
 
 TEST(LogSink, NoSink) {
@@ -126,4 +133,25 @@
   RemoveSink(test_sink);
 }
 
+TEST(LogSink, MultiSinkAdapter) {
+  constexpr size_t kMultiSinkBufferSize = 1024;
+  std::byte buffer[kMultiSinkBufferSize];
+  std::byte entry_buffer[kMultiSinkBufferSize];
+  pw::multisink::MultiSink multisink(buffer);
+  pw::multisink::Drain drain;
+  MultiSinkAdapter multisink_adapter(multisink);
+
+  multisink.AttachDrain(drain);
+  LogMessageForTest(kTokenizedMessage);
+
+  AddSink(multisink_adapter);
+  LogMessageForTest(kTokenizedMessage);
+
+  uint32_t drop_count = 0;
+  Result<ConstByteSpan> entry = drain.GetEntry(entry_buffer, drop_count);
+  ASSERT_TRUE(entry.ok());
+  EXPECT_EQ(LogMessageToString(entry.value()), kTokenizedMessage);
+  EXPECT_EQ(drop_count, 1U);
+}
+
 }  // namespace pw::log_sink
diff --git a/pw_log_sink/public/pw_log_sink/log_sink.h b/pw_log_sink/public/pw_log_sink/log_sink.h
index fbed200..648f5c2 100644
--- a/pw_log_sink/public/pw_log_sink/log_sink.h
+++ b/pw_log_sink/public/pw_log_sink/log_sink.h
@@ -55,7 +55,7 @@
 #include <string_view>
 
 #include "pw_bytes/span.h"
-#include "pw_log_multisink/sink.h"
+#include "pw_log_sink/sink.h"
 
 namespace pw::log_sink {
 
diff --git a/pw_log_sink/public/pw_log_sink/multisink_adapter.h b/pw_log_sink/public/pw_log_sink/multisink_adapter.h
new file mode 100644
index 0000000..af45cb2
--- /dev/null
+++ b/pw_log_sink/public/pw_log_sink/multisink_adapter.h
@@ -0,0 +1,45 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_log_sink/sink.h"
+#include "pw_multisink/multisink.h"
+
+namespace pw {
+namespace log_sink {
+
+class MultiSinkAdapter final : public Sink {
+ public:
+  MultiSinkAdapter(multisink::MultiSink& multisink) : multisink_(multisink) {}
+
+  // Write an entry to the sink.
+  void HandleEntry(ConstByteSpan entry) final {
+    // Best-effort attempt to send data to the sink, so status is ignored. The
+    // multisink handles failures internally and propagates them to its drains.
+    multisink_.HandleEntry(entry);
+  }
+
+  // Notifies the sink of messages dropped before ingress. The writer may use
+  // this to signal to sinks that an entry (or entries) was lost before sending
+  // to the sink (e.g. the log sink failed to encode the message).
+  void HandleDropped(uint32_t drop_count) final {
+    multisink_.HandleDropped(drop_count);
+  }
+
+ private:
+  multisink::MultiSink& multisink_;
+};
+
+}  // namespace log_sink
+}  // namespace pw
diff --git a/pw_log_sink/public/pw_log_sink/sink.h b/pw_log_sink/public/pw_log_sink/sink.h
new file mode 100644
index 0000000..a85b5b5
--- /dev/null
+++ b/pw_log_sink/public/pw_log_sink/sink.h
@@ -0,0 +1,40 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+// TODO(pwbug.dev/351): This file is very similar to pw_log_multisink/sink.h,
+// which will be deprecated in a later change.
+
+#include "pw_bytes/span.h"
+#include "pw_containers/intrusive_list.h"
+
+namespace pw {
+namespace log_sink {
+
+class Sink : public IntrusiveList<Sink>::Item {
+ public:
+  virtual ~Sink() = default;
+
+  // Write an entry to the sink. This is a best-effort attempt to send data to
+  // the sink, so failures are ignored.
+  virtual void HandleEntry(ConstByteSpan entry) = 0;
+
+  // Notifies the sink of messages dropped before ingress. The writer may use
+  // this to signal to sinks that an entry (or entries) was lost before sending
+  // to the sink (e.g. the log sink failed to encode the message).
+  virtual void HandleDropped(uint32_t drop_count) = 0;
+};
+
+}  // namespace log_sink
+}  // namespace pw