Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:10:45

0001 /// \file ROOT/RLogger.hxx
0002 /// \ingroup Base ROOT7
0003 /// \author Axel Naumann <axel@cern.ch>
0004 /// \date 2015-03-29
0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
0006 /// is welcome!
0007 
0008 /*************************************************************************
0009  * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers.               *
0010  * All rights reserved.                                                  *
0011  *                                                                       *
0012  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0013  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0014  *************************************************************************/
0015 
0016 #ifndef ROOT7_RLogger
0017 #define ROOT7_RLogger
0018 
0019 #include <atomic>
0020 #include <list>
0021 #include <memory>
0022 #include <mutex>
0023 #include <sstream>
0024 #include <string>
0025 #include <utility>
0026 
0027 namespace ROOT {
0028 namespace Experimental {
0029 
0030 class RLogEntry;
0031 class RLogManager;
0032 
0033 /**
0034  Kinds of diagnostics.
0035  */
0036 enum class ELogLevel : unsigned char {
0037    kUnset,
0038    kFatal,   ///< An error which causes further processing to be unreliable
0039    kError,   ///< An error
0040    kWarning, ///< Warnings about likely unexpected behavior
0041    kInfo,    ///< Informational messages; used for instance for tracing
0042    kDebug    ///< Debug information; only useful for developers; can have added verbosity up to 255-kDebug.
0043 };
0044 
0045 inline ELogLevel operator+(ELogLevel severity, int offset)
0046 {
0047    return static_cast<ELogLevel>(static_cast<int>(severity) + offset);
0048 }
0049 
0050 /**
0051  Keep track of emitted errors and warnings.
0052  */
0053 class RLogDiagCount {
0054 protected:
0055    std::atomic<long long> fNumWarnings{0ll};    /// Number of warnings.
0056    std::atomic<long long> fNumErrors{0ll};      /// Number of errors.
0057    std::atomic<long long> fNumFatalErrors{0ll}; /// Number of fatal errors.
0058 
0059 public:
0060    /// Returns the current number of warnings.
0061    long long GetNumWarnings() const { return fNumWarnings; }
0062 
0063    /// Returns the current number of errors.
0064    long long GetNumErrors() const { return fNumErrors; }
0065 
0066    /// Returns the current number of fatal errors.
0067    long long GetNumFatalErrors() const { return fNumFatalErrors; }
0068 
0069    /// Increase warning or error count.
0070    void Increment(ELogLevel severity)
0071    {
0072       switch (severity) {
0073       case ELogLevel::kFatal: ++fNumFatalErrors; break;
0074       case ELogLevel::kError: ++fNumErrors; break;
0075       case ELogLevel::kWarning: ++fNumWarnings; break;
0076       default:;
0077       }
0078    }
0079 };
0080 
0081 /**
0082  Abstract RLogHandler base class. ROOT logs everything from info to error
0083  to entities of this class.
0084  */
0085 class RLogHandler {
0086 public:
0087    virtual ~RLogHandler();
0088    /// Emit a log entry.
0089    /// \param entry - the RLogEntry to be emitted.
0090    /// \returns false if further emission of this Log should be suppressed.
0091    ///
0092    /// \note This function is called concurrently; log emission must be locked
0093    /// if needed. (The default log handler using ROOT's DefaultErrorHandler is locked.)
0094    virtual bool Emit(const RLogEntry &entry) = 0;
0095 };
0096 
0097 /**
0098  A log configuration for a channel, e.g. "RHist".
0099  Each ROOT module has its own log, with potentially distinct verbosity.
0100  */
0101 class RLogChannel : public RLogDiagCount {
0102    /// Name as shown in diagnostics
0103    std::string fName;
0104 
0105    /// Verbosity of this channel. By default, use the global verbosity.
0106    ELogLevel fVerbosity = ELogLevel::kUnset;
0107 
0108 public:
0109    /// Construct an anonymous channel.
0110    RLogChannel() = default;
0111 
0112    /// Construct an anonymous channel with a default verbosity.
0113    explicit RLogChannel(ELogLevel verbosity) : fVerbosity(verbosity) {}
0114 
0115    /// Construct a log channel given its name, which is part of the diagnostics.
0116    RLogChannel(const std::string &name) : fName(name) {}
0117 
0118    ELogLevel SetVerbosity(ELogLevel verbosity)
0119    {
0120       std::swap(fVerbosity, verbosity);
0121       return verbosity;
0122    }
0123    ELogLevel GetVerbosity() const { return fVerbosity; }
0124    ELogLevel GetEffectiveVerbosity(const RLogManager &mgr) const;
0125 
0126    const std::string &GetName() const { return fName; }
0127 };
0128 
0129 /**
0130  A RLogHandler that multiplexes diagnostics to different client `RLogHandler`s
0131  and keeps track of the sum of `RLogDiagCount`s for all channels.
0132 
0133  `RLogHandler::Get()` returns the process's (static) log manager.
0134  */
0135 
0136 class RLogManager : public RLogChannel, public RLogHandler {
0137    std::mutex fMutex;
0138    std::list<std::unique_ptr<RLogHandler>> fHandlers;
0139 
0140 public:
0141    /// Initialize taking a RLogHandler.
0142    RLogManager(std::unique_ptr<RLogHandler> lh) : RLogChannel(ELogLevel::kWarning)
0143    {
0144       fHandlers.emplace_back(std::move(lh));
0145    }
0146 
0147    static RLogManager &Get();
0148 
0149    /// Add a RLogHandler in the front - to be called before all others.
0150    void PushFront(std::unique_ptr<RLogHandler> handler) { fHandlers.emplace_front(std::move(handler)); }
0151 
0152    /// Add a RLogHandler in the back - to be called after all others.
0153    void PushBack(std::unique_ptr<RLogHandler> handler) { fHandlers.emplace_back(std::move(handler)); }
0154 
0155    /// Remove and return the given log handler. Returns `nullptr` if not found.
0156    std::unique_ptr<RLogHandler> Remove(RLogHandler *handler);
0157 
0158    // Emit a `RLogEntry` to the RLogHandlers.
0159    // Returns false if further emission of this Log should be suppressed.
0160    bool Emit(const RLogEntry &entry) override;
0161 };
0162 
0163 /**
0164  A diagnostic location, part of an RLogEntry.
0165  */
0166 struct RLogLocation {
0167    std::string fFile;
0168    std::string fFuncName;
0169    int fLine; // C++11 forbids "= 0" for braced-init-list initialization.
0170 };
0171 
0172 /**
0173  A diagnostic that can be emitted by the RLogManager.
0174  One can construct a RLogEntry through RLogBuilder, including streaming into
0175  the diagnostic message and automatic emission.
0176  */
0177 
0178 class RLogEntry {
0179 public:
0180    RLogLocation fLocation;
0181    std::string fMessage;
0182    RLogChannel *fChannel = nullptr;
0183    ELogLevel fLevel = ELogLevel::kFatal;
0184 
0185    RLogEntry(ELogLevel level, RLogChannel &channel) : fChannel(&channel), fLevel(level) {}
0186    RLogEntry(ELogLevel level, RLogChannel &channel, const RLogLocation &loc)
0187       : fLocation(loc), fChannel(&channel), fLevel(level)
0188    {
0189    }
0190 
0191    bool IsDebug() const { return fLevel >= ELogLevel::kDebug; }
0192    bool IsInfo() const { return fLevel == ELogLevel::kInfo; }
0193    bool IsWarning() const { return fLevel == ELogLevel::kWarning; }
0194    bool IsError() const { return fLevel == ELogLevel::kError; }
0195    bool IsFatal() const { return fLevel == ELogLevel::kFatal; }
0196 };
0197 
0198 namespace Detail {
0199 /**
0200  Builds a diagnostic entry, emitted by the static RLogManager upon destruction of this builder,
0201  where - by definition - the RLogEntry has been completely built.
0202 
0203  This builder can be used through the utility preprocessor macros R__LOG_ERROR,
0204  R__LOG_WARNING etc like this:
0205 ~~~ {.cpp}
0206      R__LOG_INFO(ROOT::Experimental::HistLog()) << "all we know is " << 42;
0207      const int decreasedInfoLevel = 5;
0208      R__LOG_XDEBUG(ROOT::WebGUILog(), decreasedInfoLevel) << "nitty-gritty details";
0209 ~~~
0210  This will automatically capture the current class and function name, the file and line number.
0211  */
0212 
0213 class RLogBuilder : public std::ostringstream {
0214    /// The log entry to be built.
0215    RLogEntry fEntry;
0216 
0217 public:
0218    RLogBuilder(ELogLevel level, RLogChannel &channel) : fEntry(level, channel) {}
0219    RLogBuilder(ELogLevel level, RLogChannel &channel, const std::string &filename, int line,
0220                const std::string &funcname)
0221       : fEntry(level, channel, {filename, funcname, line})
0222    {
0223    }
0224 
0225    /// Emit the log entry through the static log manager.
0226    ~RLogBuilder()
0227    {
0228       fEntry.fMessage = str();
0229       RLogManager::Get().Emit(fEntry);
0230    }
0231 };
0232 } // namespace Detail
0233 
0234 /**
0235  Change the verbosity level (global or specific to the RLogChannel passed to the
0236  constructor) for the lifetime of this object.
0237  Example:
0238 ~~~ {.cpp}
0239  RLogScopedVerbosity debugThis(gFooLog, ELogLevel::kDebug);
0240  Foo::SomethingToDebug();
0241 ~~~
0242  */
0243 class RLogScopedVerbosity {
0244    RLogChannel *fChannel;
0245    ELogLevel fPrevLevel;
0246 
0247 public:
0248    RLogScopedVerbosity(RLogChannel &channel, ELogLevel verbosity)
0249       : fChannel(&channel), fPrevLevel(channel.SetVerbosity(verbosity))
0250    {
0251    }
0252    explicit RLogScopedVerbosity(ELogLevel verbosity) : RLogScopedVerbosity(RLogManager::Get(), verbosity) {}
0253    ~RLogScopedVerbosity() { fChannel->SetVerbosity(fPrevLevel); }
0254 };
0255 
0256 /**
0257  Object to count the number of warnings and errors emitted by a section of code,
0258  after construction of this type.
0259  */
0260 class RLogScopedDiagCount {
0261    RLogDiagCount *fCounter = nullptr;
0262    /// The number of the RLogDiagCount's emitted warnings at construction time of *this.
0263    long long fInitialWarnings = 0;
0264    /// The number of the RLogDiagCount's emitted errors at construction time.
0265    long long fInitialErrors = 0;
0266    /// The number of the RLogDiagCount's emitted fatal errors at construction time.
0267    long long fInitialFatalErrors = 0;
0268 
0269 public:
0270    /// Construct the scoped count given a counter (e.g. a channel or RLogManager).
0271    /// The counter's lifetime must exceed the lifetime of this object!
0272    explicit RLogScopedDiagCount(RLogDiagCount &cnt)
0273       : fCounter(&cnt), fInitialWarnings(cnt.GetNumWarnings()), fInitialErrors(cnt.GetNumErrors()),
0274         fInitialFatalErrors(cnt.GetNumFatalErrors())
0275    {
0276    }
0277 
0278    /// Construct the scoped count for any diagnostic, whatever its channel.
0279    RLogScopedDiagCount() : RLogScopedDiagCount(RLogManager::Get()) {}
0280 
0281    /// Get the number of warnings that the RLogDiagCount has emitted since construction of *this.
0282    long long GetAccumulatedWarnings() const { return fCounter->GetNumWarnings() - fInitialWarnings; }
0283 
0284    /// Get the number of errors that the RLogDiagCount has emitted since construction of *this.
0285    long long GetAccumulatedErrors() const { return fCounter->GetNumErrors() - fInitialErrors; }
0286 
0287    /// Get the number of errors that the RLogDiagCount has emitted since construction of *this.
0288    long long GetAccumulatedFatalErrors() const { return fCounter->GetNumFatalErrors() - fInitialFatalErrors; }
0289 
0290    /// Whether the RLogDiagCount has emitted a warnings since construction time of *this.
0291    bool HasWarningOccurred() const { return GetAccumulatedWarnings(); }
0292 
0293    /// Whether the RLogDiagCount has emitted an error (fatal or not) since construction time of *this.
0294    bool HasErrorOccurred() const { return GetAccumulatedErrors() + GetAccumulatedFatalErrors(); }
0295 
0296    /// Whether the RLogDiagCount has emitted an error or a warning since construction time of *this.
0297    bool HasErrorOrWarningOccurred() const { return HasWarningOccurred() || HasErrorOccurred(); }
0298 };
0299 
0300 namespace Internal {
0301 
0302 inline RLogChannel &GetChannelOrManager()
0303 {
0304    return RLogManager::Get();
0305 }
0306 inline RLogChannel &GetChannelOrManager(RLogChannel &channel)
0307 {
0308    return channel;
0309 }
0310 
0311 } // namespace Internal
0312 
0313 inline ELogLevel RLogChannel::GetEffectiveVerbosity(const RLogManager &mgr) const
0314 {
0315    if (fVerbosity == ELogLevel::kUnset)
0316       return mgr.GetVerbosity();
0317    return fVerbosity;
0318 }
0319 
0320 } // namespace Experimental
0321 } // namespace ROOT
0322 
0323 #if defined(_MSC_VER)
0324 #define R__LOG_PRETTY_FUNCTION __FUNCSIG__
0325 #else
0326 #define R__LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__
0327 #endif
0328 
0329 /*
0330  Some implementation details:
0331 
0332  - The conditional `RLogBuilder` use prevents stream operators from being called if
0333  verbosity is too low, i.e.:
0334  ~~~
0335  RLogScopedVerbosity silence(RLogLevel::kFatal);
0336  R__LOG_DEBUG(7) << WillNotBeCalled();
0337  ~~~
0338  - To update counts of warnings / errors / fatal errors, those RLogEntries must
0339  always be created, even if in the end their emission will be silenced. This
0340  should be fine, performance-wise, as they should not happen frequently.
0341  - Use `(condition) && RLogBuilder(...)` instead of `if (condition) RLogBuilder(...)`
0342  to prevent "ambiguous else" in invocations such as `if (something) R__LOG_DEBUG()...`.
0343  */
0344 #define R__LOG_TO_CHANNEL(SEVERITY, CHANNEL)                                                                        \
0345    ((SEVERITY < ROOT::Experimental::ELogLevel::kInfo + 0) ||                                                        \
0346      ROOT::Experimental::Internal::GetChannelOrManager(CHANNEL).GetEffectiveVerbosity(                              \
0347         ROOT::Experimental::RLogManager::Get()) >= SEVERITY) &&                                                     \
0348       ROOT::Experimental::Detail::RLogBuilder(SEVERITY, ROOT::Experimental::Internal::GetChannelOrManager(CHANNEL), \
0349                                               __FILE__, __LINE__, R__LOG_PRETTY_FUNCTION)
0350 
0351 /// \name LogMacros
0352 /// Macros to log diagnostics.
0353 /// ~~~ {.cpp}
0354 ///     R__LOG_INFO(ROOT::Experimental::HistLog()) << "all we know is " << 42;
0355 ///
0356 ///     RLogScopedVerbosity verbose(kDebug + 5);
0357 ///     const int decreasedInfoLevel = 5;
0358 ///     R__LOG_DEBUG(ROOT::WebGUILog(), decreasedInfoLevel) << "nitty-gritty details";
0359 /// ~~~
0360 ///\{
0361 #define R__LOG_FATAL(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kFatal, __VA_ARGS__)
0362 #define R__LOG_ERROR(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kError, __VA_ARGS__)
0363 #define R__LOG_WARNING(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kWarning, __VA_ARGS__)
0364 #define R__LOG_INFO(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kInfo, __VA_ARGS__)
0365 #define R__LOG_DEBUG(DEBUGLEVEL, ...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kDebug + DEBUGLEVEL, __VA_ARGS__)
0366 ///\}
0367 
0368 #endif