Add time property to status_helper (#26099)

pull/26114/head^2
Esun Kim 4 years ago committed by GitHub
parent 81276fff2e
commit 7977494d02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 153
      src/core/lib/gprpp/status_helper.cc
  2. 17
      src/core/lib/gprpp/status_helper.h
  3. 40
      test/core/gprpp/status_helper_test.cc

@ -25,8 +25,10 @@
#include "absl/strings/cord.h" #include "absl/strings/cord.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/strings/match.h"
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include "absl/time/clock.h"
#include "google/protobuf/any.upb.h" #include "google/protobuf/any.upb.h"
#include "google/rpc/status.upb.h" #include "google/rpc/status.upb.h"
@ -37,42 +39,50 @@ namespace grpc_core {
namespace { namespace {
#define TYPE_URL_PREFIX "type.googleapis.com/grpc.status." #define TYPE_URL_PREFIX "type.googleapis.com/grpc.status."
#define TYPE_INT_TAG "int."
#define TYPE_STR_TAG "str."
#define TYPE_TIME_TAG "time."
#define TYPE_CHILDREN_TAG "children"
#define TYPE_URL(name) (TYPE_URL_PREFIX name) #define TYPE_URL(name) (TYPE_URL_PREFIX name)
const absl::string_view kTypeUrlPrefix = TYPE_URL_PREFIX; const absl::string_view kTypeUrlPrefix = TYPE_URL_PREFIX;
const absl::string_view kChildrenPropertyUrl = TYPE_URL("children"); const absl::string_view kTypeIntTag = TYPE_INT_TAG;
const absl::string_view kTypeStrTag = TYPE_STR_TAG;
const absl::string_view kTypeTimeTag = TYPE_TIME_TAG;
const absl::string_view kTypeChildrenTag = TYPE_CHILDREN_TAG;
const absl::string_view kChildrenPropertyUrl = TYPE_URL(TYPE_CHILDREN_TAG);
const char* GetStatusIntPropertyUrl(StatusIntProperty key) { const char* GetStatusIntPropertyUrl(StatusIntProperty key) {
switch (key) { switch (key) {
case StatusIntProperty::kErrorNo: case StatusIntProperty::kErrorNo:
return TYPE_URL("errno"); return TYPE_URL(TYPE_INT_TAG "errno");
case StatusIntProperty::kFileLine: case StatusIntProperty::kFileLine:
return TYPE_URL("file_line"); return TYPE_URL(TYPE_INT_TAG "file_line");
case StatusIntProperty::kStreamId: case StatusIntProperty::kStreamId:
return TYPE_URL("stream_id"); return TYPE_URL(TYPE_INT_TAG "stream_id");
case StatusIntProperty::kRpcStatus: case StatusIntProperty::kRpcStatus:
return TYPE_URL("grpc_status"); return TYPE_URL(TYPE_INT_TAG "grpc_status");
case StatusIntProperty::kOffset: case StatusIntProperty::kOffset:
return TYPE_URL("offset"); return TYPE_URL(TYPE_INT_TAG "offset");
case StatusIntProperty::kIndex: case StatusIntProperty::kIndex:
return TYPE_URL("index"); return TYPE_URL(TYPE_INT_TAG "index");
case StatusIntProperty::kSize: case StatusIntProperty::kSize:
return TYPE_URL("size"); return TYPE_URL(TYPE_INT_TAG "size");
case StatusIntProperty::kHttp2Error: case StatusIntProperty::kHttp2Error:
return TYPE_URL("http2_error"); return TYPE_URL(TYPE_INT_TAG "http2_error");
case StatusIntProperty::kTsiCode: case StatusIntProperty::kTsiCode:
return TYPE_URL("tsi_code"); return TYPE_URL(TYPE_INT_TAG "tsi_code");
case StatusIntProperty::kWsaError: case StatusIntProperty::kWsaError:
return TYPE_URL("wsa_error"); return TYPE_URL(TYPE_INT_TAG "wsa_error");
case StatusIntProperty::kFd: case StatusIntProperty::kFd:
return TYPE_URL("fd"); return TYPE_URL(TYPE_INT_TAG "fd");
case StatusIntProperty::kHttpStatus: case StatusIntProperty::kHttpStatus:
return TYPE_URL("http_status"); return TYPE_URL(TYPE_INT_TAG "http_status");
case StatusIntProperty::kOccurredDuringWrite: case StatusIntProperty::kOccurredDuringWrite:
return TYPE_URL("occurred_during_write"); return TYPE_URL(TYPE_INT_TAG "occurred_during_write");
case StatusIntProperty::ChannelConnectivityState: case StatusIntProperty::ChannelConnectivityState:
return TYPE_URL("channel_connectivity_state"); return TYPE_URL(TYPE_INT_TAG "channel_connectivity_state");
case StatusIntProperty::kLbPolicyDrop: case StatusIntProperty::kLbPolicyDrop:
return TYPE_URL("lb_policy_drop"); return TYPE_URL(TYPE_INT_TAG "lb_policy_drop");
} }
GPR_UNREACHABLE_CODE(return "unknown"); GPR_UNREACHABLE_CODE(return "unknown");
} }
@ -80,29 +90,35 @@ const char* GetStatusIntPropertyUrl(StatusIntProperty key) {
const char* GetStatusStrPropertyUrl(StatusStrProperty key) { const char* GetStatusStrPropertyUrl(StatusStrProperty key) {
switch (key) { switch (key) {
case StatusStrProperty::kDescription: case StatusStrProperty::kDescription:
return TYPE_URL("description"); return TYPE_URL(TYPE_STR_TAG "description");
case StatusStrProperty::kFile: case StatusStrProperty::kFile:
return TYPE_URL("file"); return TYPE_URL(TYPE_STR_TAG "file");
case StatusStrProperty::kOsError: case StatusStrProperty::kOsError:
return TYPE_URL("os_error"); return TYPE_URL(TYPE_STR_TAG "os_error");
case StatusStrProperty::kSyscall: case StatusStrProperty::kSyscall:
return TYPE_URL("syscall"); return TYPE_URL(TYPE_STR_TAG "syscall");
case StatusStrProperty::kTargetAddress: case StatusStrProperty::kTargetAddress:
return TYPE_URL("target_address"); return TYPE_URL(TYPE_STR_TAG "target_address");
case StatusStrProperty::kGrpcMessage: case StatusStrProperty::kGrpcMessage:
return TYPE_URL("grpc_message"); return TYPE_URL(TYPE_STR_TAG "grpc_message");
case StatusStrProperty::kRawBytes: case StatusStrProperty::kRawBytes:
return TYPE_URL("raw_bytes"); return TYPE_URL(TYPE_STR_TAG "raw_bytes");
case StatusStrProperty::kTsiError: case StatusStrProperty::kTsiError:
return TYPE_URL("tsi_error"); return TYPE_URL(TYPE_STR_TAG "tsi_error");
case StatusStrProperty::kFilename: case StatusStrProperty::kFilename:
return TYPE_URL("filename"); return TYPE_URL(TYPE_STR_TAG "filename");
case StatusStrProperty::kKey: case StatusStrProperty::kKey:
return TYPE_URL("key"); return TYPE_URL(TYPE_STR_TAG "key");
case StatusStrProperty::kValue: case StatusStrProperty::kValue:
return TYPE_URL("value"); return TYPE_URL(TYPE_STR_TAG "value");
case StatusStrProperty::kCreatedTime: }
return TYPE_URL("created_time"); GPR_UNREACHABLE_CODE(return "unknown");
}
const char* GetStatusTimePropertyUrl(StatusTimeProperty key) {
switch (key) {
case StatusTimeProperty::kCreated:
return TYPE_URL(TYPE_TIME_TAG "created_time");
} }
GPR_UNREACHABLE_CODE(return "unknown"); GPR_UNREACHABLE_CODE(return "unknown");
} }
@ -150,8 +166,7 @@ absl::Status StatusCreate(absl::StatusCode code, absl::string_view msg,
if (location.line() != -1) { if (location.line() != -1) {
StatusSetInt(&s, StatusIntProperty::kFileLine, location.line()); StatusSetInt(&s, StatusIntProperty::kFileLine, location.line());
} }
absl::Time now = grpc_core::ToAbslTime(gpr_now(GPR_CLOCK_REALTIME)); StatusSetTime(&s, StatusTimeProperty::kCreated, absl::Now());
StatusSetStr(&s, StatusStrProperty::kCreatedTime, absl::FormatTime(now));
for (const absl::Status& child : children) { for (const absl::Status& child : children) {
if (!child.ok()) { if (!child.ok()) {
StatusAddChild(&s, child); StatusAddChild(&s, child);
@ -200,6 +215,29 @@ absl::optional<std::string> StatusGetStr(const absl::Status& status,
return {}; return {};
} }
void StatusSetTime(absl::Status* status, StatusTimeProperty key,
absl::Time time) {
status->SetPayload(GetStatusTimePropertyUrl(key),
absl::Cord(absl::string_view(
reinterpret_cast<const char*>(&time), sizeof(time))));
}
absl::optional<absl::Time> StatusGetTime(const absl::Status& status,
StatusTimeProperty key) {
absl::optional<absl::Cord> p =
status.GetPayload(GetStatusTimePropertyUrl(key));
if (p.has_value()) {
absl::optional<absl::string_view> sv = p->TryFlat();
if (sv.has_value()) {
return *reinterpret_cast<const absl::Time*>(sv->data());
} else {
std::string s = std::string(*p);
return *reinterpret_cast<const absl::Time*>(s.c_str());
}
}
return {};
}
void StatusAddChild(absl::Status* status, absl::Status child) { void StatusAddChild(absl::Status* status, absl::Status child) {
upb::Arena arena; upb::Arena arena;
// Serialize msg to buf // Serialize msg to buf
@ -237,20 +275,45 @@ std::string StatusToString(const absl::Status& status) {
} }
std::vector<std::string> kvs; std::vector<std::string> kvs;
absl::optional<absl::Cord> children; absl::optional<absl::Cord> children;
status.ForEachPayload( status.ForEachPayload([&](absl::string_view type_url,
[&](absl::string_view type_url, const absl::Cord& payload) { const absl::Cord& payload) {
if (type_url.substr(0, kTypeUrlPrefix.size()) == kTypeUrlPrefix) { if (absl::StartsWith(type_url, kTypeUrlPrefix)) {
type_url.remove_prefix(kTypeUrlPrefix.size()); type_url.remove_prefix(kTypeUrlPrefix.size());
} if (type_url == kTypeChildrenTag) {
if (type_url == kChildrenPropertyUrl.substr(kTypeUrlPrefix.size())) { children = payload;
children = payload; return;
} else { }
absl::optional<absl::string_view> payload_view = payload.TryFlat(); absl::string_view payload_view;
std::string payload_str = absl::CHexEscape( std::string payload_storage;
payload_view.has_value() ? *payload_view : std::string(payload)); if (payload.TryFlat().has_value()) {
kvs.push_back(absl::StrCat(type_url, ":\"", payload_str, "\"")); payload_view = payload.TryFlat().value();
} } else {
}); payload_storage = std::string(payload);
payload_view = payload_storage;
}
if (absl::StartsWith(type_url, kTypeIntTag)) {
type_url.remove_prefix(kTypeIntTag.size());
kvs.push_back(absl::StrCat(type_url, ":", payload_view));
} else if (absl::StartsWith(type_url, kTypeStrTag)) {
type_url.remove_prefix(kTypeStrTag.size());
kvs.push_back(absl::StrCat(type_url, ":\"",
absl::CHexEscape(payload_view), "\""));
} else if (absl::StartsWith(type_url, kTypeTimeTag)) {
type_url.remove_prefix(kTypeTimeTag.size());
absl::Time t =
*reinterpret_cast<const absl::Time*>(payload_view.data());
kvs.push_back(absl::StrCat(type_url, ":\"", absl::FormatTime(t), "\""));
} else {
kvs.push_back(absl::StrCat(type_url, ":\"",
absl::CHexEscape(payload_view), "\""));
}
} else {
absl::optional<absl::string_view> payload_view = payload.TryFlat();
std::string payload_str = absl::CHexEscape(
payload_view.has_value() ? *payload_view : std::string(payload));
kvs.push_back(absl::StrCat(type_url, ":\"", payload_str, "\""));
}
});
if (children.has_value()) { if (children.has_value()) {
std::vector<absl::Status> children_status = ParseChildren(*children); std::vector<absl::Status> children_status = ParseChildren(*children);
std::vector<std::string> children_text; std::vector<std::string> children_text;

@ -22,6 +22,7 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#include "absl/status/status.h" #include "absl/status/status.h"
#include "absl/time/time.h"
#include "src/core/lib/gprpp/debug_location.h" #include "src/core/lib/gprpp/debug_location.h"
@ -95,8 +96,12 @@ enum class StatusStrProperty {
kKey, kKey,
/// value associated with the error /// value associated with the error
kValue, kValue,
/// time string to create the error };
kCreatedTime,
/// This enum should have the same value of grpc_error_times
enum class StatusTimeProperty {
/// timestamp of error creation
kCreated,
}; };
/// Creates a status with given additional information /// Creates a status with given additional information
@ -119,6 +124,14 @@ void StatusSetStr(absl::Status* status, StatusStrProperty key,
absl::optional<std::string> StatusGetStr( absl::optional<std::string> StatusGetStr(
const absl::Status& status, StatusStrProperty key) GRPC_MUST_USE_RESULT; const absl::Status& status, StatusStrProperty key) GRPC_MUST_USE_RESULT;
/// Sets the time property to the status
void StatusSetTime(absl::Status* status, StatusTimeProperty key,
absl::Time time);
/// Gets the time property from the status
absl::optional<absl::Time> StatusGetTime(
const absl::Status& status, StatusTimeProperty key) GRPC_MUST_USE_RESULT;
/// Adds a child status to status /// Adds a child status to status
void StatusAddChild(absl::Status* status, absl::Status child); void StatusAddChild(absl::Status* status, absl::Status child);

@ -20,6 +20,8 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "absl/status/status.h" #include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/time/clock.h"
#include "google/rpc/status.upb.h" #include "google/rpc/status.upb.h"
#include "upb/upb.hpp" #include "upb/upb.hpp"
@ -36,7 +38,7 @@ TEST(StatusUtilTest, CreateStatus) {
EXPECT_EQ(true, StatusGetStr(s, StatusStrProperty::kFile).has_value()); EXPECT_EQ(true, StatusGetStr(s, StatusStrProperty::kFile).has_value());
EXPECT_EQ(true, StatusGetInt(s, StatusIntProperty::kFileLine).has_value()); EXPECT_EQ(true, StatusGetInt(s, StatusIntProperty::kFileLine).has_value());
#endif #endif
EXPECT_EQ(true, StatusGetStr(s, StatusStrProperty::kCreatedTime).has_value()); EXPECT_EQ(true, StatusGetTime(s, StatusTimeProperty::kCreated).has_value());
EXPECT_THAT(StatusGetChildren(s), EXPECT_THAT(StatusGetChildren(s),
::testing::ElementsAre(absl::CancelledError())); ::testing::ElementsAre(absl::CancelledError()));
} }
@ -65,6 +67,19 @@ TEST(StatusUtilTest, GetStrNotExistent) {
StatusGetStr(s, StatusStrProperty::kOsError)); StatusGetStr(s, StatusStrProperty::kOsError));
} }
TEST(StatusUtilTest, SetAndGetTime) {
absl::Status s = absl::CancelledError();
absl::Time t = absl::Now();
StatusSetTime(&s, StatusTimeProperty::kCreated, t);
EXPECT_EQ(t, StatusGetTime(s, StatusTimeProperty::kCreated));
}
TEST(StatusUtilTest, GetTimeNotExistent) {
absl::Status s = absl::CancelledError();
EXPECT_EQ(absl::optional<absl::Time>(),
StatusGetTime(s, StatusTimeProperty::kCreated));
}
TEST(StatusUtilTest, AddAndGetChildren) { TEST(StatusUtilTest, AddAndGetChildren) {
absl::Status s = absl::CancelledError(); absl::Status s = absl::CancelledError();
absl::Status child1 = absl::AbortedError("Message1"); absl::Status child1 = absl::AbortedError("Message1");
@ -96,11 +111,28 @@ TEST(StatusUtilTest, CancelledErrorToString) {
EXPECT_EQ("CANCELLED", t); EXPECT_EQ("CANCELLED", t);
} }
TEST(StatusUtilTest, ComplexErrorToString) { TEST(StatusUtilTest, ErrorWithIntPropertyToString) {
absl::Status s = absl::CancelledError("Message"); absl::Status s = absl::CancelledError("Message");
StatusSetInt(&s, StatusIntProperty::kErrorNo, 2021); StatusSetInt(&s, StatusIntProperty::kErrorNo, 2021);
std::string t = StatusToString(s); std::string t = StatusToString(s);
EXPECT_EQ("CANCELLED:Message {errno:\"2021\"}", t); EXPECT_EQ("CANCELLED:Message {errno:2021}", t);
}
TEST(StatusUtilTest, ErrorWithStrPropertyToString) {
absl::Status s = absl::CancelledError("Message");
StatusSetStr(&s, StatusStrProperty::kDescription, "Hey");
std::string t = StatusToString(s);
EXPECT_EQ("CANCELLED:Message {description:\"Hey\"}", t);
}
TEST(StatusUtilTest, ErrorWithTimePropertyToString) {
absl::Status s = absl::CancelledError("Message");
absl::Time t = absl::FromCivil(absl::CivilSecond(2021, 4, 29, 8, 56, 30),
absl::LocalTimeZone());
StatusSetTime(&s, StatusTimeProperty::kCreated, t);
EXPECT_EQ(StatusToString(s),
absl::StrCat("CANCELLED:Message {created_time:\"",
absl::FormatTime(t), "\"}"));
} }
TEST(StatusUtilTest, ComplexErrorWithChildrenToString) { TEST(StatusUtilTest, ComplexErrorWithChildrenToString) {
@ -113,7 +145,7 @@ TEST(StatusUtilTest, ComplexErrorWithChildrenToString) {
StatusAddChild(&s, s2); StatusAddChild(&s, s2);
std::string t = StatusToString(s); std::string t = StatusToString(s);
EXPECT_EQ( EXPECT_EQ(
"CANCELLED:Message {errno:\"2021\", children:[" "CANCELLED:Message {errno:2021, children:["
"ABORTED:Message1, ALREADY_EXISTS:Message2 {os_error:\"value\"}]}", "ABORTED:Message1, ALREADY_EXISTS:Message2 {os_error:\"value\"}]}",
t); t);
} }

Loading…
Cancel
Save