| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/public/lib/statusor/status_macros.h" |
| |
| #include <algorithm> |
| |
| #include <google/protobuf/stubs/strutil.h> |
| |
| #include "glog/logging.h" |
| #include "src/public/lib/status.h" |
| |
| namespace cobalt::lib::statusor::status_macros { |
| |
| using cobalt::Status; |
| |
| static Status MakeStatus(cobalt::StatusCode code, const std::string& message) { |
| return Status(code, message); |
| } |
| |
| // Log the error at the given severity, optionally with a stack trace. |
| // If log_severity is NUM_SEVERITIES, nothing is logged. |
| static void LogError(const Status& status, const char* /*filename*/, int /*line*/, int log_severity, |
| bool /*should_log_stack_trace*/) { |
| if (log_severity != google::NUM_SEVERITIES) { |
| std::string stack_trace; |
| switch (log_severity) { |
| case google::INFO: |
| LOG(INFO) << status.error_message(); |
| break; |
| case google::WARNING: |
| LOG(WARNING) << status.error_message(); |
| break; |
| case google::ERROR: |
| LOG(ERROR) << status.error_message(); |
| break; |
| case google::FATAL: |
| LOG(FATAL) << status.error_message(); |
| break; |
| case google::NUM_SEVERITIES: |
| break; |
| default: |
| LOG(FATAL) << "Unknown LOG severity " << log_severity; |
| } |
| } |
| } |
| |
| // Make a Status with a code, error message and payload, |
| // and also send it to LOG(<log_severity>) using the given filename |
| // and line (unless should_log is false, or log_severity is |
| // NUM_SEVERITIES). If should_log_stack_trace is true, the stack |
| // trace is included in the log message (ignored if should_log is |
| // false). |
| static Status MakeError(const char* filename, int line, cobalt::StatusCode code, |
| const std::string& message, bool should_log, int log_severity, |
| bool should_log_stack_trace) { |
| if (code == cobalt::StatusCode::OK) { |
| LOG(ERROR) << "Cannot create error with status OK"; |
| code = cobalt::StatusCode::UNKNOWN; |
| } |
| Status status = MakeStatus(code, message); |
| if (should_log) { |
| LogError(status, filename, line, log_severity, should_log_stack_trace); |
| } |
| return status; |
| } |
| |
| // This method is written out-of-line rather than in the header to avoid |
| // generating a lot of inline code for error cases in all callers. |
| void MakeErrorStream::CheckNotDone() const { impl_->CheckNotDone(); } |
| |
| MakeErrorStream::Impl::Impl(const char* file, int line, cobalt::StatusCode code, |
| MakeErrorStream* error_stream, bool is_logged_by_default) |
| : file_(file), |
| line_(line), |
| code_(code), |
| is_done_(false), |
| should_log_(is_logged_by_default), |
| log_severity_(google::ERROR), |
| should_log_stack_trace_(false), |
| make_error_stream_with_output_wrapper_(error_stream) {} |
| |
| MakeErrorStream::Impl::Impl(const Status& status, PriorMessageHandling prior_message_handling, |
| const char* file, int line, MakeErrorStream* error_stream) |
| : file_(file), |
| line_(line), |
| // Make sure we show some error, even if the call is incorrect. |
| code_(!status.ok() ? status.error_code() : cobalt::StatusCode::UNKNOWN), |
| prior_message_handling_(prior_message_handling), |
| prior_message_(status.error_message()), |
| is_done_(false), |
| // Error code type is not visible here, so we can't call |
| // IsLoggedByDefault. |
| should_log_(true), |
| log_severity_(google::ERROR), |
| should_log_stack_trace_(false), |
| make_error_stream_with_output_wrapper_(error_stream) { |
| DCHECK(!status.ok()) << "Attempted to append/prepend error text to status OK"; |
| } |
| |
| MakeErrorStream::Impl::~Impl() { |
| // Note: error messages refer to the public MakeErrorStream class. |
| |
| if (!is_done_) { |
| LOG(ERROR) << "MakeErrorStream destructed without getting Status: " << file_ << ":" << line_ |
| << " " << stream_.str(); |
| } |
| } |
| |
| Status MakeErrorStream::Impl::GetStatus() { |
| // Note: error messages refer to the public MakeErrorStream class. |
| |
| // Getting a Status object out more than once is not harmful, but |
| // it doesn't match the expected pattern, where the stream is constructed |
| // as a temporary, loaded with a message, and then casted to Status. |
| if (is_done_) { |
| LOG(ERROR) << "MakeErrorStream got Status more than once: " << file_ << ":" << line_ << " " |
| << stream_.str(); |
| } |
| |
| is_done_ = true; |
| |
| const std::string& stream_str = stream_.str(); |
| const std::string str = prior_message_handling_ == kAppendToPriorMessage |
| ? google::protobuf::StrCat(prior_message_, stream_str) |
| : google::protobuf::StrCat(stream_str, prior_message_); |
| if (str.empty()) { |
| return MakeError(file_, line_, code_, |
| google::protobuf::StrCat(str, "Error without message at ", file_, ":", line_), |
| true /* should_log */, google::ERROR /* log_severity */, |
| should_log_stack_trace_); |
| } |
| return MakeError(file_, line_, code_, str, should_log_, log_severity_, should_log_stack_trace_); |
| } |
| |
| void MakeErrorStream::Impl::CheckNotDone() const { |
| if (is_done_) { |
| LOG(ERROR) << "MakeErrorStream shift called after getting Status: " << file_ << ":" << line_ |
| << " " << stream_.str(); |
| } |
| } |
| |
| } // namespace cobalt::lib::statusor::status_macros |