blob: dcd1985e201953e511f87f7616b3ee7ae0d7a3bc [file] [log] [blame]
// 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