userver: userver/utils/traceful_exception.hpp Source File
Loading...
Searching...
No Matches
traceful_exception.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/traceful_exception.hpp
4/// @brief @copybrief utils::TracefulException
5
6#include <exception>
7#include <iterator>
8#include <string>
9#include <type_traits>
10
11#include <fmt/format.h>
12#include <boost/stacktrace/stacktrace_fwd.hpp>
13
14#include <userver/compiler/select.hpp>
15#include <userver/utils/fast_pimpl.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace utils {
20
21/// @brief Base class implementing backtrace storage and message builder,
22/// published only for documentation purposes, please inherit from
23/// utils::TracefulException instead.
25public:
26 enum class TraceMode {
27 kAlways,
28 kIfLoggingIsEnabled,
29 };
30
31 static constexpr size_t kInlineBufferSize = 100;
32 using MemoryBuffer = fmt::basic_memory_buffer<char, kInlineBufferSize>;
33
34 TracefulExceptionBase();
35
36 explicit TracefulExceptionBase(std::string_view what);
37
38 explicit TracefulExceptionBase(TraceMode trace_mode);
39
40 TracefulExceptionBase(TracefulExceptionBase&&) noexcept;
41 virtual ~TracefulExceptionBase() = 0;
42
43 const MemoryBuffer& MessageBuffer() const noexcept;
44 const boost::stacktrace::basic_stacktrace<>& Trace() const noexcept;
45
46 /// Stream-like interface for message extension
47 template <typename Exception, typename T>
48 friend typename std::enable_if<
49 std::is_base_of<TracefulExceptionBase, typename std::remove_reference<Exception>::type>::value,
50 Exception&&>::type
51 operator<<(Exception&& ex, const T& data) {
52 fmt::format_to(std::back_inserter(ex.GetMessageBuffer()), "{}", data);
53 ex.EnsureNullTerminated();
54 return std::forward<Exception>(ex);
55 }
56
57private:
58 void EnsureNullTerminated();
59
60 MemoryBuffer& GetMessageBuffer();
61
62 struct Impl;
63 static constexpr std::size_t kSize = sizeof(MemoryBuffer) + compiler::SelectSize().For64Bit(24).For32Bit(12);
64 utils::FastPimpl<Impl, kSize, alignof(void*)> impl_;
65};
66
67/// @ingroup userver_universal userver_base_classes
68///
69/// @brief Exception that remembers the backtrace at the point of its
70/// construction
71// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
72class TracefulException : public std::exception, public TracefulExceptionBase {
73public:
74 using TracefulExceptionBase::TracefulExceptionBase;
75
76 const char* what() const noexcept override;
77};
78
79namespace impl {
80
81template <typename PlainException>
82// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
83class ExceptionWithAttachedTrace final : public PlainException, public TracefulExceptionBase {
84public:
85 explicit ExceptionWithAttachedTrace(const PlainException& ex)
86 : PlainException(ex),
87 TracefulExceptionBase(ex.what())
88 {}
89};
90
91template <typename Exception>
92std::enable_if_t<std::is_base_of<std::exception, Exception>::value, ExceptionWithAttachedTrace<Exception>>
93AttachTraceToException(const Exception& ex) {
94 static_assert(!std::is_base_of<TracefulExceptionBase, Exception>::value, "This exception already contains trace");
95 return ExceptionWithAttachedTrace<Exception>(ex);
96}
97
98} // namespace impl
99} // namespace utils
100
101USERVER_NAMESPACE_END