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

@ -22,6 +22,7 @@
#include <grpc/support/port_platform.h>
#include "absl/status/status.h"
#include "absl/time/time.h"
#include "src/core/lib/gprpp/debug_location.h"
@ -95,8 +96,12 @@ enum class StatusStrProperty {
kKey,
/// value associated with the error
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
@ -119,6 +124,14 @@ void StatusSetStr(absl::Status* status, StatusStrProperty key,
absl::optional<std::string> StatusGetStr(
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
void StatusAddChild(absl::Status* status, absl::Status child);

@ -20,6 +20,8 @@
#include <gtest/gtest.h>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/time/clock.h"
#include "google/rpc/status.upb.h"
#include "upb/upb.hpp"
@ -36,7 +38,7 @@ TEST(StatusUtilTest, CreateStatus) {
EXPECT_EQ(true, StatusGetStr(s, StatusStrProperty::kFile).has_value());
EXPECT_EQ(true, StatusGetInt(s, StatusIntProperty::kFileLine).has_value());
#endif
EXPECT_EQ(true, StatusGetStr(s, StatusStrProperty::kCreatedTime).has_value());
EXPECT_EQ(true, StatusGetTime(s, StatusTimeProperty::kCreated).has_value());
EXPECT_THAT(StatusGetChildren(s),
::testing::ElementsAre(absl::CancelledError()));
}
@ -65,6 +67,19 @@ TEST(StatusUtilTest, GetStrNotExistent) {
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) {
absl::Status s = absl::CancelledError();
absl::Status child1 = absl::AbortedError("Message1");
@ -96,11 +111,28 @@ TEST(StatusUtilTest, CancelledErrorToString) {
EXPECT_EQ("CANCELLED", t);
}
TEST(StatusUtilTest, ComplexErrorToString) {
TEST(StatusUtilTest, ErrorWithIntPropertyToString) {
absl::Status s = absl::CancelledError("Message");
StatusSetInt(&s, StatusIntProperty::kErrorNo, 2021);
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) {
@ -113,7 +145,7 @@ TEST(StatusUtilTest, ComplexErrorWithChildrenToString) {
StatusAddChild(&s, s2);
std::string t = StatusToString(s);
EXPECT_EQ(
"CANCELLED:Message {errno:\"2021\", children:["
"CANCELLED:Message {errno:2021, children:["
"ABORTED:Message1, ALREADY_EXISTS:Message2 {os_error:\"value\"}]}",
t);
}

Loading…
Cancel
Save