Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose and log ErrorInfo if present #7640

Merged
merged 5 commits into from
Nov 20, 2021
Merged

Conversation

devjgm
Copy link
Contributor

@devjgm devjgm commented Nov 19, 2021

Fixes: #7429

This PR adds a google::cloud::ErrorInfo class, which mirrors
ErrorInfo, and can also work with an equivalent
type in a REST world. We expose programmatic access to this via the
g::c::Status::error_info() accessor. We also log this data when
streaming a g::c::Status if it's present.

As of today, very few (if any) GCP services actually return this proto,
so I don't expect to see it much yet.

Alternative options:

  • We could move ErrorInfo into it's own error_info.h file. It's pretty closely related to Status, so I'm OK w/ it where it is, but I'm also happy to move it if people think that's preferable.
  • We can discuss the logging format that we want to use when streaming the ErrorInfo. I chose something in this PR, but we can certainly consider other output formats.

This change is Reviewable

@google-cla google-cla bot added the cla: yes This human has signed the Contributor License Agreement. label Nov 19, 2021
@devjgm devjgm changed the title Error info feat: expose and log ErrorInfo if present Nov 19, 2021
@google-cloud-cpp-bot
Copy link
Collaborator

Google Cloud Build Logs
For commit: 7c298c06267c2f39abb635e5d1a06b889ad33973

ℹ️ NOTE: Kokoro logs are linked from "Details" below.

@codecov
Copy link

codecov bot commented Nov 20, 2021

Codecov Report

Merging #7640 (f32a124) into main (d1ef166) will decrease coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #7640      +/-   ##
==========================================
- Coverage   95.28%   95.28%   -0.01%     
==========================================
  Files        1254     1254              
  Lines      113126   113226     +100     
==========================================
+ Hits       107793   107885      +92     
- Misses       5333     5341       +8     
Impacted Files Coverage Δ
google/cloud/grpc_error_delegate.cc 93.50% <100.00%> (+1.44%) ⬆️
google/cloud/grpc_error_delegate_test.cc 100.00% <100.00%> (ø)
google/cloud/status.cc 100.00% <100.00%> (ø)
google/cloud/status.h 100.00% <100.00%> (ø)
google/cloud/status_test.cc 100.00% <100.00%> (ø)
...ogle/cloud/internal/extract_long_running_result.cc 85.71% <0.00%> (-14.29%) ⬇️
google/cloud/bigtable/internal/common_client.h 94.02% <0.00%> (-5.98%) ⬇️
google/cloud/bigtable/internal/common_client.cc 95.71% <0.00%> (-1.43%) ⬇️
google/cloud/pubsub/subscriber_connection_test.cc 97.18% <0.00%> (-0.71%) ⬇️
google/cloud/pubsub/samples/samples.cc 92.10% <0.00%> (+0.07%) ⬆️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d1ef166...f32a124. Read the comment docs.

@devjgm devjgm marked this pull request as ready for review November 20, 2021 13:30
@devjgm devjgm requested a review from a team as a code owner November 20, 2021 13:30
Copy link
Contributor

@coryan coryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few last-minute nits, sorry about that. I think we should keep #7429 open until the storage library also supports ErrorInfo? We can fix that in a separate PR, of course.

Reviewed 7 of 7 files at r1, all commit messages.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @devjgm)


google/cloud/grpc_error_delegate.cc, line 73 at r1 (raw file):

    google::rpc::Status const& proto) {
  for (google::protobuf::Any const& any : proto.details()) {
    if (any.Is<google::rpc::ErrorInfo>()) {

nit: what happens if there is more than one ErrorInfo? Can you include a comment saying why that is not a problem?


google/cloud/grpc_error_delegate.cc, line 81 at r1 (raw file):

}

void FillErrorInfo(google::rpc::Status const& status, ErrorInfo& error_info) {

Why the output argument? Can we just return error_info?


google/cloud/grpc_error_delegate.cc, line 122 at r1 (raw file):

  }
  ErrorInfo error_info;
  FillErrorInfo(proto, error_info);

For a future PR: I think we should do the same thing for GCS, at least here:

Status AsStatus(HttpResponse const& http_response) {

Something like:

Status AsStatus(HttpResponse const& http_response) {
  if (HttpStatusCode::kMinContinue <= http_response.status_code &&
      http_response.status_code < HttpStatusCode::kMinSuccess) {
    // We treat the 100s (e.g. 100 Continue) as OK results. They normally are
    // ignored by libcurl, so we do not really expect to see them.
    return Status{};
  }
  if (HttpStatusCode::kMinSuccess <= http_response.status_code &&
      http_response.status_code < HttpStatusCode::kMinRedirects) {
    // We treat the 200s as Okay results.
    return Status{};
  }
  // Parse the payload as a JSON object, see https://cloud.google.com/apis/design/errors#http_mapping
  auto parsed = nlohmann::json::parse(http_response.payload, nullptr, false);
  ErrorInfo error_info = ExtractErrorInfo(parsed);
  return Status(/* map HTTP code */, parsed.value("message", ""), error_info);

google/cloud/status.h, line 70 at r1 (raw file):

 *
 * @see
 * https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto

nit: I would use https://cloud.google.com/apis/design/errors#error_info


google/cloud/status.cc, line 159 at r1 (raw file):

  auto const& error_info = s.error_info();
  if (!error_info.reason().empty()) {
    os << "\n    reason: " << error_info.reason();

Can we print the error in a single line (modulo the contents having \n in them)? It is super annoying to have multi-line error messages when using grep to parse them. Maybe format the ErrorInfo as as single-line JSON string, using the mapping from: https://cloud.google.com/apis/design/errors#http_mapping ?

Copy link
Contributor Author

@devjgm devjgm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: all files reviewed, 4 unresolved discussions (waiting on @coryan)


google/cloud/grpc_error_delegate.cc, line 73 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

nit: what happens if there is more than one ErrorInfo? Can you include a comment saying why that is not a problem?

Done.


google/cloud/grpc_error_delegate.cc, line 81 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

Why the output argument? Can we just return error_info?

Just me being midway through a refactoring and not completing it. Oops. Yes, of course returning by value is better. Fixed.


google/cloud/grpc_error_delegate.cc, line 122 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

For a future PR: I think we should do the same thing for GCS, at least here:

Status AsStatus(HttpResponse const& http_response) {

Something like:

Status AsStatus(HttpResponse const& http_response) {
  if (HttpStatusCode::kMinContinue <= http_response.status_code &&
      http_response.status_code < HttpStatusCode::kMinSuccess) {
    // We treat the 100s (e.g. 100 Continue) as OK results. They normally are
    // ignored by libcurl, so we do not really expect to see them.
    return Status{};
  }
  if (HttpStatusCode::kMinSuccess <= http_response.status_code &&
      http_response.status_code < HttpStatusCode::kMinRedirects) {
    // We treat the 200s as Okay results.
    return Status{};
  }
  // Parse the payload as a JSON object, see https://cloud.google.com/apis/design/errors#http_mapping
  auto parsed = nlohmann::json::parse(http_response.payload, nullptr, false);
  ErrorInfo error_info = ExtractErrorInfo(parsed);
  return Status(/* map HTTP code */, parsed.value("message", ""), error_info);

This sounds like a good idea. I'm inclined to this the current PR close #7429, then file a new issue about adding "ErrorInfo" support to GCS, including the details from this comment, and linking to the original #7429

WDYT?


google/cloud/status.h, line 70 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

nit: I would use https://cloud.google.com/apis/design/errors#error_info

Better. Done.


google/cloud/status.cc, line 159 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

Can we print the error in a single line (modulo the contents having \n in them)? It is super annoying to have multi-line error messages when using grep to parse them. Maybe format the ErrorInfo as as single-line JSON string, using the mapping from: https://cloud.google.com/apis/design/errors#http_mapping ?

I can change to json if you think that's better, but first a few clarifying questions about the suggestion.

  • Do you mean to print only the ErrorInfo stuff as json, and print the existing code and message not in json like it currently does? Or print all of it as json?
  • Making this valid json would require that we escape quotes in the contained strings. I could probably stuff this stuff in an nlohmann json object and offload the work to that library. Is this what you had in mind?

@google-cloud-cpp-bot
Copy link
Collaborator

Google Cloud Build Logs
For commit: e418108465b7690125d8da130d8b60d31e92f166

ℹ️ NOTE: Kokoro logs are linked from "Details" below.

Copy link
Contributor

@coryan coryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

Reviewed 2 of 2 files at r2, all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @devjgm)


google/cloud/grpc_error_delegate.cc, line 122 at r1 (raw file):

Previously, devjgm (Greg Miller) wrote…

This sounds like a good idea. I'm inclined to this the current PR close #7429, then file a new issue about adding "ErrorInfo" support to GCS, including the details from this comment, and linking to the original #7429

WDYT?

SGTM.


google/cloud/status.cc, line 159 at r1 (raw file):
Argh, quoting...

  • Yes, I was thinking just the ErrorInfo to format as json.
  • Using nlohmann::json would add a dep for common and thus all the libraries, not sure we want that.

Is this what you had in mind?

What I had in mind was ignoring the questions around quoting. It may still work, but I would not call it "JSON" anymore, something like:

std::ostream& operator<<(std::ostream& os, Status const& s) {
  if (s.ok()) return os << StatusCode::kOk;
  os << s.code << ": " << s.message();
  auto const& ei = s.error_info();
  if (ei.reason().empty() && ei.domain().empty() && ei.metadata.empty()) return os;
  os << ", error_info={reason=" << ei.reason() << ", domain=" << ei.domain() << ", metadata={";
  char const* sep = "";
  for (auto const& e : ei.metadata()) {
      os << sep << '"' << e.first << "\": \"" << e.second << '"';
      sep = ", ";
  }
  return os << "}";
}

Anyway, treat this as optional. I think it is better in that grep works, but worse in that it is harder to parse.

Fixes: googleapis#7429

This PR adds a `google::cloud::ErrorInfo` class, which mirrors
[ErrorInfo][error-info-proto], and can also work with an equivalent
type in a REST world. We expose programmatic access to this via the
`g::c::Status::error_info()` accessor. We also log this data when
streaming a `g::c::Status` if it's present.

As of today, very few (if any) GCP services actually return this proto,
so I don't expect to see it much yet.

[error-info-proto]: https://github.com/googleapis/googleapis/blob/2e680c6be4b8e48c12f006b945b93edd41be8653/google/rpc/error_details.proto#L112
@coryan
Copy link
Contributor

coryan commented Nov 20, 2021

I restarted the kokoro/macos/bazel build.

@devjgm
Copy link
Contributor Author

devjgm commented Nov 20, 2021

I restarted the kokoro/macos/bazel build.

Thanks. I'm still playing w/ one-line log formats, so I'll still be pushing another commit to build.

@google-cloud-cpp-bot
Copy link
Collaborator

Google Cloud Build Logs
For commit: 20ac43222766d88700ce63396f4d61af42adb837

ℹ️ NOTE: Kokoro logs are linked from "Details" below.

Copy link
Contributor Author

@devjgm devjgm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 4 of 7 files reviewed, all discussions resolved (waiting on @coryan)


google/cloud/grpc_error_delegate.cc, line 122 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

SGTM.

#7644


google/cloud/status.cc, line 159 at r1 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

Argh, quoting...

  • Yes, I was thinking just the ErrorInfo to format as json.
  • Using nlohmann::json would add a dep for common and thus all the libraries, not sure we want that.

Is this what you had in mind?

What I had in mind was ignoring the questions around quoting. It may still work, but I would not call it "JSON" anymore, something like:

std::ostream& operator<<(std::ostream& os, Status const& s) {
  if (s.ok()) return os << StatusCode::kOk;
  os << s.code << ": " << s.message();
  auto const& ei = s.error_info();
  if (ei.reason().empty() && ei.domain().empty() && ei.metadata.empty()) return os;
  os << ", error_info={reason=" << ei.reason() << ", domain=" << ei.domain() << ", metadata={";
  char const* sep = "";
  for (auto const& e : ei.metadata()) {
      os << sep << '"' << e.first << "\": \"" << e.second << '"';
      sep = ", ";
  }
  return os << "}";
}

Anyway, treat this as optional. I think it is better in that grep works, but worse in that it is harder to parse.

Done. The output is one line now. This is more grepable, but it's less human readable. I don't think we need these to be perfectly parseable, because if programmatic access is needed, users can inspect the ErrorInfo object directly.

Copy link
Contributor

@coryan coryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 3 of 3 files at r3, all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @devjgm)


google/cloud/status.cc, line 166 at r3 (raw file):

  auto const& e = s.error_info();
  if (e.reason().empty() && e.domain().empty() && e.metadata().empty())
    return os;

nit: braces in this case?

@devjgm
Copy link
Contributor Author

devjgm commented Nov 20, 2021

nit: braces in this case?

I'm not sure I follow. You mean print {} to indicate that error_info was empty? I thought we were trying to preserve the property that if error_info was empty, the logged output would be exactly the same as it is today and include nothing about error_info.

@devjgm
Copy link
Contributor Author

devjgm commented Nov 20, 2021

You probably meant wrapping the if-body in braces. If so, done.

@google-cloud-cpp-bot
Copy link
Collaborator

Google Cloud Build Logs
For commit: f32a1249ebc467dcc982e5e70bbfd8a03c4e05e6

ℹ️ NOTE: Kokoro logs are linked from "Details" below.

Copy link
Contributor Author

@devjgm devjgm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 6 of 7 files reviewed, all discussions resolved (waiting on @coryan)


google/cloud/status.cc, line 166 at r3 (raw file):

Previously, coryan (Carlos O'Ryan) wrote…

nit: braces in this case?

DOne

@devjgm devjgm merged commit 1a65e5b into googleapis:main Nov 20, 2021
@coryan
Copy link
Contributor

coryan commented Nov 21, 2021

You probably meant wrapping the if-body in braces. If so, done.

Yes, that is what I meant. Sorry if it was unclear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve error messages by supporting ErrorInfo and AIP-193
3 participants