The Gaudi Framework  master (181af51f)
Loading...
Searching...
No Matches
MessageSvc.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
3* *
4* This software is distributed under the terms of the Apache version 2 licence, *
5* copied verbatim in the file "LICENSE". *
6* *
7* In applying this licence, CERN does not waive the privileges and immunities *
8* granted to it by virtue of its status as an Intergovernmental Organization *
9* or submit itself to any jurisdiction. *
10\***********************************************************************************/
11
12#include "MessageSvc.h"
14#include <GaudiKernel/Kernel.h>
15#include <GaudiKernel/Message.h>
17#include <GaudiKernel/System.h>
18#include <fstream>
19#include <iostream>
20#include <sstream>
21
22namespace {
23
24 // erase_if functions for containers which do NOT invalidate iterators
25 // after the erase point, eg.std::{unordered_}{,multi}map, std::{forward_,}list.
26 // To be explicit: this does NOT work with std::vector.
27
28 // TODO: replace with std::experimental::erase_if (Libraries Fundamental TS v2)
29
30 template <typename Container, typename Iterator, typename Predicate>
31 void erase_if( Container& c, Iterator first, Iterator last, Predicate pred ) {
32 while ( first != last ) {
33 if ( pred( *first ) )
34 first = c.erase( first );
35 else
36 ++first;
37 }
38 }
39
40 template <typename Container, typename Iterator, typename Predicate>
41 void erase_if( Container& c, std::pair<Iterator, Iterator> range, Predicate pred ) {
42 return erase_if( c, std::move( range.first ), std::move( range.second ), std::forward<Predicate>( pred ) );
43 }
44
45 std::string colTrans( std::string_view col, int offset ) {
46 int icol = ( col == "black" ? MSG::BLACK
47 : col == "red" ? MSG::RED
48 : col == "green" ? MSG::GREEN
49 : col == "yellow" ? MSG::YELLOW
50 : col == "blue" ? MSG::BLUE
51 : col == "purple" ? MSG::PURPLE
52 : col == "cyan" ? MSG::CYAN
53 : col == "white" ? MSG::WHITE
54 : 8 );
55 return std::to_string( icol + offset );
56 }
57} // namespace
58
60
61static const std::string levelNames[MSG::NUM_LEVELS] = { "NIL", "VERBOSE", "DEBUG", "INFO",
62 "WARNING", "ERROR", "FATAL", "ALWAYS" };
63
64MessageSvc::MessageSvc( const std::string& name, ISvcLocator* svcloc ) : base_class( name, svcloc ) {
65
66 m_outputLevel.declareUpdateHandler( [svcloc]( Gaudi::Details::PropertyBase& ) {
67 SmartIF<IAppMgrUI> app = svcloc;
68 if ( app ) app->outputLevelUpdate();
69 } );
70
71#ifndef NDEBUG
72 // initialize the MsgStream static flag.
74#endif
75
76 for ( int ic = 0; ic < MSG::NUM_LEVELS; ++ic ) {
77 m_logColors[ic].declareUpdateHandler( &MessageSvc::setupColors, this );
78 m_msgLimit[ic].declareUpdateHandler( &MessageSvc::setupLimits, this );
79 m_thresholdProp[ic].declareUpdateHandler( &MessageSvc::setupThreshold, this );
80 }
81
82 m_logColors[MSG::FATAL].set( { "blue", "red" } );
83 m_logColors[MSG::ERROR].set( { "white", "red" } );
84 m_logColors[MSG::WARNING].set( { "yellow" } );
85
86 std::fill( std::begin( m_msgCount ), std::end( m_msgCount ), 0 );
87}
88
91 if ( sc.isFailure() ) return sc;
92
93 // make sure the map of logged stream names is initialized
95
97}
98
106
108 const auto& pname = prop.name();
109 int level =
110 ( pname == "fatalColorCode" ? MSG::FATAL
111 : pname == "errorColorCode" ? MSG::ERROR
112 : pname == "warningColorCode" ? MSG::WARNING
113 : pname == "infoColorCode" ? MSG::INFO
114 : pname == "debugColorCode" ? MSG::DEBUG
115 : pname == "verboseColorCode" ? MSG::VERBOSE
116 : pname == "alwaysColorCode"
118 : ( throw GaudiException( "ERROR: Unknown message color parameter: " + pname, name(), StatusCode::FAILURE ),
119 -1 ) );
120
121 auto& code = m_logColorCodes[level];
122
123 const auto& col_desc = m_logColors[level].value();
124
125 if ( col_desc.size() == 1 ) {
126 const std::string& desc = col_desc[0];
127 if ( desc.empty() ) {
128 code = "";
129 } else if ( desc[0] == '[' ) {
130 code = "\033" + desc;
131 } else {
132 code = "\033[" + colTrans( desc, 90 ) + ";1m";
133 }
134 } else if ( col_desc.size() == 2 ) {
135 code = "\033[" + colTrans( col_desc[0], 90 ) + ";" + colTrans( col_desc[1], 100 ) + ";1m";
136 } else { // empty desc: no color
137 code = "";
138 }
139}
140
142 // Just report problems in the settings of the limits and unknown limit parameters
143 if ( prop.name() == "alwaysLimit" ) {
144 Gaudi::Property<int>* p = dynamic_cast<Gaudi::Property<int>*>( &prop );
145 if ( p && p->value() != 0 ) {
146 std::cout << "MessageSvc ERROR: cannot suppress ALWAYS messages" << std::endl;
147 p->setValue( 0 );
148 }
149 } else if ( prop.name() == "defaultLimit" ) {
150 for ( int i = MSG::VERBOSE; i < MSG::NUM_LEVELS; ++i ) {
151 if ( i != MSG::ALWAYS ) { m_msgLimit[i] = m_msgLimit[MSG::NIL].value(); }
152 }
153 } else if ( prop.name() != "fatalLimit" && prop.name() != "errorLimit" && prop.name() != "warningLimit" &&
154 prop.name() == "infoLimit" && prop.name() == "debugLimit" && prop.name() == "verboseLimit" ) {
155 std::cout << "MessageSvc ERROR: Unknown message limit parameter: " << prop.name() << std::endl;
156 return;
157 }
158}
159
161
162 static const std::array<std::pair<const char*, MSG::Level>, 7> tbl{ { { "setFatal", MSG::FATAL },
163 { "setError", MSG::ERROR },
164 { "setWarning", MSG::WARNING },
165 { "setInfo", MSG::INFO },
166 { "setDebug", MSG::DEBUG },
167 { "setVerbose", MSG::VERBOSE },
168 { "setAlways", MSG::ALWAYS } } };
169
170 auto i = std::find_if( std::begin( tbl ), std::end( tbl ),
171 [&]( const std::pair<const char*, MSG::Level>& t ) { return prop.name() == t.first; } );
172 if ( i == std::end( tbl ) ) {
173 std::cerr << "MessageSvc ERROR: Unknown message threshold parameter: " << prop.name() << std::endl;
174 return;
175 }
176 int ic = i->second;
177
179 if ( !sap ) {
180 std::cerr << "could not dcast " << prop.name()
181 << " to a Gaudi::Property<std::vector<std::string>> (which it should be!)" << std::endl;
182 } else {
183 for ( auto& i : sap->value() ) setOutputLevel( i, ic );
184 }
185}
186
187#ifdef NDEBUG
189#else
191 if ( prop.name() == "countInactive" ) {
192 Gaudi::Property<bool>* p = dynamic_cast<Gaudi::Property<bool>*>( &prop );
193 if ( p ) MsgStream::enableCountInactive( p->value() );
194 }
195}
196#endif
197
199 m_suppress = false;
200
201 {
202 std::ostringstream os;
203
204 if ( m_stats ) {
205 os << "Summarizing all message counts" << std::endl;
206 } else {
207 os << "Listing sources of suppressed message: " << std::endl;
208 }
209
210 os << "=====================================================" << std::endl;
211 os << " Message Source | Level | Count" << std::endl;
212 os << "-----------------------------+---------+-------------" << std::endl;
213
214 bool found( false );
215
216 for ( auto itr = m_sourceMap.begin(); itr != m_sourceMap.end(); ++itr ) {
217 for ( unsigned int ic = 0; ic < MSG::NUM_LEVELS; ++ic ) {
218 if ( ( itr->second.msg[ic] >= m_msgLimit[ic] && m_msgLimit[ic] != 0 ) ||
219 ( m_stats && itr->second.msg[ic] > 0 && ic >= m_statLevel.value() ) ) {
220 os << " ";
221 os.width( 28 );
222 os.setf( std::ios_base::left, std::ios_base::adjustfield );
223 os << itr->first;
224 os << "|";
225
226 os.width( 8 );
227 os.setf( std::ios_base::right, std::ios_base::adjustfield );
228 os << levelNames[ic];
229 os << " |";
230
231 os.width( 9 );
232 os << itr->second.msg[ic];
233 os << std::endl;
234
235 found = true;
236 }
237 }
238 }
239 os << "=====================================================" << std::endl;
240 if ( found || m_stats ) std::cout << os.str() << std::flush;
241 }
242
243#ifndef NDEBUG
244 if ( m_inactCount.value() ) {
245
246 std::ostringstream os;
247 os << "Listing sources of Unprotected and Unseen messages\n";
248
249 bool found( false );
250
251 unsigned int ml( 0 );
252 for ( const auto& itr : m_inactiveMap ) {
253 for ( unsigned int ic = 0; ic < MSG::NUM_LEVELS; ++ic ) {
254 if ( itr.second.msg[ic] != 0 && itr.first.length() > ml ) { ml = itr.first.length(); }
255 }
256 }
257
258 for ( unsigned int i = 0; i < ml + 25; ++i ) os << "=";
259
260 os << std::endl << " ";
261 os.width( ml + 2 );
262 os.setf( std::ios_base::left, std::ios_base::adjustfield );
263 os << "Message Source";
264 os.width( 1 );
265 os << "| Level | Count" << std::endl;
266
267 for ( unsigned int i = 0; i < ml + 3; ++i ) os << "-";
268 os << "+---------+-----------" << std::endl;
269
270 for ( auto itr = m_inactiveMap.begin(); itr != m_inactiveMap.end(); ++itr ) {
271 for ( unsigned int ic = 0; ic < MSG::NUM_LEVELS; ++ic ) {
272 if ( itr->second.msg[ic] != 0 ) {
273 os << " ";
274 os.width( ml + 2 );
275 os.setf( std::ios_base::left, std::ios_base::adjustfield );
276 os << itr->first;
277
278 os << "|";
279
280 os.width( 8 );
281 os.setf( std::ios_base::right, std::ios_base::adjustfield );
282 os << levelNames[ic];
283
284 os << " |";
285
286 os.width( 9 );
287 os << itr->second.msg[ic];
288
289 os << std::endl;
290
291 found = true;
292 }
293 }
294 }
295 for ( unsigned int i = 0; i < ml + 25; ++i ) os << "=";
296 os << std::endl;
297
298 if ( found ) std::cout << os.str() << std::flush;
299 }
300#endif
301
302 return StatusCode::SUCCESS;
303}
304
306 auto lock = std::scoped_lock{ m_reportMutex };
308}
309
311 int key = msg.getType();
312
313 ++m_msgCount[key];
314
315 const Message* cmsg = &msg;
316
317 // processing logged streams
318 if ( !m_loggedStreams.empty() ) {
319 auto iLog = m_loggedStreams.find( msg.getSource() );
320 if ( m_loggedStreams.end() != iLog ) { ( *iLog->second ) << *cmsg << std::endl; }
321 }
322
323 if ( m_suppress.value() || m_stats.value() ) {
324
325 // Increase the counter of 'key' type of messages for the source and
326 // get the new value.
327 const int nmsg = ++( m_sourceMap[msg.getSource()].msg[key] );
328
329 if ( m_suppress.value() && m_msgLimit[key] != 0 ) {
330 if ( nmsg > m_msgLimit[key] ) return;
331 if ( nmsg == m_msgLimit[key] ) {
332 std::string txt = levelNames[key] + " message limit (" + std::to_string( m_msgLimit[key].value() ) +
333 ") reached for " + msg.getSource() + ". Suppressing further output.";
334 cmsg = new Message( msg.getSource(), MSG::WARNING, std::move( txt ) );
335 cmsg->setFormat( msg.getFormat() );
336 }
337 }
338 }
339
340 auto range = m_streamMap.equal_range( key );
341 if ( range.first != m_streamMap.end() ) {
342 std::for_each( range.first, range.second,
343 [&]( StreamMap::const_reference sm ) { *sm.second.second << *cmsg << std::endl; } );
344 } else if ( key >= outputLevel ) {
345 msg.setFormat( m_defaultFormat );
346 msg.setTimeFormat( m_defaultTimeFormat );
347 if ( !m_color ) {
348 ( *m_defaultStream ) << *cmsg << std::endl << std::flush;
349 } else {
350 ( *m_defaultStream ) << m_logColorCodes[key] << *cmsg << "\033[m" << std::endl << std::flush;
351 }
352 }
353
354 if ( cmsg != &msg ) { delete cmsg; } // cppcheck-suppress autovarInvalidDeallocation; false positive
355}
356
358
359void MessageSvc::reportMessage( std::string source, int type, std::string message ) {
360 reportMessage( Message{ std::move( source ), type, std::move( message ) } );
361}
362
363void MessageSvc::reportMessage( const StatusCode& code, std::string_view source ) {
364 auto lock = std::scoped_lock{ m_messageMapMutex };
365 i_reportMessage( code, source );
366}
367
368void MessageSvc::i_reportMessage( const StatusCode& code, std::string_view source ) {
369 int level = outputLevel( source );
370 auto report = [&]( Message mesg ) {
371 mesg.setSource( source );
372 Message stat_code( std::string{ source }, mesg.getType(), "Status Code " + std::to_string( code.getCode() ) );
373 i_reportMessage( std::move( stat_code ), level );
374 i_reportMessage( std::move( mesg ), level );
375 };
376
377 auto range = m_messageMap.equal_range( code );
378 if ( range.first != m_messageMap.end() ) {
379 std::for_each( range.first, range.second, [&]( MessageMap::const_reference sm ) { report( sm.second ); } );
380 } else {
381 report( m_defaultMessage );
382 }
383}
384
385void MessageSvc::insertStream( int key, std::string name, std::ostream* stream ) {
386 m_streamMap.emplace( key, NamedStream( std::move( name ), stream ) );
387}
388
390
391void MessageSvc::eraseStream( int message_type ) { m_streamMap.erase( message_type ); }
392
393void MessageSvc::eraseStream( int key, std::ostream* stream ) {
394 if ( stream ) {
395 erase_if( m_streamMap, m_streamMap.equal_range( key ),
396 [&]( StreamMap::const_reference j ) { return j.second.second == stream; } );
397 }
398}
399
400void MessageSvc::eraseStream( std::ostream* stream ) {
401 if ( stream ) {
402 erase_if( m_streamMap, [&]( StreamMap::const_reference j ) { return j.second.second == stream; } );
403 }
404}
405
407 auto lock = std::scoped_lock{ m_messageMapMutex };
408 m_messageMap.emplace( key, std::move( msg ) );
409}
410
412 auto lock = std::scoped_lock{ m_messageMapMutex };
413 m_messageMap.clear();
414}
415
417 auto lock = std::scoped_lock{ m_messageMapMutex };
418 m_messageMap.erase( key );
419}
420
421void MessageSvc::eraseMessage( const StatusCode& key, const Message& msg ) {
422 auto lock = std::scoped_lock{ m_messageMapMutex };
423
424 erase_if( m_messageMap, m_messageMap.equal_range( key ),
425 [&]( MessageMap::const_reference j ) { return j.second == msg; } );
426}
427
429
430int MessageSvc::outputLevel( std::string_view source ) const {
431 auto lock = std::scoped_lock{ m_thresholdMapMutex };
432 auto it = m_thresholdMap.find( source );
433 return it != m_thresholdMap.end() ? it->second : m_outputLevel.value();
434}
435
436void MessageSvc::setOutputLevel( int new_level ) { m_outputLevel = new_level; }
437
438void MessageSvc::setOutputLevel( std::string_view source, int level ) {
439 auto lock = std::scoped_lock{ m_thresholdMapMutex };
440
441 // only write if we really have to...
442 auto i = m_thresholdMap.find( source );
443 if ( i == m_thresholdMap.end() ) {
444 m_thresholdMap.emplace( source, level );
445 } else if ( i->second != level ) {
446 i->second = level;
447 }
448}
449
450std::string MessageSvc::getLogColor( int logLevel ) const {
451 return ( logLevel < MSG::NUM_LEVELS ) ? m_logColorCodes[logLevel] : "";
452}
453
454int MessageSvc::messageCount( MSG::Level level ) const { return m_msgCount[level]; }
455
456void MessageSvc::incrInactiveCount( MSG::Level level, std::string_view source ) {
457 auto entry = m_inactiveMap.find( source );
458 if ( entry == m_inactiveMap.end() ) { entry = m_inactiveMap.emplace( source, MsgAry{} ).first; }
459 ++entry->second.msg[level];
460
461 if ( std::find( begin( m_tracedInactiveSources ), end( m_tracedInactiveSources ), source ) !=
462 end( m_tracedInactiveSources ) ) {
463 std::cout << "== inactive message detected from " << source << " ==\n";
464 std::string t;
465 System::backTrace( t, 25, 0 );
466 std::cout << t << std::endl;
467 }
468}
469
471 // reset state
472 m_loggedStreams.clear();
473
474 // make the unique set of output filenames
475 std::set<std::string_view> outFileNames;
476 std::transform( m_loggedStreamsName.begin(), m_loggedStreamsName.end(),
477 std::inserter( outFileNames, outFileNames.end() ),
478 []( const auto& p ) -> std::string_view { return p.second; } );
479 // map each unique filename to an ofstream
480 std::map<std::string_view, std::shared_ptr<std::ofstream>> outStreams;
481 std::transform( outFileNames.begin(), outFileNames.end(), std::inserter( outStreams, outStreams.end() ),
482 []( std::string_view fname ) {
483 return std::pair{ fname, std::make_shared<std::ofstream>(
484 std::string{ fname }, std::ios_base::out | std::ios_base::trunc ) };
485 } );
486 // associate the stream to ofstream...
487 for ( auto& iProp : m_loggedStreamsName ) {
488 auto& stream = outStreams.at( iProp.second );
489 if ( stream->good() ) m_loggedStreams.emplace( iProp.first, stream );
490 }
491}
boost::spirit::classic::position_iterator2< ForwardIterator > Iterator
Definition Iterator.h:18
#define DECLARE_COMPONENT(type)
MsgStream & msg() const
shortcut for the method msgStream(MSG::INFO)
PropertyBase base class allowing PropertyBase* collections to be "homogeneous".
const std::string name() const
property name
Implementation of property with value of concrete type.
Definition PropertyFwd.h:27
const ValueType & value() const
Definition Property.h:229
bool setValue(const ValueType &v)
Definition Property.h:231
Define general base for Gaudi exception.
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition ISvcLocator.h:42
The Message class.
Definition Message.h:25
void setFormat(std::string msg) const
Set the format string.
Definition Message.cpp:97
Gaudi::Property< bool > m_color
Definition MessageSvc.h:146
void reportMessage(const Message &message) override
Gaudi::Property< std::string > m_defaultTimeFormat
Definition MessageSvc.h:133
std::recursive_mutex m_messageMapMutex
Mutex to synchronize multiple access to m_messageMap.
Definition MessageSvc.h:211
Message m_defaultMessage
Default Message.
Definition MessageSvc.h:179
MessageMap m_messageMap
Message map.
Definition MessageSvc.h:181
StatusCode reinitialize() override
void setupLogStreams()
StreamMap m_streamMap
Stream map.
Definition MessageSvc.h:180
std::map< std::string, MsgAry, std::less<> > m_sourceMap
Definition MessageSvc.h:194
ThresholdMap m_thresholdMap
Output level threshold map.
Definition MessageSvc.h:182
Gaudi::Property< bool > m_inactCount
Definition MessageSvc.h:167
std::map< std::string, std::shared_ptr< std::ostream >, std::less<> > m_loggedStreams
Definition MessageSvc.h:198
std::array< Gaudi::Property< int >, MSG::NUM_LEVELS > m_msgLimit
Definition MessageSvc.h:157
void setupInactCount(Gaudi::Details::PropertyBase &prop)
void setupThreshold(Gaudi::Details::PropertyBase &prop)
std::map< std::string, MsgAry, std::less<> > m_inactiveMap
Definition MessageSvc.h:194
std::array< int, MSG::NUM_LEVELS > m_msgCount
Definition MessageSvc.h:196
std::array< Gaudi::Property< std::vector< std::string > >, MSG::NUM_LEVELS > m_logColors
Definition MessageSvc.h:148
int outputLevel() const override
int messageCount(MSG::Level logLevel) const override
void setOutputLevel(int new_level) override
virtual void i_reportMessage(const Message &msg, int outputLevel)
Internal implementation of reportMessage(const Message&,int) without lock.
StatusCode initialize() override
void eraseStream() override
std::array< Gaudi::Property< std::vector< std::string > >, MSG::NUM_LEVELS > m_thresholdProp
Definition MessageSvc.h:137
void eraseMessage() override
void setupLimits(Gaudi::Details::PropertyBase &prop)
StatusCode finalize() override
Gaudi::Property< std::vector< std::string > > m_tracedInactiveSources
Definition MessageSvc.h:169
std::string getLogColor(int logLevel) const override
Gaudi::Property< unsigned int > m_statLevel
Definition MessageSvc.h:135
Gaudi::Property< bool > m_suppress
Definition MessageSvc.h:166
std::recursive_mutex m_thresholdMapMutex
Mutex to synchronize multiple access to m_thresholdMap (.
Definition MessageSvc.h:215
void setupColors(Gaudi::Details::PropertyBase &prop)
Gaudi::Property< bool > m_stats
Definition MessageSvc.h:134
std::recursive_mutex m_reportMutex
Mutex to synchronize multiple threads printing.
Definition MessageSvc.h:208
Gaudi::Property< std::string > m_defaultFormat
Definition MessageSvc.h:132
Gaudi::Property< std::map< std::string, std::string, std::less<> > > m_loggedStreamsName
Definition MessageSvc.h:175
std::pair< std::string, std::ostream * > NamedStream
Definition MessageSvc.h:37
void insertMessage(const StatusCode &code, Message message) override
void insertStream(int message_type, std::string name, std::ostream *stream) override
MessageSvc(const std::string &name, ISvcLocator *svcloc)
void incrInactiveCount(MSG::Level level, std::string_view src) override
std::string m_logColorCodes[MSG::NUM_LEVELS]
Definition MessageSvc.h:184
static GAUDI_API bool enableCountInactive(bool value=true)
Enable/disable the count of inactive messages.
Definition MsgStream.cpp:21
Gaudi::Property< int > m_outputLevel
flag indicating whether ToolHandle tools have been added to m_tools
Definition Service.h:184
const std::string & name() const override
Retrieve name of the service.
Definition Service.cpp:333
Gaudi::StateMachine::State m_state
Service state.
Definition Service.h:155
StatusCode initialize() override
Definition Service.cpp:118
Small smart pointer class with automatic reference counting for IInterface.
Definition SmartIF.h:28
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
bool isFailure() const
Definition StatusCode.h:129
bool isSuccess() const
Definition StatusCode.h:314
constexpr static const auto SUCCESS
Definition StatusCode.h:99
constexpr static const auto FAILURE
Definition StatusCode.h:100
code_t getCode() const
Retrieve value.
Definition StatusCode.h:136
decltype(auto) range(Args &&... args)
Zips multiple containers together to form a single range.
Definition details.h:81
@ GREEN
Definition IMessageSvc.h:23
@ WHITE
Definition IMessageSvc.h:23
@ CYAN
Definition IMessageSvc.h:23
@ YELLOW
Definition IMessageSvc.h:23
@ PURPLE
Definition IMessageSvc.h:23
@ BLACK
Definition IMessageSvc.h:23
@ BLUE
Definition IMessageSvc.h:23
@ RED
Definition IMessageSvc.h:23
@ NIL
Definition IMessageSvc.h:22
@ NUM_LEVELS
Definition IMessageSvc.h:22
@ WARNING
Definition IMessageSvc.h:22
@ FATAL
Definition IMessageSvc.h:22
@ DEBUG
Definition IMessageSvc.h:22
@ ERROR
Definition IMessageSvc.h:22
@ ALWAYS
Definition IMessageSvc.h:22
@ INFO
Definition IMessageSvc.h:22
@ VERBOSE
Definition IMessageSvc.h:22
GAUDI_API int backTrace(void **addresses, const int depth)
Definition System.cpp:372
Private helper class to keep the count of messages of a type (MSG::LEVEL).
Definition MessageSvc.h:187