From 15c4a98bc7d17898c11e9951307c7f4bbe9e315d Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Mon, 21 Nov 2022 14:32:04 -0800 Subject: [PATCH] Gcp Observability Logging: Add handling for adding entry as a json payload (#31723) --- src/cpp/ext/filters/logging/BUILD | 2 +- src/cpp/ext/filters/logging/logging_sink.h | 19 +- src/cpp/ext/gcp/BUILD | 1 + src/cpp/ext/gcp/observability_logging_sink.cc | 141 +++- src/cpp/ext/gcp/observability_logging_sink.h | 4 + .../gcp/observability_logging_sink_test.cc | 749 ++++++++++++++++++ 6 files changed, 905 insertions(+), 11 deletions(-) diff --git a/src/cpp/ext/filters/logging/BUILD b/src/cpp/ext/filters/logging/BUILD index ad2a9803560..e23068b9e2c 100644 --- a/src/cpp/ext/filters/logging/BUILD +++ b/src/cpp/ext/filters/logging/BUILD @@ -35,7 +35,6 @@ grpc_cc_library( ], external_deps = [ "absl/strings", - "absl/time", ], language = "c++", visibility = [ @@ -44,5 +43,6 @@ grpc_cc_library( ], deps = [ "//:gpr_platform", + "//src/core:time", ], ) diff --git a/src/cpp/ext/filters/logging/logging_sink.h b/src/cpp/ext/filters/logging/logging_sink.h index 067ba1308d5..3eb57162b6a 100644 --- a/src/cpp/ext/filters/logging/logging_sink.h +++ b/src/cpp/ext/filters/logging/logging_sink.h @@ -27,7 +27,8 @@ #include #include "absl/strings/string_view.h" -#include "absl/time/time.h" + +#include "src/core/lib/gprpp/time.h" namespace grpc { namespace internal { @@ -72,11 +73,11 @@ class LoggingSink { struct Payload { std::map metadata; - absl::Duration duration; - uint32_t status_code; + grpc_core::Duration timeout; + uint32_t status_code = 0; std::string status_message; std::string status_details; - uint32_t message_length; + uint32_t message_length = 0; std::string message; }; @@ -87,12 +88,12 @@ class LoggingSink { uint32_t ip_port; }; - uint64_t call_id; - uint64_t sequence_id; - EventType type; - Logger logger; + uint64_t call_id = 0; + uint64_t sequence_id = 0; + EventType type = LoggingSink::Entry::EventType::kUnkown; + Logger logger = LoggingSink::Entry::Logger::kUnkown; Payload payload; - bool payload_truncated; + bool payload_truncated = false; Address peer; std::string authority; std::string service_name; diff --git a/src/cpp/ext/gcp/BUILD b/src/cpp/ext/gcp/BUILD index 0c28dd07a4c..fae1a04fa12 100644 --- a/src/cpp/ext/gcp/BUILD +++ b/src/cpp/ext/gcp/BUILD @@ -101,6 +101,7 @@ grpc_cc_library( ], external_deps = [ "absl/strings", + "absl/strings:str_format", "absl/types:optional", "googleapis_logging_grpc_service", ], diff --git a/src/cpp/ext/gcp/observability_logging_sink.cc b/src/cpp/ext/gcp/observability_logging_sink.cc index 999cb5003bb..16ab40a109e 100644 --- a/src/cpp/ext/gcp/observability_logging_sink.cc +++ b/src/cpp/ext/gcp/observability_logging_sink.cc @@ -25,6 +25,7 @@ #include #include +#include "absl/strings/str_format.h" #include "absl/types/optional.h" #include "google/logging/v2/logging.grpc.pb.h" @@ -98,7 +99,133 @@ LoggingSink::Config ObservabilityLoggingSink::FindMatch( return LoggingSink::Config(0, 0); } -void ObservabilityLoggingSink::LogEntry(Entry /* entry */) { +namespace { + +std::string EventTypeToString(LoggingSink::Entry::EventType type) { + switch (type) { + case LoggingSink::Entry::EventType::kClientHeader: + return "CLIENT_HEADER"; + case LoggingSink::Entry::EventType::kServerHeader: + return "SERVER_HEADER"; + case LoggingSink::Entry::EventType::kClientMessage: + return "CLIENT_MESSAGE"; + case LoggingSink::Entry::EventType::kServerMessage: + return "SERVER_MESSAGE"; + case LoggingSink::Entry::EventType::kClientHalfClose: + return "CLIENT_HALF_CLOSE"; + case LoggingSink::Entry::EventType::kServerTrailer: + return "SERVER_TRAILER"; + case LoggingSink::Entry::EventType::kCancel: + return "CANCEL"; + case LoggingSink::Entry::EventType::kUnkown: + default: + return "EVENT_TYPE_UNKNOWN"; + } +} + +std::string LoggerToString(LoggingSink::Entry::Logger type) { + switch (type) { + case LoggingSink::Entry::Logger::kClient: + return "CLIENT"; + case LoggingSink::Entry::Logger::kServer: + return "SERVER"; + case LoggingSink::Entry::Logger::kUnkown: + default: + return "LOGGER_UNKNOWN"; + } +} + +void PayloadToJsonStructProto(LoggingSink::Entry::Payload payload, + ::google::protobuf::Struct* payload_proto) { + grpc_core::Json::Object payload_json; + if (!payload.metadata.empty()) { + auto* metadata_proto = + (*payload_proto->mutable_fields())["metadata"].mutable_struct_value(); + for (auto& metadata : payload.metadata) { + (*metadata_proto->mutable_fields())[metadata.first].set_string_value( + std::move(metadata.second)); + } + } + if (payload.timeout != grpc_core::Duration::Zero()) { + (*payload_proto->mutable_fields())["timeout"].set_string_value( + payload.timeout.ToJsonString()); + } + if (payload.status_code != 0) { + (*payload_proto->mutable_fields())["statusCode"].set_number_value( + payload.status_code); + } + if (!payload.status_message.empty()) { + (*payload_proto->mutable_fields())["statusMessage"].set_string_value( + std::move(payload.status_message)); + } + if (!payload.status_details.empty()) { + (*payload_proto->mutable_fields())["statusDetails"].set_string_value( + std::move(payload.status_details)); + } + if (payload.message_length != 0) { + (*payload_proto->mutable_fields())["messageLength"].set_number_value( + payload.message_length); + } + if (!payload.message.empty()) { + (*payload_proto->mutable_fields())["message"].set_string_value( + std::move(payload.message)); + } +} + +std::string AddressTypeToString(LoggingSink::Entry::Address::Type type) { + switch (type) { + case LoggingSink::Entry::Address::Type::kIpv4: + return "TYPE_IPV4"; + case LoggingSink::Entry::Address::Type::kIpv6: + return "TYPE_IPV6"; + case LoggingSink::Entry::Address::Type::kUnix: + return "TYPE_UNIX"; + case LoggingSink::Entry::Address::Type::kUnknown: + default: + return "TYPE_UNKNOWN"; + } +} + +void PeerToJsonStructProto(LoggingSink::Entry::Address peer, + ::google::protobuf::Struct* peer_json) { + (*peer_json->mutable_fields())["type"].set_string_value( + AddressTypeToString(peer.type)); + (*peer_json->mutable_fields())["address"].set_string_value( + std::move(peer.address)); + (*peer_json->mutable_fields())["ipPort"].set_number_value(peer.ip_port); +} + +} // namespace + +void EntryToJsonStructProto(LoggingSink::Entry entry, + ::google::protobuf::Struct* json_payload) { + (*json_payload->mutable_fields())["callId"].set_string_value( + absl::StrCat(entry.call_id)); + (*json_payload->mutable_fields())["sequenceId"].set_number_value( + entry.sequence_id); + (*json_payload->mutable_fields())["type"].set_string_value( + EventTypeToString(entry.type)); + (*json_payload->mutable_fields())["logger"].set_string_value( + LoggerToString(entry.logger)); + PayloadToJsonStructProto( + std::move(entry.payload), + (*json_payload->mutable_fields())["payload"].mutable_struct_value()); + if (entry.payload_truncated) { + (*json_payload->mutable_fields())["payloadTruncated"].set_bool_value( + entry.payload_truncated); + } + PeerToJsonStructProto( + std::move(entry.peer), + (*json_payload->mutable_fields())["peer"].mutable_struct_value()); + (*json_payload->mutable_fields())["authority"].set_string_value( + std::move(entry.authority)); + (*json_payload->mutable_fields())["serviceName"].set_string_value( + std::move(entry.service_name)); + (*json_payload->mutable_fields())["methodName"].set_string_value( + std::move(entry.method_name)); +} + +void ObservabilityLoggingSink::LogEntry(Entry entry) { struct CallContext { ClientContext context; google::logging::v2::WriteLogEntriesRequest request; @@ -109,6 +236,18 @@ void ObservabilityLoggingSink::LogEntry(Entry /* entry */) { // context, and actually use the entry. CallContext* call = new CallContext; call->context.set_authority(authority_); + call->request.set_log_name( + absl::StrFormat("projects/{%s}/logs/" + "microservices.googleapis.com%%2Fobservability%%2fgrpc", + project_id_)); + auto* proto_entry = call->request.add_entries(); + // Fill the current timestamp + gpr_timespec timespec = + grpc_core::Timestamp::Now().as_timespec(GPR_CLOCK_REALTIME); + proto_entry->mutable_timestamp()->set_seconds(timespec.tv_sec); + proto_entry->mutable_timestamp()->set_nanos(timespec.tv_nsec); + // TODO(yashykt): Check if we need to fill receive timestamp + EntryToJsonStructProto(std::move(entry), proto_entry->mutable_json_payload()); stub_->async()->WriteLogEntries(&(call->context), &(call->request), &(call->response), [call](Status status) { if (!status.ok()) { diff --git a/src/cpp/ext/gcp/observability_logging_sink.h b/src/cpp/ext/gcp/observability_logging_sink.h index 7ca9463474c..36d676f261c 100644 --- a/src/cpp/ext/gcp/observability_logging_sink.h +++ b/src/cpp/ext/gcp/observability_logging_sink.h @@ -74,6 +74,10 @@ class ObservabilityLoggingSink : public LoggingSink { std::string authority_; }; +// Exposed for just for testing purposes +void EntryToJsonStructProto(LoggingSink::Entry entry, + ::google::protobuf::Struct* json_payload); + } // namespace internal } // namespace grpc diff --git a/test/cpp/ext/gcp/observability_logging_sink_test.cc b/test/cpp/ext/gcp/observability_logging_sink_test.cc index 98e2838538d..8aaf523991c 100644 --- a/test/cpp/ext/gcp/observability_logging_sink_test.cc +++ b/test/cpp/ext/gcp/observability_logging_sink_test.cc @@ -17,6 +17,7 @@ #include "src/cpp/ext/gcp/observability_logging_sink.h" #include "gmock/gmock.h" +#include "google/protobuf/text_format.h" #include "gtest/gtest.h" #include "test/core/util/test_config.h" @@ -294,6 +295,754 @@ TEST(GcpObservabilityLoggingSinkTest, LoggingConfigServerMultipleEventEntries) { EXPECT_EQ(sink.FindMatch(false, "foo/baz"), LoggingSink::Config(512, 2048)); } +TEST(EntryToJsonStructTest, ClientHeader) { + LoggingSink::Entry entry; + entry.call_id = 1234; + entry.sequence_id = 1; + entry.type = LoggingSink::Entry::EventType::kClientHeader; + entry.payload.metadata["key"] = "value"; + entry.payload.timeout = grpc_core::Duration::Seconds(100); + entry.payload_truncated = true; + entry.peer.type = LoggingSink::Entry::Address::Type::kIpv4; + entry.peer.address = "127.0.0.1"; + entry.peer.ip_port = 12345; + entry.authority = "authority"; + entry.service_name = "service_name"; + entry.method_name = "method_name"; + + google::protobuf::Struct proto; + EntryToJsonStructProto(std::move(entry), &proto); + std::string output; + ::google::protobuf::TextFormat::PrintToString(proto, &output); + const char* pb_str = + "fields {\n" + " key: \"authority\"\n" + " value {\n" + " string_value: \"authority\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"callId\"\n" + " value {\n" + " string_value: \"1234\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"logger\"\n" + " value {\n" + " string_value: \"LOGGER_UNKNOWN\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"methodName\"\n" + " value {\n" + " string_value: \"method_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payload\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"metadata\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"key\"\n" + " value {\n" + " string_value: \"value\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " fields {\n" + " key: \"timeout\"\n" + " value {\n" + " string_value: \"100.000000000s\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payloadTruncated\"\n" + " value {\n" + " bool_value: true\n" + " }\n" + "}\n" + "fields {\n" + " key: \"peer\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"address\"\n" + " value {\n" + " string_value: \"127.0.0.1\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"ipPort\"\n" + " value {\n" + " number_value: 12345\n" + " }\n" + " }\n" + " fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"TYPE_IPV4\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"sequenceId\"\n" + " value {\n" + " number_value: 1\n" + " }\n" + "}\n" + "fields {\n" + " key: \"serviceName\"\n" + " value {\n" + " string_value: \"service_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"CLIENT_HEADER\"\n" + " }\n" + "}\n"; + EXPECT_EQ(output, pb_str); +} + +TEST(EntryToJsonStructTest, ServerHeader) { + LoggingSink::Entry entry; + entry.call_id = 1234; + entry.sequence_id = 2; + entry.type = LoggingSink::Entry::EventType::kServerHeader; + entry.logger = LoggingSink::Entry::Logger::kServer; + entry.payload.metadata["key"] = "value"; + entry.peer.type = LoggingSink::Entry::Address::Type::kIpv4; + entry.peer.address = "127.0.0.1"; + entry.peer.ip_port = 1234; + entry.authority = "authority"; + entry.service_name = "service_name"; + entry.method_name = "method_name"; + + google::protobuf::Struct proto; + EntryToJsonStructProto(std::move(entry), &proto); + std::string output; + ::google::protobuf::TextFormat::PrintToString(proto, &output); + const char* pb_str = + "fields {\n" + " key: \"authority\"\n" + " value {\n" + " string_value: \"authority\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"callId\"\n" + " value {\n" + " string_value: \"1234\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"logger\"\n" + " value {\n" + " string_value: \"SERVER\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"methodName\"\n" + " value {\n" + " string_value: \"method_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payload\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"metadata\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"key\"\n" + " value {\n" + " string_value: \"value\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"peer\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"address\"\n" + " value {\n" + " string_value: \"127.0.0.1\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"ipPort\"\n" + " value {\n" + " number_value: 1234\n" + " }\n" + " }\n" + " fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"TYPE_IPV4\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"sequenceId\"\n" + " value {\n" + " number_value: 2\n" + " }\n" + "}\n" + "fields {\n" + " key: \"serviceName\"\n" + " value {\n" + " string_value: \"service_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"SERVER_HEADER\"\n" + " }\n" + "}\n"; + EXPECT_EQ(output, pb_str); +} + +TEST(EntryToJsonStructTest, ClientMessage) { + LoggingSink::Entry entry; + entry.call_id = 1234; + entry.sequence_id = 3; + entry.type = LoggingSink::Entry::EventType::kClientMessage; + entry.logger = LoggingSink::Entry::Logger::kClient; + entry.payload.message = "hello"; + entry.payload.message_length = 5; + entry.peer.type = LoggingSink::Entry::Address::Type::kIpv4; + entry.peer.address = "127.0.0.1"; + entry.peer.ip_port = 1234; + entry.authority = "authority"; + entry.service_name = "service_name"; + entry.method_name = "method_name"; + + google::protobuf::Struct proto; + EntryToJsonStructProto(std::move(entry), &proto); + std::string output; + ::google::protobuf::TextFormat::PrintToString(proto, &output); + const char* pb_str = + "fields {\n" + " key: \"authority\"\n" + " value {\n" + " string_value: \"authority\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"callId\"\n" + " value {\n" + " string_value: \"1234\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"logger\"\n" + " value {\n" + " string_value: \"CLIENT\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"methodName\"\n" + " value {\n" + " string_value: \"method_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payload\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"message\"\n" + " value {\n" + " string_value: \"hello\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"messageLength\"\n" + " value {\n" + " number_value: 5\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"peer\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"address\"\n" + " value {\n" + " string_value: \"127.0.0.1\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"ipPort\"\n" + " value {\n" + " number_value: 1234\n" + " }\n" + " }\n" + " fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"TYPE_IPV4\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"sequenceId\"\n" + " value {\n" + " number_value: 3\n" + " }\n" + "}\n" + "fields {\n" + " key: \"serviceName\"\n" + " value {\n" + " string_value: \"service_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"CLIENT_MESSAGE\"\n" + " }\n" + "}\n"; + EXPECT_EQ(output, pb_str); +} + +TEST(EntryToJsonStructTest, ServerMessage) { + LoggingSink::Entry entry; + entry.call_id = 1234; + entry.sequence_id = 4; + entry.type = LoggingSink::Entry::EventType::kServerMessage; + entry.logger = LoggingSink::Entry::Logger::kServer; + entry.payload.message = "world"; + entry.payload.message_length = 5; + entry.peer.type = LoggingSink::Entry::Address::Type::kIpv4; + entry.peer.address = "127.0.0.1"; + entry.peer.ip_port = 12345; + entry.authority = "authority"; + entry.service_name = "service_name"; + entry.method_name = "method_name"; + + google::protobuf::Struct proto; + EntryToJsonStructProto(std::move(entry), &proto); + std::string output; + ::google::protobuf::TextFormat::PrintToString(proto, &output); + const char* pb_str = + "fields {\n" + " key: \"authority\"\n" + " value {\n" + " string_value: \"authority\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"callId\"\n" + " value {\n" + " string_value: \"1234\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"logger\"\n" + " value {\n" + " string_value: \"SERVER\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"methodName\"\n" + " value {\n" + " string_value: \"method_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payload\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"message\"\n" + " value {\n" + " string_value: \"world\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"messageLength\"\n" + " value {\n" + " number_value: 5\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"peer\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"address\"\n" + " value {\n" + " string_value: \"127.0.0.1\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"ipPort\"\n" + " value {\n" + " number_value: 12345\n" + " }\n" + " }\n" + " fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"TYPE_IPV4\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"sequenceId\"\n" + " value {\n" + " number_value: 4\n" + " }\n" + "}\n" + "fields {\n" + " key: \"serviceName\"\n" + " value {\n" + " string_value: \"service_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"SERVER_MESSAGE\"\n" + " }\n" + "}\n"; + EXPECT_EQ(output, pb_str); +} + +TEST(EntryToJsonStructTest, ClientHalfClose) { + LoggingSink::Entry entry; + entry.call_id = 1234; + entry.sequence_id = 5; + entry.type = LoggingSink::Entry::EventType::kClientHalfClose; + entry.logger = LoggingSink::Entry::Logger::kClient; + entry.peer.type = LoggingSink::Entry::Address::Type::kIpv4; + entry.peer.address = "127.0.0.1"; + entry.peer.ip_port = 1234; + entry.authority = "authority"; + entry.service_name = "service_name"; + entry.method_name = "method_name"; + + google::protobuf::Struct proto; + EntryToJsonStructProto(std::move(entry), &proto); + std::string output; + ::google::protobuf::TextFormat::PrintToString(proto, &output); + const char* pb_str = + "fields {\n" + " key: \"authority\"\n" + " value {\n" + " string_value: \"authority\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"callId\"\n" + " value {\n" + " string_value: \"1234\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"logger\"\n" + " value {\n" + " string_value: \"CLIENT\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"methodName\"\n" + " value {\n" + " string_value: \"method_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payload\"\n" + " value {\n" + " struct_value {\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"peer\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"address\"\n" + " value {\n" + " string_value: \"127.0.0.1\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"ipPort\"\n" + " value {\n" + " number_value: 1234\n" + " }\n" + " }\n" + " fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"TYPE_IPV4\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"sequenceId\"\n" + " value {\n" + " number_value: 5\n" + " }\n" + "}\n" + "fields {\n" + " key: \"serviceName\"\n" + " value {\n" + " string_value: \"service_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"CLIENT_HALF_CLOSE\"\n" + " }\n" + "}\n"; + EXPECT_EQ(output, pb_str); +} + +TEST(EntryToJsonStructTest, ServerTrailer) { + LoggingSink::Entry entry; + entry.call_id = 1234; + entry.sequence_id = 6; + entry.type = LoggingSink::Entry::EventType::kServerTrailer; + entry.logger = LoggingSink::Entry::Logger::kServer; + entry.payload.metadata["key"] = "value"; + entry.peer.type = LoggingSink::Entry::Address::Type::kIpv4; + entry.peer.address = "127.0.0.1"; + entry.peer.ip_port = 1234; + entry.authority = "authority"; + entry.service_name = "service_name"; + entry.method_name = "method_name"; + + google::protobuf::Struct proto; + EntryToJsonStructProto(std::move(entry), &proto); + std::string output; + ::google::protobuf::TextFormat::PrintToString(proto, &output); + const char* pb_str = + "fields {\n" + " key: \"authority\"\n" + " value {\n" + " string_value: \"authority\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"callId\"\n" + " value {\n" + " string_value: \"1234\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"logger\"\n" + " value {\n" + " string_value: \"SERVER\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"methodName\"\n" + " value {\n" + " string_value: \"method_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payload\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"metadata\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"key\"\n" + " value {\n" + " string_value: \"value\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"peer\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"address\"\n" + " value {\n" + " string_value: \"127.0.0.1\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"ipPort\"\n" + " value {\n" + " number_value: 1234\n" + " }\n" + " }\n" + " fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"TYPE_IPV4\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"sequenceId\"\n" + " value {\n" + " number_value: 6\n" + " }\n" + "}\n" + "fields {\n" + " key: \"serviceName\"\n" + " value {\n" + " string_value: \"service_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"SERVER_TRAILER\"\n" + " }\n" + "}\n"; + EXPECT_EQ(output, pb_str); +} + +TEST(EntryToJsonStructTest, Cancel) { + LoggingSink::Entry entry; + entry.call_id = 1234; + entry.sequence_id = 7; + entry.type = LoggingSink::Entry::EventType::kCancel; + entry.logger = LoggingSink::Entry::Logger::kClient; + entry.peer.type = LoggingSink::Entry::Address::Type::kIpv4; + entry.peer.address = "127.0.0.1"; + entry.peer.ip_port = 1234; + entry.authority = "authority"; + entry.service_name = "service_name"; + entry.method_name = "method_name"; + + google::protobuf::Struct proto; + EntryToJsonStructProto(std::move(entry), &proto); + std::string output; + ::google::protobuf::TextFormat::PrintToString(proto, &output); + const char* pb_str = + "fields {\n" + " key: \"authority\"\n" + " value {\n" + " string_value: \"authority\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"callId\"\n" + " value {\n" + " string_value: \"1234\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"logger\"\n" + " value {\n" + " string_value: \"CLIENT\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"methodName\"\n" + " value {\n" + " string_value: \"method_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"payload\"\n" + " value {\n" + " struct_value {\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"peer\"\n" + " value {\n" + " struct_value {\n" + " fields {\n" + " key: \"address\"\n" + " value {\n" + " string_value: \"127.0.0.1\"\n" + " }\n" + " }\n" + " fields {\n" + " key: \"ipPort\"\n" + " value {\n" + " number_value: 1234\n" + " }\n" + " }\n" + " fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"TYPE_IPV4\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n" + "fields {\n" + " key: \"sequenceId\"\n" + " value {\n" + " number_value: 7\n" + " }\n" + "}\n" + "fields {\n" + " key: \"serviceName\"\n" + " value {\n" + " string_value: \"service_name\"\n" + " }\n" + "}\n" + "fields {\n" + " key: \"type\"\n" + " value {\n" + " string_value: \"CANCEL\"\n" + " }\n" + "}\n"; + EXPECT_EQ(output, pb_str); +} + } // namespace } // namespace internal