Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-14 07:54:59

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