Logo ROOT  
Reference Guide
RLogger.hxx
Go to the documentation of this file.
1 /// \file ROOT/RLogger.hxx
2 /// \ingroup Base ROOT7
3 /// \author Axel Naumann <axel@cern.ch>
4 /// \date 2015-03-29
5 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6 /// is welcome!
7 
8 /*************************************************************************
9  * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
10  * All rights reserved. *
11  * *
12  * For the licensing terms see $ROOTSYS/LICENSE. *
13  * For the list of contributors see $ROOTSYS/README/CREDITS. *
14  *************************************************************************/
15 
16 #ifndef ROOT7_RLogger
17 #define ROOT7_RLogger
18 
19 #include <atomic>
20 #include <list>
21 #include <memory>
22 #include <mutex>
23 #include <sstream>
24 #include <string>
25 #include <utility>
26 
27 namespace ROOT {
28 namespace Experimental {
29 
30 class RLogEntry;
31 class RLogManager;
32 
33 /**
34  Kinds of diagnostics.
35  */
36 enum class ELogLevel : unsigned char {
37  kUnset,
38  kFatal, ///< An error which causes further processing to be unreliable
39  kError, ///< An error
40  kWarning, ///< Warnings about likely unexpected behavior
41  kInfo, ///< Informational messages; used for instance for tracing
42  kDebug ///< Debug information; only useful for developers; can have added verbosity up to 255-kDebug.
43 };
44 
45 inline ELogLevel operator+(ELogLevel severity, int offset)
46 {
47  return static_cast<ELogLevel>(static_cast<int>(severity) + offset);
48 }
49 
50 /**
51  Keep track of emitted errors and warnings.
52  */
54 protected:
55  std::atomic<long long> fNumWarnings{0ll}; /// Number of warnings.
56  std::atomic<long long> fNumErrors{0ll}; /// Number of errors.
57  std::atomic<long long> fNumFatalErrors{0ll}; /// Number of fatal errors.
58 
59 public:
60  /// Returns the current number of warnings.
61  long long GetNumWarnings() const { return fNumWarnings; }
62 
63  /// Returns the current number of errors.
64  long long GetNumErrors() const { return fNumErrors; }
65 
66  /// Returns the current number of fatal errors.
67  long long GetNumFatalErrors() const { return fNumFatalErrors; }
68 
69  /// Increase warning or error count.
70  void Increment(ELogLevel severity)
71  {
72  switch (severity) {
73  case ELogLevel::kFatal: ++fNumFatalErrors; break;
74  case ELogLevel::kError: ++fNumErrors; break;
75  case ELogLevel::kWarning: ++fNumWarnings; break;
76  default:;
77  }
78  }
79 };
80 
81 /**
82  Abstract RLogHandler base class. ROOT logs everything from info to error
83  to entities of this class.
84  */
85 class RLogHandler {
86 public:
87  virtual ~RLogHandler();
88  /// Emit a log entry.
89  /// \param entry - the RLogEntry to be emitted.
90  /// \returns false if further emission of this Log should be suppressed.
91  ///
92  /// \note This function is called concurrently; log emission must be locked
93  /// if needed. (The default log handler using ROOT's DefaultErrorHandler is locked.)
94  virtual bool Emit(const RLogEntry &entry) = 0;
95 };
96 
97 /**
98  A log configuration for a channel, e.g. "RHist".
99  Each ROOT module has its own log, with potentially distinct verbosity.
100  */
101 class RLogChannel : public RLogDiagCount {
102  /// Name as shown in diagnostics
103  std::string fName;
104 
105  /// Verbosity of this channel. By default, use the global verbosity.
107 
108 public:
109  /// Construct an anonymous channel.
110  RLogChannel() = default;
111 
112  /// Construct an anonymous channel with a default vebosity.
113  explicit RLogChannel(ELogLevel verbosity) : fVerbosity(verbosity) {}
114 
115  /// Construct a log channel given its name, which is part of the diagnostics.
116  RLogChannel(const std::string &name) : fName(name) {}
117 
119  {
120  std::swap(fVerbosity, verbosity);
121  return verbosity;
122  }
123  ELogLevel GetVerbosity() const { return fVerbosity; }
125 
126  const std::string &GetName() const { return fName; }
127 };
128 
129 /**
130  A RLogHandler that multiplexes diagnostics to different client `RLogHandler`s
131  and keeps track of the sum of `RLogDiagCount`s for all channels.
132 
133  `RLogHandler::Get()` returns the process's (static) log manager.
134  */
135 
136 class RLogManager : public RLogChannel, public RLogHandler {
137  std::mutex fMutex;
138  std::list<std::unique_ptr<RLogHandler>> fHandlers;
139 
140 public:
141  /// Initialize taking a RLogHandler.
142  RLogManager(std::unique_ptr<RLogHandler> lh) : RLogChannel(ELogLevel::kWarning)
143  {
144  fHandlers.emplace_back(std::move(lh));
145  }
146 
147  static RLogManager &Get();
148 
149  /// Add a RLogHandler in the front - to be called before all others.
150  void PushFront(std::unique_ptr<RLogHandler> handler) { fHandlers.emplace_front(std::move(handler)); }
151 
152  /// Add a RLogHandler in the back - to be called after all others.
153  void PushBack(std::unique_ptr<RLogHandler> handler) { fHandlers.emplace_back(std::move(handler)); }
154 
155  /// Remove and return the given log handler. Returns `nullptr` if not found.
156  std::unique_ptr<RLogHandler> Remove(RLogHandler *handler);
157 
158  // Emit a `RLogEntry` to the RLogHandlers.
159  // Returns false if further emission of this Log should be suppressed.
160  bool Emit(const RLogEntry &entry) override;
161 };
162 
163 /**
164  A diagnostic location, part of an RLogEntry.
165  */
166 struct RLogLocation {
167  std::string fFile;
168  std::string fFuncName;
169  int fLine; // C++11 forbids "= 0" for braced-init-list initialization.
170 };
171 
172 /**
173  A diagnostic that can be emitted by the RLogManager.
174  One can construct a RLogEntry through RLogBuilder, including streaming into
175  the diagnostic message and automatic emission.
176  */
177 
178 class RLogEntry {
179 public:
181  std::string fMessage;
182  RLogChannel *fChannel = nullptr;
184 
185  RLogEntry(ELogLevel level, RLogChannel &channel) : fChannel(&channel), fLevel(level) {}
186  RLogEntry(ELogLevel level, RLogChannel &channel, const RLogLocation &loc)
187  : fLocation(loc), fChannel(&channel), fLevel(level)
188  {
189  }
190 
191  bool IsDebug() const { return fLevel >= ELogLevel::kDebug; }
192  bool IsInfo() const { return fLevel == ELogLevel::kInfo; }
193  bool IsWarning() const { return fLevel == ELogLevel::kWarning; }
194  bool IsError() const { return fLevel == ELogLevel::kError; }
195  bool IsFatal() const { return fLevel == ELogLevel::kFatal; }
196 };
197 
198 namespace Detail {
199 /**
200  Builds a diagnostic entry, emitted by the static RLogManager upon destruction of this builder,
201  where - by definition - the RLogEntry has been completely built.
202 
203  This builder can be used through the utility preprocessor macros R__LOG_ERROR,
204  R__LOG_WARNING etc like this:
205  ```c++
206  R__LOG_INFO(ROOT::Experimental::HistLog()) << "all we know is " << 42;
207  const int decreasedInfoLevel = 5;
208  R__LOG_XDEBUG(ROOT::Experimental::WebGUILog(), decreasedInfoLevel) << "nitty-gritty details";
209  ```
210  This will automatically capture the current class and function name, the file and line number.
211  */
212 
213 class RLogBuilder : public std::ostringstream {
214  /// The log entry to be built.
215  RLogEntry fEntry;
216 
217 public:
218  RLogBuilder(ELogLevel level, RLogChannel &channel) : fEntry(level, channel) {}
219  RLogBuilder(ELogLevel level, RLogChannel &channel, const std::string &filename, int line,
220  const std::string &funcname)
221  : fEntry(level, channel, {filename, funcname, line})
222  {
223  }
224 
225  /// Emit the log entry through the static log manager.
226  ~RLogBuilder()
227  {
228  fEntry.fMessage = str();
229  RLogManager::Get().Emit(fEntry);
230  }
231 };
232 } // namespace Detail
233 
234 /**
235  Change the verbosity level (global or specific to the RLogChannel passed to the
236  constructor) for the lifetime of this object.
237  Example:
238  ```c++
239  RLogScopedVerbosity debugThis(gFooLog, ELogLevel::kDebug);
240  Foo::SomethingToDebug();
241  ```
242  */
243 class RLogScopedVerbosity {
244  RLogChannel *fChannel;
245  ELogLevel fPrevLevel;
246 
247 public:
248  RLogScopedVerbosity(RLogChannel &channel, ELogLevel verbosity)
249  : fChannel(&channel), fPrevLevel(channel.SetVerbosity(verbosity))
250  {
251  }
252  explicit RLogScopedVerbosity(ELogLevel verbosity) : RLogScopedVerbosity(RLogManager::Get(), verbosity) {}
253  ~RLogScopedVerbosity() { fChannel->SetVerbosity(fPrevLevel); }
254 };
255 
256 /**
257  Object to count the number of warnings and errors emitted by a section of code,
258  after construction of this type.
259  */
260 class RLogScopedDiagCount {
261  RLogDiagCount *fCounter = nullptr;
262  /// The number of the RLogDiagCount's emitted warnings at construction time of *this.
263  long long fInitialWarnings = 0;
264  /// The number of the RLogDiagCount's emitted errors at construction time.
265  long long fInitialErrors = 0;
266  /// The number of the RLogDiagCount's emitted fatal errors at construction time.
267  long long fInitialFatalErrors = 0;
268 
269 public:
270  /// Construct the scoped count given a counter (e.g. a channel or RLogManager).
271  /// The counter's lifetime must exceed the lifetime of this object!
272  explicit RLogScopedDiagCount(RLogDiagCount &cnt)
273  : fCounter(&cnt), fInitialWarnings(cnt.GetNumWarnings()), fInitialErrors(cnt.GetNumErrors()),
274  fInitialFatalErrors(cnt.GetNumFatalErrors())
275  {
276  }
277 
278  /// Construct the scoped count for any diagnostic, whatever its channel.
279  RLogScopedDiagCount() : RLogScopedDiagCount(RLogManager::Get()) {}
280 
281  /// Get the number of warnings that the RLogDiagCount has emitted since construction of *this.
282  long long GetAccumulatedWarnings() const { return fCounter->GetNumWarnings() - fInitialWarnings; }
283 
284  /// Get the number of errors that the RLogDiagCount has emitted since construction of *this.
285  long long GetAccumulatedErrors() const { return fCounter->GetNumErrors() - fInitialErrors; }
286 
287  /// Get the number of errors that the RLogDiagCount has emitted since construction of *this.
288  long long GetAccumulatedFatalErrors() const { return fCounter->GetNumFatalErrors() - fInitialFatalErrors; }
289 
290  /// Whether the RLogDiagCount has emitted a warnings since construction time of *this.
291  bool HasWarningOccurred() const { return GetAccumulatedWarnings(); }
292 
293  /// Whether the RLogDiagCount has emitted an error (fatal or not) since construction time of *this.
294  bool HasErrorOccurred() const { return GetAccumulatedErrors() + GetAccumulatedFatalErrors(); }
295 
296  /// Whether the RLogDiagCount has emitted an error or a warning since construction time of *this.
297  bool HasErrorOrWarningOccurred() const { return HasWarningOccurred() || HasErrorOccurred(); }
298 };
299 
300 namespace Internal {
301 
302 inline RLogChannel &GetChannelOrManager()
303 {
304  return RLogManager::Get();
305 }
306 inline RLogChannel &GetChannelOrManager(RLogChannel &channel)
307 {
308  return channel;
309 }
310 
311 } // namespace Internal
312 
313 inline ELogLevel RLogChannel::GetEffectiveVerbosity(const RLogManager &mgr) const
314 {
316  return mgr.GetVerbosity();
317  return fVerbosity;
318 }
319 
320 } // namespace Experimental
321 } // namespace ROOT
322 
323 #if defined(_MSC_VER)
324 #define R__LOG_PRETTY_FUNCTION __FUNCSIG__
325 #else
326 #define R__LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__
327 #endif
328 
329 /*
330  Some implementation details:
331 
332  - The conditional `RLogBuilder` use prevents stream operators from being called if
333  verbosity is too low, i.e.:
334  ````
335  RLogScopedVerbosity silence(RLogLevel::kFatal);
336  R__LOG_DEBUG(7) << WillNotBeCalled();
337  ```
338  - To update counts of warnings / errors / fatal errors, those RLogEntries must
339  always be created, even if in the end their emission will be silenced. This
340  should be fine, performance-wise, as they should not happen frequently.
341  - Use `(condition) && RLogBuilder(...)` instead of `if (condition) RLogBuilder(...)`
342  to prevent "ambiguous else" in invocations such as `if (something) R__LOG_DEBUG()...`.
343  */
344 #define R__LOG_TO_CHANNEL(SEVERITY, CHANNEL) \
345  ((SEVERITY < ROOT::Experimental::ELogLevel::kInfo + 0) || \
346  ROOT::Experimental::Internal::GetChannelOrManager(CHANNEL).GetEffectiveVerbosity( \
347  ROOT::Experimental::RLogManager::Get()) >= SEVERITY) && \
348  ROOT::Experimental::Detail::RLogBuilder(SEVERITY, ROOT::Experimental::Internal::GetChannelOrManager(CHANNEL), \
349  __FILE__, __LINE__, R__LOG_PRETTY_FUNCTION)
350 
351 /// \name LogMacros
352 /// Macros to log diagnostics.
353 /// ```c++
354 /// R__LOG_INFO(ROOT::Experimental::HistLog()) << "all we know is " << 42;
355 ///
356 /// RLogScopedVerbosity verbose(kDebug + 5);
357 /// const int decreasedInfoLevel = 5;
358 /// R__LOG_DEBUG(decreasedInfoLevel, ROOT::Experimental::WebGUILog()) << "nitty-gritty details";
359 /// ```
360 ///\{
361 #define R__LOG_FATAL(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kFatal, __VA_ARGS__)
362 #define R__LOG_ERROR(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kError, __VA_ARGS__)
363 #define R__LOG_WARNING(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kWarning, __VA_ARGS__)
364 #define R__LOG_INFO(...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kInfo, __VA_ARGS__)
365 #define R__LOG_DEBUG(DEBUGLEVEL, ...) R__LOG_TO_CHANNEL(ROOT::Experimental::ELogLevel::kDebug + DEBUGLEVEL, __VA_ARGS__)
366 ///\}
367 
368 #endif
ROOT::Experimental::RLogEntry::fChannel
RLogChannel * fChannel
Definition: RLogger.hxx:182
ROOT::Experimental::RLogLocation::fFuncName
std::string fFuncName
Definition: RLogger.hxx:168
ROOT::Experimental::RLogDiagCount::fNumFatalErrors
std::atomic< long long > fNumFatalErrors
Number of errors.
Definition: RLogger.hxx:57
ROOT::Experimental::RLogEntry::fLevel
ELogLevel fLevel
Definition: RLogger.hxx:183
ROOT::Experimental::operator+
ELogLevel operator+(ELogLevel severity, int offset)
Definition: RLogger.hxx:45
ROOT::Experimental::RLogHandler::~RLogHandler
virtual ~RLogHandler()
Definition: RLogger.cxx:28
ROOT::Experimental::RLogEntry::IsWarning
bool IsWarning() const
Definition: RLogger.hxx:193
ROOT::Experimental::ELogLevel::kWarning
@ kWarning
Warnings about likely unexpected behavior.
kWarning
const Int_t kWarning
Definition: TError.h:45
ROOT::Experimental::RLogManager::Remove
std::unique_ptr< RLogHandler > Remove(RLogHandler *handler)
Remove and return the given log handler. Returns nullptr if not found.
Definition: RLogger.cxx:68
ROOT::Experimental::ELogLevel::kFatal
@ kFatal
An error which causes further processing to be unreliable.
ROOT::Experimental::RLogManager::RLogManager
RLogManager(std::unique_ptr< RLogHandler > lh)
Initialize taking a RLogHandler.
Definition: RLogger.hxx:142
ROOT::Experimental::RLogDiagCount::fNumErrors
std::atomic< long long > fNumErrors
Number of warnings.
Definition: RLogger.hxx:56
ROOT::Experimental::RLogManager::Emit
bool Emit(const RLogEntry &entry) override
Emit a log entry.
Definition: RLogger.cxx:82
ROOT::Experimental::RLogChannel::GetVerbosity
ELogLevel GetVerbosity() const
Definition: RLogger.hxx:123
ROOT::Experimental::ELogLevel::kDebug
@ kDebug
Debug information; only useful for developers; can have added verbosity up to 255-kDebug.
ELogLevel
ELogLevel
Definition: TSystem.h:55
ROOT::Experimental::RLogChannel
A log configuration for a channel, e.g.
Definition: RLogger.hxx:101
ROOT::Experimental::RLogChannel::RLogChannel
RLogChannel(ELogLevel verbosity)
Construct an anonymous channel with a default vebosity.
Definition: RLogger.hxx:113
ROOT::Experimental::RLogChannel::RLogChannel
RLogChannel()=default
Construct an anonymous channel.
ROOT::Experimental::RLogManager::PushFront
void PushFront(std::unique_ptr< RLogHandler > handler)
Add a RLogHandler in the front - to be called before all others.
Definition: RLogger.hxx:150
ROOT::Experimental::RLogEntry
A diagnostic that can be emitted by the RLogManager.
Definition: RLogger.hxx:178
ROOT::Experimental::RLogChannel::fName
std::string fName
Name as shown in diagnostics.
Definition: RLogger.hxx:103
ROOT::Experimental::RLogChannel::GetEffectiveVerbosity
ELogLevel GetEffectiveVerbosity(const RLogManager &mgr) const
ROOT::Experimental::RLogLocation
A diagnostic location, part of an RLogEntry.
Definition: RLogger.hxx:166
ROOT::Experimental::RLogDiagCount::fNumWarnings
std::atomic< long long > fNumWarnings
Definition: RLogger.hxx:55
ROOT::Experimental::RLogLocation::fFile
std::string fFile
Definition: RLogger.hxx:167
ROOT::Experimental::RLogEntry::RLogEntry
RLogEntry(ELogLevel level, RLogChannel &channel, const RLogLocation &loc)
Definition: RLogger.hxx:186
ROOT::Experimental::RLogChannel::fVerbosity
ELogLevel fVerbosity
Verbosity of this channel. By default, use the global verbosity.
Definition: RLogger.hxx:106
line
TLine * line
Definition: entrylistblock_figure1.C:235
ROOT::Experimental::ELogLevel::kError
@ kError
An error.
ROOT::Experimental::RLogDiagCount
Keep track of emitted errors and warnings.
Definition: RLogger.hxx:53
ROOT::Experimental::RLogManager::PushBack
void PushBack(std::unique_ptr< RLogHandler > handler)
Add a RLogHandler in the back - to be called after all others.
Definition: RLogger.hxx:153
ROOT::Experimental::ELogLevel
ELogLevel
Kinds of diagnostics.
Definition: RLogger.hxx:36
ROOT::Experimental::RLogManager::fMutex
std::mutex fMutex
Definition: RLogger.hxx:137
ROOT::Experimental::RLogDiagCount::GetNumWarnings
long long GetNumWarnings() const
Number of fatal errors.
Definition: RLogger.hxx:61
ROOT::Experimental::ELogLevel::kUnset
@ kUnset
ROOT::Experimental::RLogEntry::fLocation
RLogLocation fLocation
Definition: RLogger.hxx:180
ROOT::Experimental::RLogHandler::Emit
virtual bool Emit(const RLogEntry &entry)=0
Emit a log entry.
name
char name[80]
Definition: TGX11.cxx:110
ROOT::Experimental::Internal::swap
void swap(RDirectoryEntry &e1, RDirectoryEntry &e2) noexcept
Definition: RDirectoryEntry.hxx:94
ROOT::Experimental::ELogLevel::kInfo
@ kInfo
Informational messages; used for instance for tracing.
ROOT::Experimental::RLogEntry::RLogEntry
RLogEntry(ELogLevel level, RLogChannel &channel)
Definition: RLogger.hxx:185
ROOT::Experimental::RLogEntry::IsInfo
bool IsInfo() const
Definition: RLogger.hxx:192
ROOT::Experimental::RLogEntry::IsDebug
bool IsDebug() const
Definition: RLogger.hxx:191
ROOT::Experimental::RLogChannel::GetName
const std::string & GetName() const
Definition: RLogger.hxx:126
ROOT::Experimental::RLogLocation::fLine
int fLine
Definition: RLogger.hxx:169
xmlio::cnt
const char * cnt
Definition: TXMLSetup.cxx:75
ROOT::Experimental::RLogManager::Get
static RLogManager & Get()
Definition: RLogger.cxx:62
ROOT::Experimental::RLogDiagCount::GetNumFatalErrors
long long GetNumFatalErrors() const
Returns the current number of fatal errors.
Definition: RLogger.hxx:67
ROOT::Experimental::RLogManager::fHandlers
std::list< std::unique_ptr< RLogHandler > > fHandlers
Definition: RLogger.hxx:138
ROOT::Experimental::RLogManager
A RLogHandler that multiplexes diagnostics to different client RLogHandlers and keeps track of the su...
Definition: RLogger.hxx:136
ROOT::Experimental::RLogEntry::fMessage
std::string fMessage
Definition: RLogger.hxx:181
ROOT
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Definition: EExecutionPolicy.hxx:4
ROOT::Experimental::RLogHandler
Abstract RLogHandler base class.
Definition: RLogger.hxx:85
ROOT::Experimental::RLogChannel::RLogChannel
RLogChannel(const std::string &name)
Construct a log channel given its name, which is part of the diagnostics.
Definition: RLogger.hxx:116
ROOT::Experimental::RLogDiagCount::GetNumErrors
long long GetNumErrors() const
Returns the current number of errors.
Definition: RLogger.hxx:64
ROOT::Experimental::RLogEntry::IsFatal
bool IsFatal() const
Definition: RLogger.hxx:195
ROOT::Experimental::RLogEntry::IsError
bool IsError() const
Definition: RLogger.hxx:194
ROOT::Experimental::RLogChannel::SetVerbosity
ELogLevel SetVerbosity(ELogLevel verbosity)
Definition: RLogger.hxx:118
ROOT::Experimental::RLogDiagCount::Increment
void Increment(ELogLevel severity)
Increase warning or error count.
Definition: RLogger.hxx:70