Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-19 08:27:44

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 // STL include(s)
0012 #include <ctime>
0013 #include <iomanip>
0014 #include <iostream>
0015 #include <memory>
0016 #include <mutex>
0017 #include <optional>
0018 #include <sstream>
0019 #include <stdexcept>
0020 #include <string>
0021 #include <string_view>
0022 #include <thread>
0023 #include <utility>
0024 
0025 /// @addtogroup logging
0026 /// @{
0027 
0028 /// @defgroup logging_macros Logging Macros
0029 /// @ingroup logging
0030 /// @brief Helper macros for logging with @ref Acts::Logger
0031 ///
0032 /// When a logger accessible via the `logger()` method, see @ref logging_patterns,
0033 /// use these macros to perform the actual logging:
0034 ///
0035 /// @snippet{trimleft} examples/logging.cpp Logging Macros
0036 ///
0037 /// The macros support stream-style formatting with `<<` operators.
0038 /// @{
0039 
0040 /// @brief Macro to use a local Acts::Logger object
0041 ///
0042 /// @param log_object logger instance of type
0043 //         `std::unique_ptr<const Acts::Logger>`
0044 ///
0045 /// @pre In the current scope, the symbol @c logger is not yet defined.
0046 /// @post The ownership of the given @c log_object is transferred and
0047 ///       @c log_object should not be used directly any more.
0048 ///
0049 /// This macro allows to use a locally defined logging object with the ACTS_*
0050 /// logging macros. The envisaged usage is the following:
0051 ///
0052 /// @snippet{trimleft} examples/logging.cpp Local logger macro
0053 #define ACTS_LOCAL_LOGGER(log_object)                                          \
0054   struct __local_acts_logger {                                                 \
0055     explicit __local_acts_logger(std::unique_ptr<const ::Acts::Logger> logger) \
0056         : m_logger(std::move(logger)) {}                                       \
0057                                                                                \
0058     const ::Acts::Logger& operator()() const { return *m_logger; }             \
0059                                                                                \
0060     std::unique_ptr<const ::Acts::Logger> m_logger;                            \
0061   };                                                                           \
0062   __local_acts_logger logger(log_object);
0063 
0064 /// Log a message at the specified level with an explicit logger instance
0065 /// @param lgr The logger instance (must be a Acts::Logger reference)
0066 /// @param level The logging level
0067 /// @param x The message to log
0068 #define ACTS_LOG_WITH_LOGGER(lgr, level, x) \
0069   do {                                      \
0070     if ((lgr).doPrint(level)) {             \
0071       std::ostringstream os;                \
0072       os << x;                              \
0073       (lgr).log(level, os.str());           \
0074     }                                       \
0075   } while (0)
0076 
0077 /// Log a message at the specified level
0078 /// @param level The logging level
0079 /// @param x The message to log
0080 #define ACTS_LOG(level, x) ACTS_LOG_WITH_LOGGER(logger(), level, x)
0081 
0082 /// @brief macro for verbose debug output
0083 ///
0084 /// @param x debug message
0085 ///
0086 /// @pre @c logger() must be a valid expression in the scope where this
0087 ///      macro is used and it must return a Acts::Logger object.
0088 ///
0089 /// The debug message is printed if the current Acts::Logging::Level <=
0090 /// Acts::Logging::VERBOSE.
0091 #define ACTS_VERBOSE(x) ACTS_LOG(Acts::Logging::VERBOSE, x)
0092 
0093 /// @brief macro for debug debug output
0094 ///
0095 /// @param x debug message
0096 ///
0097 /// @pre @c logger() must be a valid expression in the scope where this
0098 ///      macro is used and it must return a Acts::Logger object.
0099 ///
0100 /// The debug message is printed if the current Acts::Logging::Level <=
0101 /// Acts::Logging::DEBUG.
0102 #define ACTS_DEBUG(x) ACTS_LOG(Acts::Logging::DEBUG, x)
0103 
0104 /// @brief macro for info debug output
0105 ///
0106 /// @param x debug message
0107 ///
0108 /// @pre @c logger() must be a valid expression in the scope where this
0109 ///      macro is used and it must return a Acts::Logger object.
0110 ///
0111 /// The debug message is printed if the current Acts::Logging::Level <=
0112 /// Acts::Logging::INFO.
0113 #define ACTS_INFO(x) ACTS_LOG(Acts::Logging::INFO, x)
0114 
0115 /// @brief macro for warning debug output
0116 ///
0117 /// @param x debug message
0118 ///
0119 /// @pre @c logger() must be a valid expression in the scope where this
0120 ///      macro is used and it must return a Acts::Logger object.
0121 ///
0122 /// The debug message is printed if the current Acts::Logging::Level <=
0123 /// Acts::Logging::WARNING.
0124 #define ACTS_WARNING(x) ACTS_LOG(Acts::Logging::WARNING, x)
0125 
0126 /// @brief macro for error debug output
0127 ///
0128 /// @param x debug message
0129 ///
0130 /// @pre @c logger() must be a valid expression in the scope where this
0131 ///      macro is used and it must return a Acts::Logger object.
0132 ///
0133 /// The debug message is printed if the current Acts::Logging::Level <=
0134 /// Acts::Logging::ERROR.
0135 #define ACTS_ERROR(x) ACTS_LOG(Acts::Logging::ERROR, x)
0136 
0137 /// @brief macro for fatal debug output
0138 ///
0139 /// @param x debug message
0140 ///
0141 /// @pre @c logger() must be a valid expression in the scope where this
0142 ///      macro is used and it must return a Acts::Logger object.
0143 ///
0144 /// The debug message is printed if the current Acts::Logging::Level <=
0145 /// Acts::Logging::FATAL.
0146 #define ACTS_FATAL(x) ACTS_LOG(Acts::Logging::FATAL, x)
0147 
0148 /// @}
0149 /// @}
0150 
0151 namespace Acts {
0152 
0153 namespace Logging {
0154 
0155 /// @addtogroup logging
0156 /// @{
0157 
0158 /// @brief constants steering the debug output
0159 ///
0160 /// All messages with a debug level equal or higher than the currently set
0161 /// debug output level will be printed.
0162 enum Level {
0163   VERBOSE = 0,  ///< Detailed diagnostic trace information
0164   DEBUG,        ///< Debug information during development
0165   INFO,         ///< General information messages
0166   WARNING,      ///< Non-critical error conditions
0167   ERROR,        ///< Error conditions which require follow-up
0168   FATAL,        ///< Unrecoverable error conditions
0169   MAX           ///< Filler level
0170 };
0171 
0172 /// @brief Get the string name for a logging level
0173 /// @param level The logging level
0174 /// @return String representation of the logging level
0175 inline std::string_view levelName(Level level) {
0176   switch (level) {
0177     case Level::VERBOSE:
0178       return "VERBOSE";
0179     case Level::DEBUG:
0180       return "DEBUG";
0181     case Level::INFO:
0182       return "INFO";
0183     case Level::WARNING:
0184       return "WARNING";
0185     case Level::ERROR:
0186       return "ERROR";
0187     case Level::FATAL:
0188       return "FATAL";
0189     case Level::MAX:
0190       return "MAX";
0191     default:
0192       throw std::invalid_argument{"Unknown level"};
0193   }
0194 }
0195 
0196 /// @defgroup logging_thresholds Logging Thresholds
0197 /// @ingroup logging
0198 /// @brief Functions and classes to manage logging failure thresholds
0199 ///
0200 /// Generally, log levels in ACTS are only of informative value: even
0201 /// @ref Acts::Logging::Level::ERROR and @ref Acts::Logging::Level::FATAL will only print
0202 /// messages, **and not terminate execution**.
0203 ///
0204 /// This is desirable in an experiment context, where jobs should not
0205 /// immediately terminate when ACTS encounters something that is logged as an
0206 /// error. In a test context, however, this behavior is not optimal: the tests
0207 /// should ensure in known configurations errors do not occur, or only in
0208 /// specific circumstances. To solve this, ACTS implements an optional log
0209 /// *threshold* mechanism.
0210 ///
0211 /// The threshold mechanism is steered via the CMake option
0212 /// `ACTS_ENABLE_LOG_FAILURE_THRESHOLD`, so the logging operates in one of two
0213 /// modes:
0214 ///
0215 /// 1. **No log failure threshold** exists, log levels are informative only.
0216 ///    This is the default behavior.
0217 /// 2. A **runtime log failure threshold** is available. With
0218 ///    `ACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON` the logger code compiles in a
0219 ///    check against a global threshold variable, seeded from the
0220 ///    `ACTS_LOG_FAILURE_THRESHOLD` environment variable and settable with
0221 ///    @ref Acts::Logging::setFailureThreshold. A message at or above it raises
0222 ///    @ref Acts::Logging::ThresholdFailure after it has been emitted. The
0223 ///    threshold defaults to @ref Acts::Logging::Level::MAX, so an enabled build
0224 ///    with no threshold set behaves like a disabled one.
0225 ///
0226 /// @{
0227 
0228 #ifdef DOXYGEN
0229 /// @brief Get debug level above which an exception will be thrown after logging
0230 ///
0231 /// All messages with a debug level equal or higher than the return value of
0232 /// this function will cause an exception to be thrown after log emission.
0233 ///
0234 /// @note Depending on the preprocessor setting @c ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0235 ///       this operation is either constexpr or a runtime operation.
0236 /// @return The log level threshold for failure
0237 Level getFailureThreshold();
0238 
0239 #else
0240 
0241 #ifdef ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0242 Level getFailureThreshold();
0243 #else
0244 constexpr Level getFailureThreshold() {
0245   // Default "NO" failure threshold
0246   return Level::MAX;
0247 }
0248 #endif
0249 
0250 #endif
0251 
0252 /// @brief Set debug level above which an exception will be thrown after logging
0253 ///
0254 /// All messages with a debug level equal or higher than @p level will
0255 /// cause an exception to be thrown after log emission.
0256 ///
0257 /// @warning The runtime log failure threshold is **global state**, therefore
0258 ///          this function is  **not threadsafe**. The intention is that this
0259 ///          level is set once, before multi-threaded execution begins, and then
0260 ///          not modified before the end of the job.
0261 /// @note This function is only available if @c ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0262 ///       is set. Otherwise an exception is thrown.
0263 /// @param level Log level above which exceptions will be thrown
0264 void setFailureThreshold(Level level);
0265 
0266 /// Custom exception class so threshold failures can be caught
0267 class ThresholdFailure : public std::runtime_error {
0268   using std::runtime_error::runtime_error;
0269 };
0270 
0271 /// Helper class that changes the failure threshold for the duration of its
0272 /// lifetime.
0273 class ScopedFailureThreshold {
0274  public:
0275   /// Constructor that sets the failure threshold for the scope
0276   /// @param level The logging level to set as failure threshold
0277   explicit ScopedFailureThreshold(Level level) { setFailureThreshold(level); }
0278   ScopedFailureThreshold(const ScopedFailureThreshold&) = delete;
0279   ScopedFailureThreshold& operator=(const ScopedFailureThreshold&) = delete;
0280   ScopedFailureThreshold(ScopedFailureThreshold&&) = delete;
0281   ScopedFailureThreshold& operator=(ScopedFailureThreshold&&) = delete;
0282 
0283   ~ScopedFailureThreshold() noexcept;
0284 
0285  private:
0286   Level m_previousLevel{getFailureThreshold()};
0287 };
0288 
0289 /// @}
0290 
0291 /// @brief abstract base class for printing debug output
0292 ///
0293 /// Implementations of this interface need to define how and where to @a print
0294 /// debug messages (e.g. to a file, to a stream into a database etc).
0295 class OutputPrintPolicy {
0296  public:
0297   /// virtual default destructor
0298   virtual ~OutputPrintPolicy() = default;
0299 
0300   /// @brief handle output of debug message
0301   ///
0302   /// @param [in] lvl   debug output level of message
0303   /// @param [in] input text of debug message
0304   virtual void flush(const Level& lvl, const std::string& input) = 0;
0305 
0306   /// Return the name of the print policy
0307   /// @return the name
0308   virtual const std::string& name() const = 0;
0309 
0310   /// Make a copy of this print policy with a new name
0311   /// @param name the new name
0312   /// @return the copy
0313   virtual std::unique_ptr<OutputPrintPolicy> clone(
0314       const std::string& name) const = 0;
0315 };
0316 
0317 /// @brief abstract base class for filtering debug output
0318 ///
0319 /// Implementations of this interface need to define whether a debug message
0320 /// with a certain debug level is processed or filtered out.
0321 class OutputFilterPolicy {
0322  public:
0323   /// virtual default destructor
0324   virtual ~OutputFilterPolicy() = default;
0325 
0326   /// @brief decide whether a debug message should be processed
0327   ///
0328   /// @param [in] lvl debug level of debug message
0329   ///
0330   /// @return @c true of debug message should be processed, @c false if debug
0331   ///         message should be skipped
0332   virtual bool doPrint(const Level& lvl) const = 0;
0333 
0334   /// Get the level of this filter policy
0335   /// @return the levele
0336   virtual Level level() const = 0;
0337 
0338   /// Make a copy of this filter policy with a new level
0339   /// @param level the new level
0340   /// @return the new copy
0341   virtual std::unique_ptr<OutputFilterPolicy> clone(Level level) const = 0;
0342 };
0343 
0344 /// @brief default filter policy for debug messages
0345 ///
0346 /// All debug messages with a debug level equal or larger to the specified
0347 /// threshold level are processed.
0348 class DefaultFilterPolicy final : public OutputFilterPolicy {
0349  public:
0350   /// @brief constructor
0351   ///
0352   /// @param [in] lvl threshold debug level
0353   explicit DefaultFilterPolicy(Level lvl) : m_level(lvl) {
0354     if (lvl > getFailureThreshold()) {
0355       throw ThresholdFailure(
0356           "Requested debug level is incompatible with "
0357           "the ACTS_LOG_FAILURE_THRESHOLD=" +
0358           std::string{levelName(getFailureThreshold())} +
0359           " configuration. See "
0360           "https://cern.ch/acts-log-thresh");
0361     }
0362   }
0363 
0364   /// virtual default destructor
0365   ~DefaultFilterPolicy() override = default;
0366 
0367   /// @brief decide whether a debug message should be processed
0368   ///
0369   /// @param [in] lvl debug level of debug message
0370   ///
0371   /// @return @c true if @p lvl >= #m_level, otherwise @c false
0372   bool doPrint(const Level& lvl) const override { return m_level <= lvl; }
0373 
0374   /// Get the level of this filter policy
0375   /// @return the levele
0376   Level level() const override { return m_level; }
0377 
0378   /// Make a copy of this filter policy with a new level
0379   /// @param level the new level
0380   /// @return the new copy
0381   std::unique_ptr<OutputFilterPolicy> clone(Level level) const override {
0382     return std::make_unique<DefaultFilterPolicy>(level);
0383   }
0384 
0385  private:
0386   /// threshold debug level for messages to be processed
0387   Level m_level;
0388 };
0389 
0390 /// @brief base class for decorating the debug output
0391 ///
0392 /// Derived classes may augment the debug message with additional information.
0393 /// Chaining different decorators is possible to customize the output to your
0394 /// needs.
0395 class OutputDecorator : public OutputPrintPolicy {
0396  public:
0397   /// @brief constructor wrapping actual output print policy
0398   ///
0399   /// @param [in] wrappee output print policy object which is wrapped by this
0400   ///        decorator object
0401   explicit OutputDecorator(std::unique_ptr<OutputPrintPolicy> wrappee)
0402       : m_wrappee(std::move(wrappee)) {}
0403 
0404   /// @brief flush the debug message to the destination stream
0405   ///
0406   /// @param [in] lvl   debug level of debug message
0407   /// @param [in] input text of debug message
0408   ///
0409   /// This function delegates the flushing of the debug message to its wrapped
0410   /// object.
0411   void flush(const Level& lvl, const std::string& input) override {
0412     m_wrappee->flush(lvl, input);
0413   }
0414 
0415   /// Return the name of the output decorator (forwards to wrappee)
0416   /// @return the name
0417   const std::string& name() const override { return m_wrappee->name(); }
0418 
0419  protected:
0420   /// wrapped object for printing the debug message
0421   std::unique_ptr<OutputPrintPolicy> m_wrappee;
0422 };
0423 
0424 /// @brief decorate debug message with a name
0425 ///
0426 /// The debug message is complemented with a name.
0427 class NamedOutputDecorator final : public OutputDecorator {
0428  public:
0429   /// @brief constructor
0430   ///
0431   /// @param [in] wrappee  output print policy object to be wrapped
0432   /// @param [in] name     name to be added to debug message
0433   /// @param [in] maxWidth maximum width of field used for name
0434   NamedOutputDecorator(std::unique_ptr<OutputPrintPolicy> wrappee,
0435                        const std::string& name, unsigned int maxWidth = 15)
0436       : OutputDecorator(std::move(wrappee)),
0437         m_name(name),
0438         m_maxWidth(maxWidth) {}
0439 
0440   /// @brief flush the debug message to the destination stream
0441   ///
0442   /// @param [in] lvl   debug level of debug message
0443   /// @param [in] input text of debug message
0444   ///
0445   /// This function prepends the given name to the debug message and then
0446   /// delegates the flushing of the whole message to its wrapped object.
0447   void flush(const Level& lvl, const std::string& input) override {
0448     std::ostringstream os;
0449     os << std::left << std::setw(static_cast<int>(m_maxWidth))
0450        << m_name.substr(0, m_maxWidth - 3) << input;
0451     OutputDecorator::flush(lvl, os.str());
0452   }
0453 
0454   /// Make a copy of this print policy with a new name
0455   /// @param name the new name
0456   /// @return the copy
0457   std::unique_ptr<OutputPrintPolicy> clone(
0458       const std::string& name) const override {
0459     return std::make_unique<NamedOutputDecorator>(m_wrappee->clone(name), name,
0460                                                   m_maxWidth);
0461   }
0462 
0463   /// Get this named output decorators name
0464   /// @return the name
0465   const std::string& name() const override { return m_name; }
0466 
0467  private:
0468   /// name to be prepended
0469   std::string m_name;
0470 
0471   /// maximum width of field for printing the name
0472   unsigned int m_maxWidth;
0473 };
0474 
0475 /// @brief decorate debug message with a time stamp
0476 ///
0477 /// The debug message is complemented with a time stamp.
0478 class TimedOutputDecorator final : public OutputDecorator {
0479  public:
0480   /// @brief constructor
0481   ///
0482   /// @param [in] wrappee output print policy object to be wrapped
0483   /// @param [in] format  format of time stamp (see std::strftime)
0484   explicit TimedOutputDecorator(std::unique_ptr<OutputPrintPolicy> wrappee,
0485                                 const std::string& format = "%X")
0486       : OutputDecorator(std::move(wrappee)), m_format(format) {}
0487 
0488   /// @brief flush the debug message to the destination stream
0489   ///
0490   /// @param [in] lvl   debug level of debug message
0491   /// @param [in] input text of debug message
0492   ///
0493   /// This function prepends a time stamp to the debug message and then
0494   /// delegates the flushing of the whole message to its wrapped object.
0495   void flush(const Level& lvl, const std::string& input) override {
0496     std::ostringstream os;
0497     os << std::left << std::setw(12) << now() << input;
0498     OutputDecorator::flush(lvl, os.str());
0499   }
0500 
0501   /// Make a copy of this print policy with a new name
0502   /// @param name the new name
0503   /// @return the copy
0504   std::unique_ptr<OutputPrintPolicy> clone(
0505       const std::string& name) const override {
0506     return std::make_unique<TimedOutputDecorator>(m_wrappee->clone(name),
0507                                                   m_format);
0508   }
0509 
0510  private:
0511   /// @brief get current time stamp
0512   ///
0513   /// @return current time stamp as string
0514   std::string now() const {
0515     char buffer[20];
0516     time_t t{};
0517     std::time(&t);
0518     struct tm tbuf{};
0519     std::strftime(buffer, sizeof(buffer), m_format.c_str(),
0520                   localtime_r(&t, &tbuf));
0521     return buffer;
0522   }
0523 
0524   /// format of the time stamp (see std::strftime for details)
0525   std::string m_format;
0526 };
0527 
0528 /// @brief decorate debug message with a thread ID
0529 ///
0530 /// The debug message is complemented with a thread ID.
0531 class ThreadOutputDecorator final : public OutputDecorator {
0532  public:
0533   /// @brief constructor
0534   ///
0535   /// @param [in] wrappee output print policy object to be wrapped
0536   explicit ThreadOutputDecorator(std::unique_ptr<OutputPrintPolicy> wrappee)
0537       : OutputDecorator(std::move(wrappee)) {}
0538 
0539   /// @brief flush the debug message to the destination stream
0540   ///
0541   /// @param [in] lvl   debug level of debug message
0542   /// @param [in] input text of debug message
0543   ///
0544   /// This function prepends the thread ID to the debug message and then
0545   /// delegates the flushing of the whole message to its wrapped object.
0546   void flush(const Level& lvl, const std::string& input) override {
0547     std::ostringstream os;
0548     os << std::left << std::setw(20) << std::this_thread::get_id() << input;
0549     OutputDecorator::flush(lvl, os.str());
0550   }
0551 
0552   /// Make a copy of this print policy with a new name
0553   /// @param name the new name
0554   /// @return the copy
0555   std::unique_ptr<OutputPrintPolicy> clone(
0556       const std::string& name) const override {
0557     return std::make_unique<ThreadOutputDecorator>(m_wrappee->clone(name));
0558   }
0559 };
0560 
0561 /// @brief decorate debug message with its debug level
0562 ///
0563 /// The debug message is complemented with its debug level.
0564 class LevelOutputDecorator final : public OutputDecorator {
0565  public:
0566   /// @brief constructor
0567   ///
0568   /// @param [in] wrappee output print policy object to be wrapped
0569   explicit LevelOutputDecorator(std::unique_ptr<OutputPrintPolicy> wrappee)
0570       : OutputDecorator(std::move(wrappee)) {}
0571 
0572   /// @brief flush the debug message to the destination stream
0573   ///
0574   /// @param [in] lvl   debug level of debug message
0575   /// @param [in] input text of debug message
0576   ///
0577   /// This function prepends the debug level to the debug message and then
0578   /// delegates the flushing of the whole message to its wrapped object.
0579   void flush(const Level& lvl, const std::string& input) override {
0580     std::ostringstream os;
0581     os << std::left << std::setw(10) << toString(lvl) << input;
0582     OutputDecorator::flush(lvl, os.str());
0583   }
0584 
0585   /// Make a copy of this print policy with a new name
0586   /// @param name the new name
0587   /// @return the copy
0588   std::unique_ptr<OutputPrintPolicy> clone(
0589       const std::string& name) const override {
0590     return std::make_unique<LevelOutputDecorator>(m_wrappee->clone(name));
0591   }
0592 
0593  private:
0594   /// @brief convert debug level to string
0595   ///
0596   /// @param [in] lvl debug level
0597   ///
0598   /// @return string representation of debug level
0599   std::string toString(const Level& lvl) const {
0600     static const char* const buffer[] = {"VERBOSE", "DEBUG", "INFO",
0601                                          "WARNING", "ERROR", "FATAL"};
0602     return buffer[lvl];
0603   }
0604 };
0605 
0606 /// @brief default print policy for debug messages
0607 ///
0608 /// This class allows to print debug messages without further modifications to
0609 /// a specified output stream.
0610 class DefaultPrintPolicy final : public OutputPrintPolicy {
0611  public:
0612   /// @brief constructor
0613   ///
0614   /// @param [in] out pointer to output stream object
0615   ///
0616   /// @pre @p out is non-zero
0617   explicit DefaultPrintPolicy(std::ostream* out = &std::cout) : m_out(out) {}
0618 
0619   /// @brief flush the debug message to the destination stream
0620   ///
0621   /// @param [in] lvl   debug level of debug message
0622   /// @param [in] input text of debug message
0623   void flush(const Level& lvl, const std::string& input) final {
0624     // Mutex to serialize access to std::cout
0625     static std::mutex s_stdoutMutex;
0626     std::unique_lock lock{s_stdoutMutex,
0627                           std::defer_lock};  // prep empty, we might not need it
0628 
0629     if (m_out == &std::cout) {
0630       lock.lock();  // lock only if we are printing to std::cout
0631     }
0632 
0633     (*m_out) << input << std::endl;
0634     if (lvl >= getFailureThreshold()) {
0635       throw ThresholdFailure(
0636           "Previous debug message exceeds the "
0637           "ACTS_LOG_FAILURE_THRESHOLD=" +
0638           std::string{levelName(getFailureThreshold())} +
0639           " configuration, bailing out. See "
0640           "https://acts.readthedocs.io/en/latest/core/misc/"
0641           "logging.html#logging-thresholds");
0642     }
0643   }
0644 
0645   /// Fulfill @c OutputPrintPolicy interface. This policy doesn't actually have a
0646   /// name, so the assumption is that somewhere in the decorator hierarchy,
0647   /// there is something that returns a name without delegating to a wrappee,
0648   /// before reaching this overload.
0649   /// @note This method will throw an exception
0650   /// @return the name, but it never returns
0651   const std::string& name() const override {
0652     throw std::runtime_error{
0653         "Default print policy doesn't have a name. Is there no named output in "
0654         "the decorator chain?"};
0655   };
0656 
0657   /// Make a copy of this print policy with a new name
0658   /// @return the copy
0659   std::unique_ptr<OutputPrintPolicy> clone(
0660       const std::string& /*name*/) const override {
0661     return std::make_unique<DefaultPrintPolicy>(m_out);
0662   };
0663 
0664  private:
0665   /// pointer to destination output stream
0666   std::ostream* m_out;
0667 };
0668 
0669 /// @}
0670 
0671 }  // namespace Logging
0672 
0673 /// @brief class for printing debug output
0674 /// @ingroup logging
0675 ///
0676 /// This class provides the user interface for printing debug messages with
0677 /// different levels of severity.
0678 ///
0679 class Logger {
0680  public:
0681   /// @brief construct from output print and filter policy
0682   ///
0683   /// @param [in] pPrint  policy for printing debug messages
0684   /// @param [in] pFilter policy for filtering debug messages
0685   Logger(std::unique_ptr<Logging::OutputPrintPolicy> pPrint,
0686          std::unique_ptr<Logging::OutputFilterPolicy> pFilter)
0687       : m_printPolicy(std::move(pPrint)), m_filterPolicy(std::move(pFilter)) {}
0688 
0689   /// @brief decide whether a message with a given debug level has to be printed
0690   ///
0691   /// @param [in] lvl debug level of debug message
0692   ///
0693   /// @return @c true if debug message should be printed, otherwise @c false
0694   bool doPrint(const Logging::Level& lvl) const {
0695     return m_filterPolicy->doPrint(lvl);
0696   }
0697 
0698   /// @brief log a debug message
0699   ///
0700   /// @param [in] lvl debug level of debug message
0701   /// @param [in] input text of debug message
0702   void log(const Logging::Level& lvl, const std::string& input) const {
0703     if (doPrint(lvl)) {
0704       m_printPolicy->flush(lvl, input);
0705     }
0706   }
0707 
0708   /// Return the print policy for this logger
0709   /// @return the print policy
0710   const Logging::OutputPrintPolicy& printPolicy() const {
0711     return *m_printPolicy;
0712   }
0713 
0714   /// Return the filter policy for this logger
0715   /// @return the filter policy
0716   const Logging::OutputFilterPolicy& filterPolicy() const {
0717     return *m_filterPolicy;
0718   }
0719 
0720   /// Return the level of the filter policy of this logger
0721   /// @return the level
0722   Logging::Level level() const { return m_filterPolicy->level(); }
0723 
0724   /// Return the name of the print policy of this logger
0725   /// @return the name
0726   const std::string& name() const { return m_printPolicy->name(); }
0727 
0728   /// Make a copy of this logger, optionally changing the name or the level
0729   /// @param _name the optional new name
0730   /// @param _level the optional new level
0731   /// @return Unique pointer to a cloned logger
0732   std::unique_ptr<Logger> clone(
0733       const std::optional<std::string>& _name = std::nullopt,
0734       const std::optional<Logging::Level>& _level = std::nullopt) const {
0735     return std::make_unique<Logger>(
0736         m_printPolicy->clone(_name.value_or(name())),
0737         m_filterPolicy->clone(_level.value_or(level())));
0738   }
0739 
0740   /// Make a copy of the logger, with a new level. Convenience function for
0741   /// if you only want to change the level but not the name.
0742   /// @param _level the new level
0743   /// @return the new logger
0744   std::unique_ptr<Logger> clone(Logging::Level _level) const {
0745     return clone(std::nullopt, _level);
0746   }
0747 
0748   /// Make a copy of the logger, with a suffix added to the end of it's
0749   /// name. You can also optionally supply a new level
0750   /// @param suffix the suffix to add to the end of the name
0751   /// @param _level the optional new level
0752   /// @return Unique pointer to a cloned logger with modified name
0753   std::unique_ptr<Logger> cloneWithSuffix(
0754       const std::string& suffix,
0755       std::optional<Logging::Level> _level = std::nullopt) const {
0756     return clone(name() + suffix, _level.value_or(level()));
0757   }
0758 
0759   /// Helper function so a logger reference can be used as is with the logging
0760   /// macros
0761   /// @return Reference to this logger
0762   const Logger& operator()() const { return *this; }
0763 
0764  private:
0765   /// policy object for printing debug messages
0766   std::unique_ptr<Logging::OutputPrintPolicy> m_printPolicy;
0767 
0768   /// policy object for filtering debug messages
0769   std::unique_ptr<Logging::OutputFilterPolicy> m_filterPolicy;
0770 };
0771 
0772 /// @brief get default debug output logger
0773 ///
0774 /// @param [in] name       name of the logger instance
0775 /// @param [in] lvl        debug threshold level
0776 /// @param [in] log_stream output stream used for printing debug messages
0777 ///
0778 /// This function returns a pointer to a Logger instance with the following
0779 /// decorations enabled:
0780 /// - time stamps
0781 /// - name of logging instance
0782 /// - debug level
0783 ///
0784 /// @return pointer to logging instance
0785 std::unique_ptr<const Logger> getDefaultLogger(
0786     const std::string& name, const Logging::Level& lvl,
0787     std::ostream* log_stream = &std::cout);
0788 
0789 /// Get a dummy logger that discards all output
0790 /// @return Reference to dummy logger instance
0791 const Logger& getDummyLogger();
0792 
0793 }  // namespace Acts