| // Copyright 2026 The Abseil Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| // ----------------------------------------------------------------------------- |
| // File: status_builder.h |
| // ----------------------------------------------------------------------------- |
| |
| #ifndef ABSL_STATUS_STATUS_BUILDER_H_ |
| #define ABSL_STATUS_STATUS_BUILDER_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <ostream> |
| #include <string> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "absl/base/attributes.h" |
| #include "absl/base/config.h" |
| #include "absl/base/log_severity.h" |
| #include "absl/base/optimization.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/cord.h" |
| #include "absl/strings/internal/ostringstream.h" |
| #include "absl/strings/internal/stringify_stream.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/time/time.h" |
| #include "absl/types/source_location.h" |
| |
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| |
| class LogSink; |
| class StatusBuilder; |
| |
| namespace status_internal { |
| |
| // StatusBuilder's operator<< overloads use an AbslStringifyStream to allow us |
| // to use AbslStringify. This wraps (but does not own) an OStringStream, which |
| // we use for speed. We bundle them together in Stream here, partly for |
| // convenience in StatusBuilder's implementation, and partly to help make sure |
| // their lifetimes are managed correctly. |
| class Stream { |
| public: |
| explicit Stream(std::string& message) |
| : ostringstream_(&message), absl_stringify_stream_(ostringstream_) {} |
| |
| template <typename T> |
| friend Stream& operator<<(Stream& stream, const T& t) { |
| stream.absl_stringify_stream_ << t; |
| return stream; |
| } |
| |
| private: |
| absl::strings_internal::OStringStream ostringstream_; |
| absl::strings_internal::StringifyStream absl_stringify_stream_; |
| }; |
| |
| // StatusBuilder::With() adaptors can be classified as either "pure policy" or |
| // "terminal". Terminal adaptors are a mix of "side effect" or "conversion". |
| // We differentiate between these types by the functor's return type. |
| // |
| // This is currently for analysis only, as part of an ongoing LSC investigation. |
| template <typename Fn, typename Arg, typename Expected> |
| inline constexpr bool kResultMatches = |
| std::is_same_v<std::decay_t<std::invoke_result_t<Fn, Arg>>, Expected>; |
| |
| template <typename Adaptor, typename Builder> |
| using PurePolicy = |
| std::enable_if_t<kResultMatches<Adaptor, Builder, StatusBuilder>, |
| std::invoke_result_t<Adaptor, Builder>>; |
| |
| template <typename Adaptor, typename Builder> |
| using SideEffect = |
| std::enable_if_t<kResultMatches<Adaptor, Builder, absl::Status>, |
| std::invoke_result_t<Adaptor, Builder>>; |
| |
| template <typename Adaptor, typename Builder> |
| using Conversion = |
| std::enable_if_t<!kResultMatches<Adaptor, Builder, StatusBuilder> && |
| !kResultMatches<Adaptor, Builder, absl::Status>, |
| std::invoke_result_t<Adaptor, Builder>>; |
| |
| class StatusBuilderPrivateAccessor; |
| |
| } // namespace status_internal |
| |
| // Internal argument-dependent lookup extension point for StatusBuilder. |
| // Necessary for allowing StatusBuilder to be open-sourced into Abseil without |
| // introducing hard dependencies on non-canonical error spaces or protobuf. |
| // |
| // What we would have preferred here is to just leave |
| // StatusBuilder::SetErrorCode() undeclared for OSS users, but defined for |
| // internal users. However, C++ doesn't provide a great way to do that directly, |
| // since merely omitting a definition still leaves it declared and thus |
| // accessible. |
| // |
| // We therefore use this ADL extension point as the closest moral equivalent: |
| // this allows use to define a simple shell for the method in OSS, but to |
| // prevent its actual usage (via SFINAE) unless this extension point is also |
| // defined, which is only the case in our internal libraries. |
| void AbslInternalSetErrorCode(StatusBuilder&, absl::StatusCode); |
| |
| // Specifies how to join the error message in the original status and any |
| // additional message that has been streamed into the builder. |
| enum class MessageJoinStyle { |
| kAnnotate, |
| kAppend, |
| kPrepend, |
| }; |
| |
| // Creates a status based on an original_status, but enriched with additional |
| // information. The builder implicitly converts to Status and StatusOr<T> |
| // allowing for it to be returned directly. |
| // |
| // StatusBuilder builder(original); |
| // builder.SetPayload(proto); |
| // builder << "info about error"; |
| // return builder; |
| // |
| // It provides method chaining to simplify typical usage: |
| // |
| // return StatusBuilder(original) |
| // .Log(absl::LogSeverity::kWarning) << "oh no!"; |
| // |
| // In more detail: |
| // - When the original status is OK, all methods become no-ops and nothing will |
| // be logged. |
| // - Messages streamed into the status builder are collected into a single |
| // additional message string. |
| // - The original Status's message and the additional message are joined |
| // together when the result status is built. |
| // - By default, the messages will be joined as if by `util::Annotate`, which |
| // includes a convenience separator between the original message and the |
| // additional one. This behavior can be changed with the `SetAppend()` and |
| // `SetPrepend()` methods of the builder. |
| // - By default, the result status is not logged. The `Log` and |
| // `EmitStackTrace` methods will cause the builder to log the result status |
| // when it is built. |
| // - All side effects (like logging or constructing a stack trace) happen when |
| // the builder is converted to a status. |
| class ABSL_MUST_USE_RESULT StatusBuilder { |
| public: |
| explicit StatusBuilder(); |
| ~StatusBuilder(); |
| |
| // Creates a `StatusBuilder` based on an original status. If logging is |
| // enabled, it will use `location` as the location from which the log message |
| // occurs. A typical user will not specify `location`, allowing it to default |
| // to the current location. |
| explicit StatusBuilder( |
| const absl::Status& original_status, |
| absl::SourceLocation location = absl::SourceLocation::current()); |
| explicit StatusBuilder( |
| absl::Status&& original_status, |
| absl::SourceLocation location = absl::SourceLocation::current()); |
| |
| // Creates a `StatusBuilder` from a status code. If logging is enabled, it |
| // will use `location` as the location from which the log message occurs. A |
| // typical user will not specify `location`, allowing it to default to the |
| // current location. |
| explicit StatusBuilder( |
| absl::StatusCode code, |
| absl::SourceLocation location = absl::SourceLocation::current()); |
| |
| StatusBuilder(const StatusBuilder& sb); |
| StatusBuilder& operator=(const StatusBuilder& sb); |
| StatusBuilder(StatusBuilder&&) = default; |
| StatusBuilder& operator=(StatusBuilder&&) = default; |
| |
| // Mutates the builder so that the final additional message is prepended to |
| // the original error message in the status. A convenience separator is not |
| // placed between the messages. |
| // |
| // NOTE: Multiple calls to `SetPrepend` and `SetAppend` just adjust the |
| // behavior of the final join of the original status with the extra message. |
| // |
| // Returns `*this` to allow method chaining. |
| StatusBuilder& SetPrepend() &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& SetPrepend() &&; |
| |
| // Mutates the builder so that the final additional message is appended to the |
| // original error message in the status. A convenience separator is not |
| // placed between the messages. |
| // |
| // NOTE: Multiple calls to `SetPrepend` and `SetAppend` just adjust the |
| // behavior of the final join of the original status with the extra message. |
| // |
| // Returns `*this` to allow method chaining. |
| StatusBuilder& SetAppend() &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& SetAppend() &&; |
| |
| // Mutates the builder to disable any logging that was set using any of the |
| // logging functions below. Returns `*this` to allow method chaining. |
| StatusBuilder& SetNoLogging() &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& SetNoLogging() &&; |
| |
| // Mutates the builder so that the result status will be logged (without a |
| // stack trace) when this builder is converted to a Status. This overrides |
| // the logging settings from earlier calls to any of the logging mutator |
| // functions. Returns `*this` to allow method chaining. |
| StatusBuilder& Log(absl::LogSeverity level) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& Log(absl::LogSeverity level) &&; |
| |
| StatusBuilder& LogError() & { return Log(absl::LogSeverity::kError); } |
| ABSL_MUST_USE_RESULT StatusBuilder&& LogError() && { |
| return std::move(LogError()); |
| } |
| StatusBuilder& LogWarning() & { return Log(absl::LogSeverity::kWarning); } |
| ABSL_MUST_USE_RESULT StatusBuilder&& LogWarning() && { |
| return std::move(LogWarning()); |
| } |
| StatusBuilder& LogInfo() & { return Log(absl::LogSeverity::kInfo); } |
| ABSL_MUST_USE_RESULT StatusBuilder&& LogInfo() && { |
| return std::move(LogInfo()); |
| } |
| |
| // Mutates the builder so that the result status will be logged every N |
| // invocations (without a stack trace) when this builder is converted to a |
| // Status. This overrides the logging settings from earlier calls to any of |
| // the logging mutator functions. Returns `*this` to allow method chaining. |
| StatusBuilder& LogEveryN(absl::LogSeverity level, int n) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& LogEveryN(absl::LogSeverity level, |
| int n) &&; |
| |
| // Mutates the builder so that the result status will be logged once per |
| // period (without a stack trace) when this builder is converted to a Status. |
| // This overrides the logging settings from earlier calls to any of the |
| // logging mutator functions. Returns `*this` to allow method chaining. |
| // If period is absl::ZeroDuration() or less, then this is equivalent to |
| // calling the Log() method. |
| StatusBuilder& LogEvery(absl::LogSeverity level, absl::Duration period) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& LogEvery(absl::LogSeverity level, |
| absl::Duration period) &&; |
| |
| // Mutates the builder so that the result status will be VLOGged (without a |
| // stack trace) when this builder is converted to a Status. `verbose_level` |
| // indicates the verbosity level that would be passed to VLOG(). This |
| // overrides the logging settings from earlier calls to any of the logging |
| // mutator functions. Returns `*this` to allow method chaining. |
| StatusBuilder& VLog(int verbose_level) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& VLog(int verbose_level) &&; |
| |
| // Mutates the builder so that a stack trace will be logged if the status is |
| // logged. One of the logging setters above should be called as well. If |
| // logging is not yet enabled this behaves as if LogInfo().EmitStackTrace() |
| // was called. Returns `*this` to allow method chaining. |
| StatusBuilder& EmitStackTrace() &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& EmitStackTrace() &&; |
| |
| // Mutates the builder so that the result status will also be logged to the |
| // provided `sink` when this builder is converted to a status. Overwrites any |
| // sink set prior. The provided `sink` must point to a valid object by the |
| // time this builder is converted to a status. Has no effect if this builder |
| // is not configured to log by calling any of the LogXXX methods. Returns |
| // `*this` to allow method chaining. |
| StatusBuilder& AlsoOutputToSink(absl::LogSink* sink) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& AlsoOutputToSink(absl::LogSink* sink) &&; |
| |
| // Mutates the builder so that the result status will only be logged to the |
| // provided `sink` when this builder is converted to a status. Overwrites any |
| // sink set prior. The provided `sink` must point to a valid object by the |
| // time this builder is converted to a status. Has no effect if this builder |
| // is not configured to log by calling any of the LogXXX methods. Returns |
| // `*this` to allow method chaining. |
| StatusBuilder& OnlyOutputToSink(absl::LogSink* sink) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& OnlyOutputToSink(absl::LogSink* sink) &&; |
| |
| // Appends to the extra message that will be added to the original status. By |
| // default, the extra message is added to the original message as if by |
| // `util::Annotate`, which includes a convenience separator between the |
| // original message and the enriched one. |
| template <typename T> |
| StatusBuilder& operator<<(const T& value) &; |
| |
| template <typename T> |
| ABSL_MUST_USE_RESULT StatusBuilder&& operator<<(const T& value) &&; |
| |
| // Adds a payload for the status that will be returned by this StatusBuilder. |
| // Note that this is equivalent to `Status::SetPayload`, to attach protos to |
| // the MessageSet payload, use `util::AttachPayload`. Returns '*this' to allow |
| // method chaining. |
| StatusBuilder& SetPayload(absl::string_view type_url, absl::Cord payload) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& SetPayload(absl::string_view type_url, |
| absl::Cord payload) && { |
| return std::move(SetPayload(type_url, std::move(payload))); |
| } |
| |
| std::optional<absl::Cord> GetPayload(absl::string_view type_url) const; |
| |
| // INTERNAL API. NOT FOR PUBLIC USE. |
| template <typename MessageSetExtension, typename ExtensionIdentifier> |
| auto AttachPayload(const MessageSetExtension& obj, |
| const ExtensionIdentifier& id) & // |
| -> std::enable_if_t< |
| std::is_void_v<decltype(AbslInternalAttachPayload(*this, obj, id))>, |
| StatusBuilder&> { |
| AbslInternalAttachPayload(*this, obj, id); |
| return *this; |
| } |
| |
| // As above, but &&-qualified. |
| // |
| // Uses decltype() to propagate template constraints (SFINAE). |
| template <typename MessageSetExtension, typename ExtensionIdentifier> |
| ABSL_MUST_USE_RESULT auto AttachPayload(const MessageSetExtension& obj, |
| const ExtensionIdentifier& id) && // |
| -> decltype(std::move(AttachPayload(obj, id))) { |
| return std::move(AttachPayload(obj, id)); |
| } |
| |
| // Like `AttachPayload() &`, but with the default extension ID. |
| // |
| // INTERNAL API. NOT FOR PUBLIC USE. |
| template <typename MessageSetExtension> |
| auto AttachPayload(const MessageSetExtension& obj) & // |
| -> std::enable_if_t< |
| std::is_void_v<decltype(AbslInternalAttachPayload(*this, obj))>, |
| StatusBuilder&> { |
| AbslInternalAttachPayload(*this, obj); |
| return *this; |
| } |
| |
| // As above, but &&-qualified. |
| // |
| // Uses decltype() to propagate template constraints (SFINAE). |
| template <typename MessageSetExtension> |
| ABSL_MUST_USE_RESULT auto AttachPayload(const MessageSetExtension& obj) && // |
| -> decltype(std::move(AttachPayload(obj))) { |
| return std::move(AttachPayload(obj)); |
| } |
| |
| // HasPayload() |
| // |
| // Indicates whether the Status object that will be returned by the |
| // StatusBuilder contains any payloads with a type extending proto2's |
| // `MessageSet`, returning `true` if so. Having a payload does not guarantee |
| // the presence of a payload with a specific type. Note that returning `false` |
| // does not necessarily indicate the absence of a payload, but only the |
| // absence on one which extends `MessageSet`. |
| bool HasPayload() const; |
| |
| // INTERNAL API. NOT FOR PUBLIC USE. |
| template <typename Enum> |
| auto SetErrorCode(Enum code) & // |
| -> std::enable_if_t< |
| std::is_void_v<decltype(AbslInternalSetErrorCode(*this, code))>, |
| StatusBuilder&> { |
| AbslInternalSetErrorCode(*this, code); |
| return *this; |
| } |
| |
| // As above, but &&-qualified. |
| // |
| // Uses decltype() to propagate template constraints (SFINAE). |
| template <typename Enum> |
| ABSL_MUST_USE_RESULT auto SetErrorCode(Enum code) && // |
| -> decltype(std::move(SetErrorCode(code))) { |
| return std::move(SetErrorCode(code)); |
| } |
| |
| // Sets the status code for the status that will be returned by this |
| // StatusBuilder. Returns `*this` to allow method chaining. |
| StatusBuilder& SetCode(absl::StatusCode code) &; |
| ABSL_MUST_USE_RESULT StatusBuilder&& SetCode(absl::StatusCode code) && { |
| return std::move(SetCode(code)); |
| } |
| |
| ///////////////////////////////// Adaptors ///////////////////////////////// |
| // |
| // A StatusBuilder `adaptor` is a functor which can be included in a builder |
| // method chain. There are two common variants: |
| // |
| // 1. `Pure policy` adaptors modify the StatusBuilder and return the modified |
| // object, which can then be chained with further adaptors or mutations. |
| // |
| // 2. `Terminal` adaptors consume the builder's Status and return some |
| // other type of object. Alternatively, the consumed Status may be used |
| // for side effects, e.g. by passing it to a side channel. A terminal |
| // adaptor cannot be chained. |
| // |
| // Careful: The conversion of StatusBuilder to Status has side effects! |
| // Adaptors must ensure that this conversion happens at most once in the |
| // builder chain. The easiest way to do this is to determine the adaptor type |
| // and follow the corresponding guidelines: |
| // |
| // Pure policy adaptors should: |
| // (a) Take a StatusBuilder as input parameter. |
| // (b) NEVER convert the StatusBuilder to Status: |
| // - Never assign the builder to a Status variable. |
| // - Never pass the builder to a function whose parameter type is Status, |
| // including by reference (e.g. const Status&). |
| // - Never pass the builder to any function which might convert the |
| // builder to Status (i.e. this restriction is viral). |
| // (c) Return a StatusBuilder (usually the input parameter). |
| // |
| // Terminal adaptors should: |
| // (a) Take a Status as input parameter (not a StatusBuilder!). |
| // (b) Return a type matching the enclosing function. (This can be `void`.) |
| // |
| // Adaptors do not necessarily fit into one of these categories. However, any |
| // which satisfies the conversion rule can always be decomposed into a pure |
| // adaptor chained into a terminal adaptor. (This is recommended.) |
| // |
| // Examples |
| // |
| // Pure adaptors allow teams to configure team-specific error handling |
| // policies. For example: |
| // |
| // StatusBuilder TeamPolicy(StatusBuilder builder) { |
| // builder.SetPayload(...); |
| // return std::move(builder).Log(absl::LogSeverity::kWarning); |
| // } |
| // |
| // ABSL_RETURN_IF_ERROR(foo()).With(TeamPolicy); |
| // |
| // Because pure policy adaptors return the modified StatusBuilder, they |
| // can be chained with further adaptors, e.g.: |
| // |
| // ABSL_RETURN_IF_ERROR(foo()).With(TeamPolicy).With(OtherTeamPolicy); |
| // |
| // Terminal adaptors are often used for type conversion. This allows |
| // ABSL_RETURN_IF_ERROR to be used in functions which do not return Status. |
| // For example, a function might want to return some default value on error: |
| // |
| // int GetSysCounter() { |
| // int value; |
| // ABSL_RETURN_IF_ERROR(ReadCounterFile(filename, &value)) |
| // .LogInfo() |
| // .With([](const absl::Status& unused) { return 0; }); |
| // return value; |
| // } |
| // |
| // For the simple case of returning a constant (e.g. zero, false, nullptr) on |
| // error, consider `status_macros::Return` or `status_macros::ReturnVoid`: |
| // |
| // #include "util/task/contrib/status_macros/return.h" |
| // |
| // bool DoMyThing() { |
| // ABSL_RETURN_IF_ERROR(foo()).LogWarning().With(status_macros::Return(false)); |
| // ... |
| // } |
| // |
| // A terminal adaptor may instead (or additionally) be used to create side |
| // effects that are not supported natively by `StatusBuilder`, such as |
| // returning the Status through a side channel. For example, |
| // `util::TaskReturn` returns the Status through the `util::Task` that it was |
| // initialized with. This adaptor then returns `void`, to match the typical |
| // return type of functions that maintain state through `util::Task`: |
| // |
| // class TaskReturn { |
| // public: |
| // explicit TaskReturn(Task* t) : task_(t) {} |
| // void operator()(const Status& status) const { task_->Return(status); } |
| // // ... |
| // }; |
| // |
| // void Read(absl::string_view name, util::Task* task) { |
| // int64 id; |
| // ABSL_RETURN_IF_ERROR(GetIdForName(name, &id)).With(TaskReturn(task)); |
| // ABSL_RETURN_IF_ERROR(ReadForId(id)).With(TaskReturn(task)); |
| // task->Return(); |
| // } |
| |
| // Calls `adaptor` on this status builder to apply policies, type conversions, |
| // and/or side effects on the StatusBuilder. Returns the value returned by |
| // `adaptor`, which may be any type including `void`. See comments above. |
| // |
| // Style guide exception for Ref qualified methods and rvalue refs |
| // (cl/128258530). This allows us to avoid a copy in the common case. |
| // |
| // Note: All With() overrides are equivalent, and return Adaptor(this). They |
| // are part of an ongoing LSC investigation. |
| template <typename Adaptor> |
| auto With(Adaptor&& adaptor) & -> status_internal::PurePolicy< |
| Adaptor, StatusBuilder&> { |
| return std::forward<Adaptor>(adaptor)(*this); |
| } |
| template <typename Adaptor> |
| ABSL_MUST_USE_RESULT auto With( |
| Adaptor&& |
| adaptor) && -> status_internal::PurePolicy<Adaptor, StatusBuilder&&> { |
| return std::forward<Adaptor>(adaptor)(std::move(*this)); |
| } |
| |
| template <typename Adaptor> |
| auto With(Adaptor&& adaptor) & -> status_internal::SideEffect< |
| Adaptor, StatusBuilder&> { |
| return std::forward<Adaptor>(adaptor)(*this); |
| } |
| template <typename Adaptor> |
| ABSL_MUST_USE_RESULT auto With( |
| Adaptor&& |
| adaptor) && -> status_internal::SideEffect<Adaptor, StatusBuilder&&> { |
| return std::forward<Adaptor>(adaptor)(std::move(*this)); |
| } |
| |
| template <typename Adaptor> |
| auto With(Adaptor&& adaptor) & -> status_internal::Conversion< |
| Adaptor, StatusBuilder&> { |
| return std::forward<Adaptor>(adaptor)(*this); |
| } |
| template <typename Adaptor> |
| ABSL_MUST_USE_RESULT auto With( |
| Adaptor&& |
| adaptor) && -> status_internal::Conversion<Adaptor, StatusBuilder&&> { |
| return std::forward<Adaptor>(adaptor)(std::move(*this)); |
| } |
| |
| // Returns true if the Status created by this builder will be ok(). |
| ABSL_MUST_USE_RESULT bool ok() const; |
| |
| // Returns the (canonical) error code for the Status created by this builder. |
| absl::StatusCode code() const; |
| |
| // Implicit conversion to Status. |
| // |
| // Careful: this operator has side effects, so it should be called at |
| // most once. In particular, do NOT use this conversion operator to inspect |
| // the status from an adapter object passed into With(). |
| // |
| // Style guide exception for using Ref qualified methods and for implicit |
| // conversions (cl/124566728). This override allows us to implement |
| // ABSL_RETURN_IF_ERROR with 2 move operations in the common case. |
| operator absl::Status() const&; // NOLINT: Builder converts implicitly. |
| operator absl::Status() &&; // NOLINT: Builder converts implicitly. |
| |
| // Returns the source location used to create this builder. This differs from |
| // `GetPreviousSourceLocations()` as this location is the single location for |
| // the creation of this builder. |
| ABSL_MUST_USE_RESULT absl::SourceLocation source_location() const; |
| |
| // Returns the source locations previously recorded in the status. This will |
| // not include the source location of the `StatusBuilder` itself (which is |
| // available via `source_location()`). When the builder is converted to a |
| // Status, the builder's location will be appended to this list of previous |
| // locations. For `StatusBuilder`s created without an original status (e.g., |
| // from a status code), this will be empty. |
| decltype(auto) GetPreviousSourceLocations() const { |
| if (rep_ == nullptr) { |
| return absl::OkStatus().GetSourceLocations(); |
| } |
| return rep_->status.GetSourceLocations(); |
| } |
| |
| // Returns a string based on the `mode`. This produces the same string as |
| // would converting to a Status and calling operator<<, but it does so without |
| // side effects (e.g., logging). Therefore, it is safe to use from within an |
| // adapter object passed into With(). |
| std::string ToString() const; |
| |
| private: |
| friend class status_internal::StatusBuilderPrivateAccessor; |
| |
| // Returns true if the compiler can determine that the instance is empty. That |
| // is, `rep_ == nullptr`. |
| // A `false` return does not necessarily indicate that it has a rep. It just |
| // can't prove it doesn't. |
| ABSL_ATTRIBUTE_ALWAYS_INLINE bool IsKnownToBeEmpty() const { |
| #if ABSL_HAVE_BUILTIN(__builtin_constant_p) |
| // __builtin_constant_p does not like it when it has non trivial expressions |
| // in it, and `rep_==nullptr` is a user-defined operator. |
| // Do it out of the builtin and pass the bool instead. |
| bool is_empty = rep_ == nullptr; |
| return __builtin_constant_p(is_empty) && is_empty; |
| #else |
| return false; |
| #endif |
| } |
| |
| // Tells the compiler that it can assume that `rep_` is null. |
| // This is verified in non-opt mode. |
| void AssumeEmpty() const { |
| if (rep_ != nullptr) ABSL_UNREACHABLE(); |
| } |
| |
| static std::string CurrentStackTrace(); |
| |
| struct Rep; |
| // Destroy the `Rep` object. It is just calling the destructor of the |
| // `unique_ptr`, but out of line. |
| static void Destroy(std::unique_ptr<Rep>); |
| |
| // Creates a Status from this builder and logs it if the builder has been |
| // configured to log itself. |
| // NOTE: This function is `static` to prevent escaping the `this` pointer. We |
| // transfer the `Rep` to delegate the destruction. |
| static absl::Status CreateStatusAndConditionallyLog(absl::SourceLocation loc, |
| std::unique_ptr<Rep> rep); |
| |
| // Infrequently set builder options, instantiated lazily. This reduces |
| // average construction/destruction time (e.g. the `stream` is fairly |
| // expensive). Stacks can also be blown if StatusBuilder grows too large. |
| // This is primarily an issue for debug builds, which do not necessarily |
| // re-use stack space within a function across the sub-scopes used by |
| // status macros. |
| struct Rep { |
| explicit Rep(const absl::Status& s); |
| explicit Rep(absl::Status&& s); |
| Rep(const Rep& r); |
| ~Rep(); |
| void InitStream(); |
| |
| // The status that the result will be based on. Can be modified by |
| // SetPayload(). |
| absl::Status status; |
| |
| enum class LoggingMode { |
| kDisabled, |
| kLog, |
| kVLog, |
| kLogEveryN, |
| kLogEveryPeriod |
| }; |
| LoggingMode logging_mode = LoggingMode::kDisabled; |
| |
| // The severity level at which the Status should be logged. Note that |
| // `logging_mode == LoggingMode::kVLog` always logs at severity INFO. |
| absl::LogSeverity log_severity; |
| |
| // The level at which the Status should be VLOGged. |
| // Only used when `logging_mode == LoggingMode::kVLog`. |
| int verbose_level; |
| |
| // Only log every N invocations. |
| // Only used when `logging_mode == LoggingMode::kLogEveryN`. |
| int n; |
| |
| // Only log once per period. |
| // Only used when `logging_mode == LoggingMode::kLogEveryPeriod`. |
| absl::Duration period; |
| |
| // Gathers additional messages added with `<<` for use in the final status. |
| std::string stream_message; |
| std::optional<status_internal::Stream> stream; |
| |
| // If not nullptr, specifies the log sink where log output should be |
| // sent to. Only used when `logging_mode != LoggingMode::kDisabled`. |
| absl::LogSink* sink = nullptr; |
| |
| // Specifies how to join the message in `status` and `stream`. |
| MessageJoinStyle message_join_style = MessageJoinStyle::kAnnotate; |
| |
| // Whether to log stack trace. Only used when `logging_mode != |
| // LoggingMode::kDisabled`. |
| bool should_log_stack_trace = false; |
| |
| // When this is true, will send both to `sink` and will log. When this is |
| // false, will only send to `sink`. |
| bool also_send_to_log = true; |
| }; |
| |
| static Rep* InitRep(const absl::Status& s) { |
| if (s.ok()) { |
| return nullptr; |
| } else { |
| return new Rep(s); |
| } |
| } |
| |
| static Rep* InitRep(absl::Status&& s) { |
| if (s.ok()) { |
| return nullptr; |
| } else { |
| Rep* rep = InitRepImpl(std::move(s)); |
| ABSL_ASSUME(rep != nullptr); |
| return rep; |
| } |
| } |
| |
| // Out of line function that constructs the Rep. |
| // Note that `s` is by value because of the TRIVIAL_ABI. |
| static Rep* InitRepImpl(absl::Status s); |
| |
| // The location to record if this status is logged. |
| absl::SourceLocation loc_; |
| |
| // nullptr if the result status will be OK. Extra fields moved to the heap to |
| // minimize stack space. |
| std::unique_ptr<Rep> rep_; |
| |
| // For testing Rep's implememntation. |
| friend class StatusBuilderTest; |
| }; |
| |
| // Implicitly converts `builder` to `Status` and write it to `os`. |
| std::ostream& operator<<(std::ostream& os, const StatusBuilder& builder); |
| std::ostream& operator<<(std::ostream& os, StatusBuilder&& builder); |
| |
| // StatusBuilder policy to append an extra message to the original status. |
| // |
| // This is most useful with adaptors such as util::TaskReturn that otherwise |
| // would prevent use of operator<<. For example: |
| // |
| // ABSL_RETURN_IF_ERROR(foo(val)) |
| // .With(util::ExtraMessage("when calling foo()")) |
| // .With(util::TaskReturn(task)); |
| // |
| // or |
| // |
| // ABSL_RETURN_IF_ERROR(foo(val)) |
| // .With(util::ExtraMessage() << "val: " << val) |
| // .With(util::TaskReturn(task)); |
| // |
| // Note in the above example, the ABSL_RETURN_IF_ERROR macro ensures the |
| // ExtraMessage expression is evaluated only in the error case, so efficiency of |
| // constructing the message is not a concern in the success case. |
| class ExtraMessage { |
| public: |
| ExtraMessage() : ExtraMessage(std::string()) {} |
| explicit ExtraMessage(std::string msg) |
| : msg_(std::move(msg)), stream_(msg_) {} |
| |
| ExtraMessage( |
| ExtraMessage&& other) noexcept // strings::OStringStream is stateless |
| // so we can simply move over the message. |
| : ExtraMessage(std::move(other.msg_)) {} |
| |
| // Appends to the extra message that will be added to the original status. By |
| // default, the extra message is added to the original message as if by |
| // `util::Annotate`, which includes a convenience separator between the |
| // original message and the enriched one. |
| template <typename T> |
| ExtraMessage& operator<<(const T& value) & { |
| stream_ << value; |
| return *this; |
| } |
| |
| // As above, preserving the rvalue-ness of the ExtraMessage object. |
| template <typename T> |
| ExtraMessage&& operator<<(const T& value) && { |
| *this << value; |
| return std::move(*this); |
| } |
| |
| // Appends to the extra message that will be added to the original status. By |
| // default, the extra message is added to the original message as if by |
| // `util::Annotate`, which includes a convenience separator between the |
| // original message and the enriched one. |
| StatusBuilder operator()(StatusBuilder builder) const { |
| builder << msg_; |
| return builder; |
| } |
| |
| private: |
| std::string msg_; |
| status_internal::Stream stream_; |
| }; |
| |
| // Implementation details follow; clients should ignore. |
| |
| inline StatusBuilder::StatusBuilder(absl::StatusCode code, |
| absl::SourceLocation location) |
| : loc_(location), rep_(InitRep(absl::Status(code, ""))) {} |
| |
| inline StatusBuilder::StatusBuilder(const StatusBuilder& sb) : loc_(sb.loc_) { |
| if (sb.rep_ != nullptr) { |
| rep_ = std::make_unique<Rep>(*sb.rep_); |
| } |
| } |
| |
| inline StatusBuilder::StatusBuilder(absl::Status&& original_status, |
| absl::SourceLocation location) |
| : loc_(location), rep_(InitRep(std::move(original_status))) {} |
| |
| inline StatusBuilder::~StatusBuilder() { |
| if (IsKnownToBeEmpty()) { |
| // Nothing to do. |
| return; |
| } |
| // We will run the destructor logic, so move it out of line. |
| // The destructor of the unique_ptr runs ~Rep() and then ::operator delete. |
| // We don't want that bloat on the caller. |
| Destroy(std::move(rep_)); |
| // Tell the compiler that `rep_` was not filled again even if `this` escaped. |
| AssumeEmpty(); |
| } |
| |
| inline StatusBuilder& StatusBuilder::operator=(const StatusBuilder& sb) { |
| loc_ = sb.loc_; |
| if (sb.rep_ != nullptr) { |
| rep_ = std::make_unique<Rep>(*sb.rep_); |
| } else { |
| rep_ = nullptr; |
| } |
| return *this; |
| } |
| |
| inline StatusBuilder& StatusBuilder::SetPrepend() & { |
| if (rep_ == nullptr) return *this; |
| rep_->message_join_style = MessageJoinStyle::kPrepend; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::SetPrepend() && { |
| return std::move(SetPrepend()); |
| } |
| |
| inline StatusBuilder& StatusBuilder::SetAppend() & { |
| if (rep_ == nullptr) return *this; |
| rep_->message_join_style = MessageJoinStyle::kAppend; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::SetAppend() && { |
| return std::move(SetAppend()); |
| } |
| |
| inline StatusBuilder& StatusBuilder::SetNoLogging() & { |
| if (rep_ != nullptr) { |
| rep_->logging_mode = Rep::LoggingMode::kDisabled; |
| rep_->should_log_stack_trace = false; |
| } |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::SetNoLogging() && { |
| return std::move(SetNoLogging()); |
| } |
| |
| inline StatusBuilder& StatusBuilder::Log(absl::LogSeverity level) & { |
| if (rep_ == nullptr) return *this; |
| rep_->logging_mode = Rep::LoggingMode::kLog; |
| rep_->log_severity = level; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::Log(absl::LogSeverity level) && { |
| return std::move(Log(level)); |
| } |
| |
| inline StatusBuilder& StatusBuilder::LogEveryN(absl::LogSeverity level, |
| int n) & { |
| if (rep_ == nullptr) return *this; |
| if (n < 1) return Log(level); |
| rep_->logging_mode = Rep::LoggingMode::kLogEveryN; |
| rep_->log_severity = level; |
| rep_->n = n; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::LogEveryN(absl::LogSeverity level, |
| int n) && { |
| return std::move(LogEveryN(level, n)); |
| } |
| |
| inline StatusBuilder& StatusBuilder::LogEvery(absl::LogSeverity level, |
| absl::Duration period) & { |
| if (rep_ == nullptr) return *this; |
| if (period <= absl::ZeroDuration()) return Log(level); |
| rep_->logging_mode = Rep::LoggingMode::kLogEveryPeriod; |
| rep_->log_severity = level; |
| rep_->period = period; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::LogEvery(absl::LogSeverity level, |
| absl::Duration period) && { |
| return std::move(LogEvery(level, period)); |
| } |
| |
| inline StatusBuilder& StatusBuilder::VLog(int verbose_level) & { |
| if (rep_ == nullptr) return *this; |
| rep_->logging_mode = Rep::LoggingMode::kVLog; |
| rep_->verbose_level = verbose_level; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::VLog(int verbose_level) && { |
| return std::move(VLog(verbose_level)); |
| } |
| |
| inline StatusBuilder& StatusBuilder::EmitStackTrace() & { |
| if (rep_ == nullptr) return *this; |
| if (rep_->logging_mode == Rep::LoggingMode::kDisabled) { |
| // Default to INFO logging, otherwise nothing would be emitted. |
| rep_->logging_mode = Rep::LoggingMode::kLog; |
| rep_->log_severity = absl::LogSeverity::kInfo; |
| } |
| rep_->should_log_stack_trace = true; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::EmitStackTrace() && { |
| return std::move(EmitStackTrace()); |
| } |
| |
| inline StatusBuilder& StatusBuilder::AlsoOutputToSink(absl::LogSink* sink) & { |
| if (rep_ == nullptr) return *this; |
| rep_->sink = sink; |
| rep_->also_send_to_log = true; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::AlsoOutputToSink(absl::LogSink* sink) && { |
| return std::move(AlsoOutputToSink(sink)); |
| } |
| inline StatusBuilder& StatusBuilder::OnlyOutputToSink(absl::LogSink* sink) & { |
| if (rep_ == nullptr) return *this; |
| rep_->sink = sink; |
| rep_->also_send_to_log = false; |
| return *this; |
| } |
| inline StatusBuilder&& StatusBuilder::OnlyOutputToSink(absl::LogSink* sink) && { |
| return std::move(OnlyOutputToSink(sink)); |
| } |
| |
| template <typename T> |
| StatusBuilder& StatusBuilder::operator<<(const T& value) & { |
| if (rep_ == nullptr) return *this; |
| if (!rep_->stream.has_value()) { |
| rep_->InitStream(); |
| } |
| *rep_->stream << value; |
| return *this; |
| } |
| |
| template <typename T> |
| StatusBuilder&& StatusBuilder::operator<<(const T& value) && { |
| return std::move(operator<<(value)); |
| } |
| |
| inline StatusBuilder& StatusBuilder::SetPayload(absl::string_view type_url, |
| absl::Cord payload) & { |
| if (rep_ != nullptr) { |
| rep_->status.SetPayload(type_url, std::move(payload)); |
| } |
| return *this; |
| } |
| |
| inline std::optional<absl::Cord> StatusBuilder::GetPayload( |
| absl::string_view type_url) const { |
| return rep_ == nullptr ? std::nullopt : rep_->status.GetPayload(type_url); |
| } |
| |
| inline bool StatusBuilder::ok() const { |
| return rep_ == nullptr ? true : rep_->status.ok(); |
| } |
| |
| inline absl::StatusCode StatusBuilder::code() const { |
| return rep_ == nullptr ? absl::StatusCode::kOk : rep_->status.code(); |
| } |
| |
| inline StatusBuilder::operator absl::Status() && { |
| // Tell the compiler that the `ok()` path will return an `Ok` status, but do |
| // it only if the compiler can determine it at compile time. |
| // When it can't, we delegate this check into the out of line function. |
| if (IsKnownToBeEmpty()) { |
| return absl::OkStatus(); |
| } |
| absl::Status result = CreateStatusAndConditionallyLog(loc_, std::move(rep_)); |
| // Tell the compiler that `rep_` was not filled again even if `this` escaped. |
| AssumeEmpty(); |
| return result; |
| } |
| |
| inline absl::SourceLocation StatusBuilder::source_location() const { |
| return loc_; |
| } |
| |
| // HasPayload() |
| // |
| // Indicates whether the Status object that will be returned by the |
| // StatusBuilder contains any payloads with a type extending proto2's |
| // `MessageSet`, returning `true` if so. Having a payload does not guarantee the |
| // presence of a payload with a specific type. Note that returning `false` does |
| // not necessarily indicate the absence of a payload, but only the absence on |
| // one which extends `MessageSet`. |
| inline bool HasPayload(const StatusBuilder& builder) { |
| return builder.HasPayload(); |
| } |
| |
| ABSL_NAMESPACE_END |
| } // namespace absl |
| |
| #endif // ABSL_STATUS_STATUS_BUILDER_H_ |