Warning, /FWCore/MessageService/doc/suppression.txt is written in an unsupported language. File is not indexed.
0001 Suppression of Messages in MessageLogger
0002 ----------------------------------------
0003
0004 Basics of message suppression
0005 -----------------------------
0006
0007 By definition, suppression of messages means that some subset of
0008 messages issued by <LogWhatever> will never (in the case of multithread
0009 operation) even be put on the queue for the MessageLoggerScribe thread
0010 to look at. (In single-thread mode, no calls to any messafge service logging
0011 functions are made.)
0012
0013 To suppress a message, the software must know, "at the top", that no message
0014 of this ilk can possibly be reacted to.
0015
0016 For practical purposes, this has several consequences:
0017
0018 1. Savings of activity:
0019 a. Overhead of adding to the queue, waking the scribe thread, consuming
0020 the message is eliminated.
0021 b. Preparation of the header and the extended message id never happens.
0022 c. Message is not "shopped" to the various destinations, saving
0023 various map lookups. This includes the statistics destinations.
0024 d. Of course no actual I/O or is done.
0025
0026 2. Granularity of choice:
0027 a. Suppression cannot distinguish between one destination and another.
0028 b. Suppression knows nothing about the category of the message.
0029 c. Suppression does know about the severity level of the message.
0030 c. Suppression CAN depend on which module is running.
0031
0032 3. Effect:
0033 a. A suppressed message cannot contribute to any statistics destination.
0034 b. There cannot be a way to over-ride suppression at the destination level.
0035
0036 Other configuration-specified preferences can cause various messages to be
0037 ignored by various destinations, but that is moot to the issue of suppresssion
0038 because to decide to ignore a message, the destination has to see the message,
0039 thus the message must have made it to the MessageLoggerScribe.
0040
0041 Suppression can be controlled by compile-time defines, by thresholds and
0042 module specifications set up in the configuration file, or by a combination
0043 of both.
0044
0045 Although by definition the activities above are elided, there is potential for
0046 avoiding (or incurring) various other burdens. In particular, the message
0047 content itself might take non-trivial time to build up. It is desirable that
0048 in the case of a suppressed message, this work be avoided:
0049
0050 4. Further savings of activity:
0051 a. The various operator<<'s appearing in the message-issuing statement
0052 are not executed.
0053 b. Any functions called to prepare for those operator<<'s are not called.
0054
0055 If suppression is dictated by a compile-time define, then a fifth savings can
0056 be imagined:
0057
0058 5. Savings of space:
0059 a. The code for the various operator<<'s and or functions appearing in the
0060 statement issuing the message does not even apear in the executable.
0061
0062 The non-execution of functions appearing in a statement involving a
0063 suppressed LogDebug, while quite desirable to our community, violates C++
0064 semantics about expression evaluation. The user "has a right to expect"
0065 that in A(b) << foo(), the function foo() is called and any side effects
0066 will happen. Creating a situation in which these semantics are violated
0067 can be accomplished via a pre-preocessor macro or define (and I suspect
0068 ONLY that way). The reason we are somewhat comfortable with this violation
0069 of C++ expression evaluation semantics is that this is the identical situation
0070 occuring in the context of the assert macro. Users are familiar with the fact
0071 that using a function with side effects inside an assert is poison, and
0072 nobody takes arms against the writer of assert on that issue.
0073
0074 Enabling LogDebug by compile-time define
0075 ----------------------------------------
0076
0077 In the new code, LogDebug and LogTrace are by default always suppressed.
0078 To enable them, one must define EDM_ML_DEBUG.
0079
0080 To do this on the build command, one does
0081
0082 scram b clean; scram b USER_CXXFLAGS="-DEDM_ML_DEBUG"
0083
0084 There is a bit of a subtlety. Scram does check for changes in these user flags
0085 when deciding what does and does not require recompilation. But this implies
0086 that everything will be recompiled. In principle you would rather recompile
0087 only files that included MessageLogger.h (however, we notice that is **is**
0088 pretty much everything). It would be even better to recompile only files that
0089 use LogDebug or LogTrace. This we cannot force the build system to do. Thus
0090 we either need to do scram v clean first (as in the above mantra) or touch
0091 MessageLogger.h.
0092
0093 A consequence is that if some irrelevant file has advanced to a new version,
0094 then without using the versioning capabilities of cvs, there is no way USING
0095 THE SCRAM COMMAND LINE ALONE to just enable LogDebug for your files without
0096 moving to the new version of the irrelevant files.
0097
0098 Of course, one can place a
0099 #define EDM_ML_DEBUG
0100 into one's own code, and that will enable LogDebug for just that file.
0101
0102 Status before Sep 2010
0103 ----------------------
0104
0105 In the original idea for LogDebug, a compile-time define would cause LogDebug
0106 to become a complete no-op, resulting in code that was identical to the code
0107 with the LogDebug statement commented out. This turned out not to be as easy
0108 as originally anticipated, and indeed the original code missed the mark in
0109 (at least) two ways:
0110
0111 1. When the suppression was specified at compile time, the code for
0112 functions needed to prepare the data composing the message was still
0113 being generated, and the code implementing the functions to prepare
0114 the message was still being executed, because the Suppress_LogDebug_ class
0115 was still doing operator<<, treated in this way:
0116
0117 Suppress_LogDebug_ &operator<< (T const & t) { return *this; }
0118
0119 That implies that if the rhs of the operator<< is foo(), then foo() must
0120 be called since from the compiler's perspective foo() might have side
0121 effects.
0122
0123 2. When the suppression was specified dynamically, the compiler inherently
0124 could not avoid generating the code for foo(), but it also would be
0125 executing foo(), and that (wee shall see) could have been avoided.
0126
0127 Although some care was taken to expose, by inlining, as much of the mechanism
0128 as possible so that a good compiler could know to throw everything away, the
0129 compiler does not know (and cannot be told) that the individual streaming
0130 operators and/or the functions called to compute the data being streamed are
0131 "pure." Empirically, therefore, LogDebug occupied non-negligible time (in
0132 some cases) even when suppressed.
0133
0134 The solution to this dilemma, at least for the compile-time case, lies in
0135 a lexical technique suggested by C. Jones. This forces the pre-preocessor to
0136 generate a statement such that the possibly-expensive function and operator<<
0137 calls are REQUIRED (by the C++ standard) to be elided by the compiler.
0138
0139 In the dynamic case, the solution is to not to leave the realm of the
0140 macros until after the decision-point about suppression. This complicates
0141 matters due to the two-levels of macro, but is not conceptually any different.
0142
0143 Given that LogDebug() behaves logically like a ctor, it is less than
0144 trivial to implement these techniques; more about that below in the Coding
0145 Details section.
0146
0147
0148 Details of how suppression booleans are maintained
0149 --------------------------------------------------
0150
0151 I will use edm::LogInfo as an example; the WARNING level loggers behave
0152 similarly, ERROR level messages cannot be suppressed, and LogDebug has a
0153 special macro structure discussed below for placing the line number into
0154 the message.
0155
0156 Let's start from the business end (how the suppression boolean is used)
0157 and trace back to see how it is modified and finally how it is set up
0158 and how modification policy is established.
0159
0160 The variable obtained by edm::MessageDrop::instance()->infoEnabled
0161 is the relevant suppression boolean. It is used in the ctor of LogInfo
0162 to decide whether to initialize a unique_ptr to a MessageSender with
0163 an actual MessageSender, or with 0.
0164
0165 In MessageLogger/interface/MessageLogger.h:
0166 class LogInfo
0167 {
0168 public:
0169 explicit LogInfo( std::string const & id )
0170 : ap ( edm::MessageDrop::instance()->infoEnabled ?
0171 new MessageSender(ELinfo,id) : 0 )
0172 { }
0173 // ...
0174 }
0175
0176 The act of destroying the MessageSender, which occurs when in the dtor of
0177 LogInfo this unique_ptr is destructed, is what triggers preparation of the
0178 ErrorObject and placing that ErrorObject onto the MessageLoggerQueue.
0179 That is, if the MessageSender is constructed, then INEVITABLY the message
0180 will appear on the queue, and such a message by definition was not suppressed.
0181
0182 How does edm::MessageDrop::instance()->infoEnabled evolve?
0183
0184 If we look in MessageService/src/MessageLogger.cc (note that the other header
0185 we looked at was in MessageLogger, not MessageService), we see that various
0186 calls to the following helper functions are set up as callbacks:
0187
0188 establishModuleCtor is called by
0189 preModuleConstruction
0190 preSourceConstruction
0191
0192 unEstablishModule is called by
0193 preModuleConstruction
0194 preSourceConstruction
0195
0196 establishModule is called by
0197 preModuleEndJob
0198 preModuleEndRun
0199 preModuleEndLumi
0200 preModule
0201 preModuleEndLumi
0202 preModuleEndRun
0203 preModuleEndJob
0204
0205 unEstablishModule is called by
0206 preModuleEndJob
0207 preModuleEndRun
0208 preModuleEndLumi
0209 preModule
0210 preModuleEndLumi
0211 preModuleEndRun
0212 preModuleEndJob
0213
0214 So we see that whenever code for some module is running, establishModule
0215 with that module descriptor will have been called.
0216
0217 In establishModuleCtor and establishModule, values are set up for
0218 messageDrop->debugEnabled, messageDrop->infoEnabled, and warningEnabled.
0219
0220 The code first searches for this moduleLable() (label, not name) in the map
0221 suppression_levels. If it fines a suppression level for this module,
0222 then it sets each of enabling variables (for example infoEnabled) true if
0223 the indicated supression level is less than the corresponding severity level.
0224 For example, if suppression_levels["thisModule"] = ELsev_info (or higher),
0225 then infoEnabled is set false; if it is ELsev_debug then infoEnabled is
0226 set true. If the module label is not found in the list, then infoEnabled
0227 and warningEnabled are true.
0228
0229 Current code (as of 9/17/10) treats debugEnabled differently: First,
0230 it sets up debugEnabled according to two booleans anyDebugEnabledand
0231 everyDebugEnabled (the usage is the obvious) and the map
0232 debugEnabledModules_ (if the bools don't settle the issue and the map
0233 contains the module lable, then debugEnabled is set true). Then if
0234 the suppression_levels map dose not contain this module label, that
0235 value of debugEnabled is left holding.
0236
0237 When the module is unEstablished, the value goes back to the original
0238 infoEnabled, as stored in nonModule_infoEnabled (similarly for the others.
0239 nonModule_debugEnabled, nonModule_infoEnabled, and nonModule_warningEnabled
0240 are given initial values (false, true, true) as a matter of good C++ practice,
0241 but these initial values are not used. On the other hand, initial values of
0242 messageDrop->debugEnabled, infoEnabled, and warningEnabled (which are relevant
0243 for messages issued outside of all modules) are set true in MessageDrop.cc.
0244
0245 So far, we see that infoEnabled is used to decide whether to create a
0246 MessageSender, and that infoEnabled is established each time a module is
0247 entered, based on suppression_levels. All that remains is to see how
0248 suppression_levels (and, for logDebug, anyDebugEnabled, everyDebugEnabled,
0249 and debugEnabledModules_) get set up.
0250
0251 In MessageLogger.cc, the ctor of MessageLogger sets suppression_levels
0252 based on the three vectors suppressDebug, suppressInfo and suppressWarning.
0253 Each of those, in turn, were obtained as a vstring from the configuration
0254 (parameters "suppressDebug", "suppressInfo", and "suppressWarning").
0255 Similarly, debugEnabledModules_ is obtained from debugModules, which is
0256 obtained from the vstring parameter "debugModules". anyDebugEnabled is
0257 set based on debugModules being empty, and if this is the case,
0258 MessageDrop::debugEnabled is also set false (which will be relevant
0259 when outside of all modules). Finally, if any of the values in debugModules
0260 is "*", then everyDebugEnabled_ is set true.
0261
0262 Current (until Sep 2010) LogDebug Suppression
0263 --------------------------------------------
0264
0265 The code defining the (macro) LogDebug looked like this:
0266
0267 #ifdef EDM_MESSAGELOGGER_SUPPRESS_LOGDEBUG
0268 #define LogDebug(id) edm::Suppress_LogDebug_()
0269 #define LogTrace(id) edm::Suppress_LogDebug_()
0270 #else
0271 #define LogDebug(id) \
0272 ( !edm::MessageDrop::debugEnabled ) \
0273 ? edm::LogDebug_() \
0274 : edm::LogDebug_(id, __FILE__, __LINE__)
0275 // ...
0276
0277 class Suppress_LogDebug_
0278 {
0279 public:
0280 template< class T >
0281 Suppress_LogDebug_ &operator<< (T const & t) { return *this; }
0282 }; // Suppress_LogDebug_
0283
0284 This implies that **even if debugEnabled is false** a line like
0285
0286 LogDebug ("cat") << foo(objectA) << objectB;
0287
0288 will force the compiler, to properly execute any side effects of operator<<
0289 and of foo(objectA), to do the operator<<'s and to call foo(), even though
0290 the user's intent was surely to skip them. While operator<<, taken from
0291 SuppressLogDebug_, is trivial, foo() probably is not.
0292
0293 Similarly, if debugEnabled is false when LogDebug is not suppressed by the
0294 define, then even though no MessageSender will be created, the operator<<
0295 and foo() calls to construct the message still will be executed.
0296
0297 Placing line numbers into a message
0298 -----------------------------------
0299
0300 In order to place line number and source file name into a message,
0301 the LogDebug token **must** be a macro -- nobody is interested in
0302 the line and file of the place in MessageLogger.h or .cc that the
0303 ctor of some LogDebug class appears.
0304
0305 In retrospect, it was this forcing of the usse of a macro that enticed
0306 us to provide assert-like behavior in terms of eliding LogDebug in the
0307 debug-disabled case. For code in the MessageFacility package, where users
0308 have requested forms of LogInfo etc. having line numbers and file names,
0309 this same issue will arise again, and we should carefully think out what
0310 we and theusers want to do.
0311
0312 Coding details in moving to optimal suppression
0313 -----------------------------------------------
0314
0315 For LogDebug (and LogTrace) ONLY we wish to have a general suppression
0316 variable which does not do anything with the messageDrop singleton and
0317 is controlled solely by a define.
0318
0319 The goal is to prevent the execution of operator<< and foo(), and ideally to
0320 prevent even the inclusion of non-executed code for the operator<< and foo(),
0321 when the user does something like
0322
0323 LogDebug ("cat") << foo(objectA )<< objectB;
0324
0325 and the LogDebug is suppressed. Part one of this goal is to achieve that
0326 optimization when the information indicating suppression is via a macro.
0327 Part two is to (if we can) achieve the optimization under control of a
0328 run-time variable.
0329
0330 The trick for part one (suggested by C. Jones) exploits the lexical
0331 properties of the preprocssor, to constuct a statement that contains
0332 a ?: conditional, with the predicate being compile-time true, and the
0333 operator<< constructs coming out as the tail end of the (unexecuted)
0334 if-false statement. Thus:
0335
0336 #define LogDebug(id) true ? edm::SuppressLogDebug() :edm::SuppressLogDebug()
0337
0338 such that the user statement becomes
0339
0340 true ? edm::SuppressLogDebug() :edm::SuppressLogDebug()
0341 << foo(objectA) << objectB;
0342
0343 and this is equivalent (known at compile time) to
0344
0345 edm::SuppressLogDebug();
0346
0347 which, we notice does not even contain the code for foo(objectA) or any
0348 operator<<.
0349
0350 Part two can also be accomplished, although in that case the code for the
0351 operator<<'s and foo() obviously will be generated. Consider the
0352 following two-tier construct.
0353
0354 #define MIGHTSUPPRESSLOGDEBUG(id) disabling_variable ? \\
0355 SuppressLogDebug_() ? LogDebug_(id)
0356
0357 #ifdef DISABLING_SYMBOL
0358 #define LogDebug(id) true ? SuppressLogDebug_() : SuppressLogDebug_()
0359 #else
0360 #define LogDebug(id) MIGHTSUPPRESSLOGDEBUG(id)
0361 #endif
0362
0363 Then if DISABLING_SYMBOL is true, the user statement becomes
0364
0365 true ? edm::SuppressLogDebug() :edm::SuppressLogDebug()
0366 <<foo(objectA) << objectB;
0367
0368 which we already saw satisfies the part 1 goal. If DISABLING_SYMBOL is false,
0369 the user statement becomes
0370
0371 disabling_variable ? edm::SuppressLogDebug_()
0372 : LogDebug_(id) << foo(objectA) << objectB;
0373
0374 Clearly, if disabling_variable is false, you get the proper LogDebug_ call with
0375 the associated streaming. If disabling_variable is true, you get a ctor of
0376 SuppressLogDebug_() as the entire statement; the anonymous object immediately
0377 goes out of scpe when tthe statement ends, and thus nothing is done. So this
0378 satisfies part two.
0379
0380 We should choose symbol names that are appropriate for the CMS EDM, of course.
0381 It has already been specified that the define the user sees to enable
0382 LogDebug should be EDM_ML_DEBUG. Since this is now to be disconnected from
0383 NDEBUG and other such symbols, we can use it directly. The
0384 MIGHTSUPPRESSLOGDEBUG is an internal to the MessageLogger, but those too
0385 should start with EDM_ML_; a good name is EDM_ML_ENABLED_LOGDEBUG. But we
0386 don't even need that extra level of "indirection" since we are already
0387 going to a #define for LogDebug(id). So the code would look like:
0388
0389 #ifndef EDM_ML_DEBUG
0390 #define LogDebug(id) true ? edm::Suppress_LogDebug_() : edm::Suppress_LogDebug_()
0391 #else
0392 #define LogDebug(id) \\
0393 isDebugEnabled() ? edm::Suppress_LogDebug_() : \\
0394 edm::LogDebug_(id, __FILE__, __LINE__)
0395 #endif
0396
0397 However, the dynamic case of this (if EDM_ML_DEBUG is defined) is not quite
0398 correct C++, since the two possible values for the ?: operator have different
0399 types which cannot be converted to one another.
0400
0401
0402 Here is the "Aha!" moment: Since in each of the cases where debug is disabled,
0403 there is nothing in the expression after Suppress_LogDebug_(), there would
0404 be no harm in using LogDebug() -- the default ctor -- instead. The insight
0405 is that even though nothing was done to make the operator<< be effortless,
0406 that is fine because the operator<< won't appear in executed code!
0407
0408 #ifndef EDM_ML_DEBUG
0409 #define LogDebug(id) true ? edm::Suppress_LogDebug_() : edm::Suppress_LogDebug_()
0410 #else
0411 #define LogDebug(id) \\
0412 isDebugEnabled() ? edm::LogDebug() : \\
0413 edm::LogDebug_(id, __FILE__, __LINE__)
0414 #endif
0415
0416 Finally, because these techniques use preprocessor macros, we need to
0417 look at what happens if the user statement is broken into multiple lines.
0418 As our example:
0419
0420 LogDebug ("cat") << foo(objectA )
0421 << objectB;
0422
0423 In the absense of EDM_ML_DEBUG this would become
0424
0425 true ?edm::SuppressLogDebug_() : edm::SuppressLogDebug_() << foo(objectA)
0426 << objectB;
0427
0428 which is fine. If EDM_ML_DEBUG is defined it becomes
0429
0430 !debugEnabled ? edm::SuppressLogDebug_() : \\
0431 edm::LogDebug_(id, __FILE__, __LINE__) << foo(objectA)
0432 << objectB;
0433
0434 which is also fine.
0435
0436 Effect on "message blocks"
0437 --------------------------
0438
0439 A technique recommended if the user needs to prepare a message in a loop
0440 or with conditional code looks like:
0441
0442 if (isInfoEnabled()) {
0443 edm::LogInfo m("cat");
0444 for (i=0; i<imax; ++i) m << foo(i);
0445 } // m goes out of scope, causing the message to be sent.
0446
0447 Let's see what happens when we use that with LogDebug instead, in light of
0448 these macros.
0449
0450 if (isDebugEnabled()) {
0451 LogDebug m("cat");
0452 for (i=0; i<imax; ++i) m << foo(i);
0453 } // m goes out of scope, causing the message to be sent.
0454
0455 becomes a pre-processing error whether or not EDM_ML_DEBUG is defined
0456 (since LogDebug is a macro requiring an argument, but LogDebug m("cat")
0457 breaks that up at the m). Pre-processor changes in the next version of C++
0458 might allow us to do something more robust, but we need to provide a mantra
0459 that works now.
0460
0461 We will need to recommend a different mantra for LogDebug. The following
0462 will work:
0463
0464 #ifdef EDM_ML_DEBUG
0465 if (isDebugEnabled()) {
0466 edm::LogDebug_ m (LogDebug ("cat"));
0467 for (i=0; i<imax; ++i) m << foo(i);
0468 } // m goes out of scope, causing the message to be sent.
0469 #endif
0470
0471 If EDM_ML_DEBUG is defined that becomes
0472
0473 if (isDebugEnabled()) {
0474 edm::LogDebug_ m (edm::LogDebug_("cat", __FILE__, __LINE__));
0475 for (i=0; i<imax; ++i) m << foo(i);
0476 } // m goes out of scope, causing the message to be sent.
0477
0478 while if EDM_ML_DEBUG is not defined that becomes nothing at all.
0479
0480 We will need to document this technique difference for LogDebug.
0481
0482 By the way, is right to inline the functions isDebugEnabled(),
0483 isInfoEnabled() and isWarningEnabled(), to make this quicker.
0484 In particular, isDebugEnabled() is important since it will be in the
0485 path of dynamically suppressed LogDebug's.
0486
0487 Strategy for testing EDM_ML_DEBUG
0488 ---------------------------------
0489
0490 Existing tests (as of Sep 2010) assumed LogDebug would be enabled buy default.
0491 This was about to change, so naively the tests would need to be modified to
0492 reflect the change.
0493
0494 A first step was to temporarily define EDM_ML_DEBUG in MessageLogger.h
0495 just to see that existing unit tests wre not directly broken by the changes
0496 made, in circumstances where the behavior should not have changed. That worked
0497 fine.
0498
0499 Next we want to see whether our mantra for users to enable LogDebug will work.
0500 So we do a build with the right define on the build line and see if the unit
0501 tests still pass.
0502 scram b clean; scram b USER_CXXFLAGS="-DEDM_ML_DEBUG"
0503 worked. Without the clean, scram does not
0504
0505 Next, we eliminated that temporary define and had to look at the outputs of all
0506 unit tests that broke. For the ones breaking in the now-proper way, we
0507 moved the new correct outputs into the output area. But this is not sufficient:
0508 it would leave us with unit tests that completely skip testiong LogDebug.
0509
0510 The last step is to create tests that work with LogDebug enabled, and there
0511 are two sorts of these tests: a) Those where LogDebugs are still suppressed
0512 by configuration; b) those where LogDebugs should cause output. Fortunately,
0513 once we enable LogDebug, both sorts are tested by our suite, because typical
0514 unit tests produce both a file with LogDebug, and file(s) with higher
0515 thresholds. But how to force the define of DEDM_ML_DEBUG in some parts
0516 of the build but not others? That can be done in the buildfile, via
0517 flags lines. We choose some of the unit tests to do those on.
0518
0519 Where is it determined that u1, for instance, works with UnitTestClient_A?
0520 Certainly not in u1.cpp or u1.sh, which are trivial. It is in u1_cfg.py,
0521 where we have
0522 process.sendSomeMessages = cms.EDAnalyzer("UnitTestClient_A")
0523 So if we wish to create a debug-enabled test corresponding to u1, we should
0524 be able to copy u1_cfg.py to u1d_cfg.py, change the UnitTestClient_A to
0525 UnitTestClient_Ad, and in the Buildfile.xml, have something like the lines
0526 <library file="UnitTestClient_A.cc" name="UnitTestClient_A">
0527 <flags EDM_PLUGIN="1" -DEDM_ML_DEBUG/>
0528 <use name="FWCore/MessageLogger"/>
0529 <use name="FWCore/Framework"/>
0530 </library>
0531 Unfortunately that does not work (parse error on -DEDM_ML_DEBUG) and after a few
0532 more tries, it seemed wrong to continue wandering around without documentation.
0533 Instead of changing the build file, we can make a new client source
0534 UnitTestClient_Ad.cc, identical to UnitTestClient_A.cc but with
0535 #define EDM_ML_DEBUG at the top.
0536
0537
0538 Optimization when no threshold is low enough
0539 --------------------------------------------
0540
0541 There was as of 9/21/110 no mechanism to disable LogInfo if all thresholds are
0542 higher than logInfo, and similarly for logWarning (not sure if the issue
0543 is relevant for logDebug). This could be a huge savings. Not easy to do,
0544 but should not be too tricky.
0545
0546 The tough part is that while looking for suppressInfo in the configuration
0547 is easy enough, seeing whether any destination has a high enough threshold
0548 is involved, so that it needs to be done in the configuration code that is
0549 down in the scribe. So the strategy will have to be setting up a vairiable
0550 that is used in the scribe thread to set the field in the MessageDrop, just
0551 as when a suppressed module is entered.
0552
0553 The appropriate variables really are statics in MessageDrop, and we name them
0554 debugAlwaysSuppressed, infoAlwaysSuppressed, warningAlwaysSuppressed.
0555 The usage for the LogInfo and LogWarning cases is that the ctor looks like
0556 explicit LogInfo( std::string const & id )
0557 : ap ( (!MessageDrop::infoAlwaysSuppressed // Change log 21
0558 && edm::MessageDrop::instance()->infoEnabled) ?
0559 new MessageSender(ELinfo,id) : 0 )
0560 Observe that since !MessageDrop::infoAlwaysSuppressed is checked first,
0561 in the important case of universal suppression, the overhead for the
0562 MessageDrop::instance() call is not incurred -- this is the secondary
0563 performance improvement. (The main improvement is that in config files which
0564 do not explicitly suppress INFO, but where no thresholds are as low as INFO,
0565 the info messages get suppressed anyway.)
0566
0567 The usage is a bit different for the debug case: The define line (in the
0568 case where EDM_ML_DEBUG is defined so that LogDebug is not out of the question)
0569 becomes:
0570
0571 #define LogDebug(id) \\
0572 (edm::MessageDrop::debugAlwaysSuppressed \\
0573 || !edm::MessageDrop::debugEnabled) ? \\
0574 edm::LogDebug_() : edm::LogDebug_(id, __FILE__, __LINE__)
0575
0576 and, since if the logDebug is going to be suppressed (for whichever reason)
0577 the default ctor of LogDebug_ is called, which sets the class instance variable
0578 debugEnabled to false, nothing needs to be changed in the LogDebug_ class.
0579 (For reasons of paranoia, in the meaty ctor for LogDebug_, we do initialize
0580 debugEnabled to !debugAlwaysSuppressed rather than true, but this may be
0581 overkill.)
0582
0583 That leaves the issue of how (and where) to establish the values of
0584 debugAlwaysSuppressed and its bretheren. Since for EVERY destination
0585 configure_dest (in the scribe) sets the threshold severity explicitly,
0586 this is a common choke point, at which we can adjust these variables.
0587 What that implies is that they must start off true, and become false
0588 when some destination is configured to have a low enough threshold.
0589 Actually, just in case some path avoids configuration altogether, we
0590 start these off as false by initialization, change them to true once
0591 configure_ordinary_destinations() is reached, and then change them in
0592 configure_dest() if and when a low enough threshold is encountered.
0593
0594 Having implemented these changes, we now wish to find a clean way of
0595 testing whether the changes were effective. This is harder than it
0596 looks, since the suprressed messages were not going to have any effect
0597 even before this performance-only improvement. And one would like to
0598 make this a test that can be part of the test suite.
0599
0600 The technique to do this testing is as follows:
0601
0602 1) Add an extra class in MessageLogger.h, namely
0603 LogWarningThatSuppressesLikeLogInfo. To protect
0604 against some user using this, hide it in a sub-namespace
0605 edm::edmtest. This class does its test for suppression
0606 using infoEnabled but uses a severity level of LogWarning.
0607
0608 2) Create a unit test that does some LogWarning's and some
0609 LogInfo's, and also some LogWarningThatSuppressesLikeLogInfo's.
0610
0611 3) Create a cfg file (A) with one destination,which sets a
0612 threshold of LogWarning, and another (B) with two destinations, one
0613 of which has threshold of LogWarning, the other LogInfo. Each
0614 uses this new unit test.
0615
0616 4) In cmsRun (A), no trace of the LogWarningThatSuppressesLikeLogInfo
0617 should appear. In cmsRun (B), it should appear in the both destinations,
0618 as a %MSG-w message (warning).
0619
0620 Possible further improvement opportunities
0621 ------------------------------------------
0622
0623 The initial value of MessageDrop->debugEnabled (in MessageDrop.cc) is
0624 true. This is perhaps relevant in cases where debug is overall enabled,
0625 and one is not in any module. So you run with debug on, and if the
0626 framework itself has LogDebugs in it, you can't suppress them without
0627 suppressing all debug messages. It might be better to make this false.
0628 (Trivial change to make.) It might also be desirable to have some way
0629 to turn on/off non-module LogDebugs. However, this issue is very likely
0630 moot since users don't write non-module code, and probably framework
0631 people are not leaving many LogDebugs around.
0632
0633 ---
0634
0635 Currently, LogDebug depends on the define EDM_ML_DEBUG at header time.
0636 So the user who wants to define this symbol must do so before the header,
0637 and has control only on a per-file basis. It might be better to give users
0638 finer control by putting the #ifdef inside the definition of LogDebug, as in
0639 #define LogDebug(id) \\
0640 #ifndef EDM_ML_DEBUG \\
0641 true ? edm::Suppress_LogDebug_() : edm::Suppress_LogDebug_() \\
0642 #else \\
0643 #define LogDebug(id) \\
0644 isDebugEnabled() ? edm::LogDebug() : \\
0645 edm::LogDebug_(id, __FILE__, __LINE__) \\
0646 #endif
0647 This of course is not quite right, and the business of line ends and
0648 continuations might be a severe pain in the neck, which is why we have not
0649 made this improvement.
0650
0651 ---
0652
0653 This is not strictly speaking only a suppression issue but is in the same code
0654 area. Currently every time a module is entered or exitted, a call to
0655 MessageDrop::instance() is made to tell where to put information such as
0656 the suppression variables and the module label. Although nobody is currently
0657 griping about these calls, they may be taking non-negligible time.
0658 If the user could declare "no need for module names in messages" then
0659 when there is also no ML configuration information that is module sensitive
0660 (that is, any suppress or enable directives use "*"), then we can do without
0661 this instance() call. Some simple decisions would need to be made, such as
0662 what module name to output or whether to modify ELoutput to eliminate the
0663 module name when this option is chosen; and the option would have to be coded
0664 into the configuration and validation and the documentation and into the
0665 establish_module routines (or into the code that puts the establish_module
0666 routine onto the callback stack in the first place; probably the whole callback
0667 would now be superfluous).
0668
0669 This would be several day's work but might be worthwhile.
0670
0671 ---