// ball_logthrottle.h -*-C++-*- #ifndef INCLUDED_BALL_LOGTHROTTLE #define INCLUDED_BALL_LOGTHROTTLE #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide throttling equivalents of some of the 'ball_log' macros. // //@MACROS: BALL_LOGTHROTTLE_TRACE, BALL_LOGTHROTTLE_DEBUG, // BALL_LOGTHROTTLE_INFO, BALL_LOGTHROTTLE_WARN, // BALL_LOGTHROTTLE_ERROR, BALL_LOGTHROTTLE_FATAL, // BALL_LOGTHROTTLE_STREAM, // // BALL_LOGTHROTTLE_TRACE_BLOCK, BALL_LOGTHROTTLE_DEBUG_BLOCK, // BALL_LOGTHROTTLE_INFO_BLOCK, BALL_LOGTHROTTLE_WARN_BLOCK, // BALL_LOGTHROTTLE_ERROR_BLOCK, BALL_LOGTHROTTLE_FATAL_BLOCK, // BALL_LOGTHROTTLE_BLOCK, // // BALL_LOGTHROTTLEVA_TRACE, BALL_LOGTHROTTLEVA_DEBUG, // BALL_LOGTHROTTLEVA_INFO, BALL_LOGTHROTTLEVA_WARN, // BALL_LOGTHROTTLEVA_ERROR, BALL_LOGTHROTTLEVA_FATAL, // BALL_LOGTHROTTLEVA // //@SEE_ALSO: ball_log, bdlmt_throttle // //@DESCRIPTION: This component provides numerous macros for performing logging // where the number of messages logged is "throttled", meaning that the number // of messages that will be logged within a given time interval is limited. // The macros in this component are all analogous to corresponding macros in // 'ball_log'. For example, the throttling version of 'BALL_LOG_INFO' is // 'BALL_LOGTHROTTLE_INFO', and the throttling version of 'BALL_LOGVA' is // 'BALL_LOGTHROTTLEVA'. // // Each log message has a 'SEVERITY' associated with it (see 'ball_severity' // for the definitions of the six standard severities). Those macros that // don't contain a 'SEVERITY' in their name are passed an integral severity // value in the range '[0 .. 255]', with suggested values defined in // 'ball::Severity', as their first argument. The next two arguments, // 'MAX_SIMULTANEOUS_MESSAGES' and 'NANOSECONDS_PER_MESSAGE', are described // immediately below. // // Each invocation of any of the macros provided by this component instantiates // its own 'bdlmt::Throttle' object to effect the throttling behavior. Note // that each throttle object is statically declared, so it is shared by all // threads. // ///Throttling Concepts ///------------------- // As clients attempt to output messages using the throttling log macros, the // throttle instantiated by each of the macros accumulates a time debt for // those messages that dissipates over time. The maximum value for this time // debt is given by 'MAX_SIMULTANEOUS_MESSAGES * NANOSECONDS_PER_MESSAGE'. The // 'MAX_SIMULTANEOUS_MESSAGES' configuration parameter thereby limits the // maximum number of messages that will be permitted in an arbitrarily short // time, and debt is "paid off" at a rate of '1 / NANOSECONDS_PER_MESSAGE'. // // This behavior is known as a "leaky-bucket": messages permitted place water // in the bucket, the passage of time drains water from the bucket, and the // bucket has a maximum capacity. Messages are permitted only when there is // enough empty room in the bucket such that the additional water poured in // won't overflow it. A leaky bucket is an efficiently implementable // approximation for allowing only a certain number of messages over a window // of time. // ///Leaky Bucket and Time Debt Example /// - - - - - - - - - - - - - - - - - // In this section we illustrate time debt via a leaky bucket whose // 'MAX_SIMULTANEOUS_MESSAGES' and 'NANOSECONDS_PER_MESSAGE' constraints are 4 // and '10LL * 1000 * 1000 * 1000' (10 seconds), respectively. For ease of // exposition, all nanosecond quantities in the rest of this example are // expressed in seconds. // // Given the leaky bucket just described, we may observe the sequence of events // as displayed in the this table: //.. // +--------------+----------------+----------------+---------------+ // | Time | Time Debt | Message | Time Debt | // | in Seconds | in Seconds | Permitted? | in Seconds | // | Since First | Before Logging | Threshold: | After Log or | // | Message | Attempt | (4-1)*10 = 30 | Attempt | // +--------------+----------------+----------------+---------------+ // | 0 | 0 | 0 <= 30 Yes | 0+10 -> 10 | // | 2 | 10- 2 -> 8 | 8 <= 30 Yes | 8+10 -> 18 | // | 4 | 18- 2 -> 16 | 16 <= 30 Yes | 16+10 -> 26 | // | 8 | 26- 4 -> 22 | 22 <= 30 Yes | 22+10 -> 32 | // | 9 | 32- 1 -> 31 | 31 > 30 No | 31+ 0 -> 31 | // | 10 | 31- 1 -> 30 | 30 <= 30 Yes | 30+10 -> 40 | // | 11 | 40- 1 -> 39 | 39 > 30 No | 39+ 0 -> 39 | // | 12 | 39- 1 -> 38 | 38 > 30 No | 38+ 0 -> 38 | // | 13 | 38- 1 -> 37 | 37 > 30 No | 37+ 0 -> 37 | // | 14 | 37- 1 -> 36 | 36 > 30 No | 36+ 0 -> 36 | // | 15 | 36- 1 -> 35 | 35 > 30 No | 35+ 0 -> 35 | // | 35 | 35-20 -> 15 | 15 <= 30 Yes | 15+10 -> 25 | // | 75 | 25-40 -> 0* | 0 <= 30 Yes | 0+10 -> 10 | // +--------------+----------------+----------------+---------------+ // // * -- At 'Time == 75', the time debt goes to zero, even though 25-40 is // negative -- the time debt can never go negative, and never above the // bucket capacity of 40. //.. // So observe that at 8 seconds, the time debt to begin with is 26. 4 seconds // have elapsed since the previous event at the 4-second mark, so the time debt // becomes '26 - 4 == 22'. This is less than the threshold of 30, so the // message is permitted, and we add the time per message, which is 10 billion // nanoseconds, or 10 seconds, and the time debt at the end is 32 seconds. // // Then observe that at the next event, at 9 seconds, the time debt to begin // with is 32. One second has elapsed since the previous event, so the time // debt becomes '32 - 1 == 31'. This is higher than the threshold of 30, so // the message is not permitted, and the time debt remains 31. // // Note that it is never possible for more than 'MAX_SIMULTANEOUS_MESSAGES' to // occur within any time span of less than 'NANOSECONDS_PER_MESSAGE' // nanoseconds. // ///Thread Safety ///------------- // All macros defined in this component are thread-safe, and can be invoked // concurrently by multiple threads. // ///Macro Reference ///--------------- // This section documents the preprocessor macros defined in this component. // Note that the 'MAX_SIMULTANEOUS_MESSAGES' and 'NANOSECONDS_PER_MESSAGE' // arguments to all the macros defined in this component must be compile-time // constants, and may not contain any floating-point subexpressions. Also note // that 'MAX_SIMULTANEOUS_MESSAGES' is of type 'int', // 'NANOSECONDS_PER_MESSAGE' is of type 'bsls::Types::Int64', and any // 'SEVERITY' argument is of type 'int' and is interpreted as described by the // 'ball_severity' component. // // The following constraints pertain to all of the macros defined in this // component. // //: o 'MAX_SIMULTANEOUS_MESSAGES' and 'NANOSECONDS_PER_MESSAGE' must be //: compile-time constants, and may not contain any floating-point //: subexpressions. //: //: o 'MAX_SIMULTANEOUS_MESSAGES', 'NANOSECONDS_PER_MESSAGE', and 'SEVERITY' //: are of types 'int', 'bsls::Types::Int64', and 'int', respectively. //: //: o The behavior is undefined unless 'SEVERITY' is in the range '[0 .. 255]', //: '0 <= MAX_SIMULTANEOUS_MESSAGES', '0 <= NANOSECONDS_PER_MESSAGE', //: '0 < MAX_SIMULTANEOUS_MESSAGES || 0 < NANOSECONDS_PER_MESSAGE', and //: 'MAX_SIMULTANEOUS_MESSAGES * NANOSECONDS_PER_MESSAGE <= LLONG_MAX'. // ///Example Shorthands /// - - - - - - - - - // The following two constants will be used in the examples immediately // following the macro documentation: //.. // static const int k_MSM = 4; // 'MAX_SIMULTANEOUS_MESSAGES' argument // static const bsls::Types::Int64 k_NPM = 10LL * 1000 * 1000 * 1000; // // 'NANOSECONDS_PER_MESSAGE' argument //.. // ///Stream-Based Throttling Macros /// - - - - - - - - - - - - - - - // The 'BALL_LOGTHROTTLE_*' macros are analogous to the 'BALL_LOG_*' macros, // except that they take two additional throttle-related arguments, // 'MAX_SIMULTANEOUS_MESSAGE' and 'NANOSECONDS_PER_MESSAGE', described in // {Throttling Concepts} above. // // A set of macros based on C++ streams, 'BALL_LOGTHROTTLE_TRACE', // 'BALL_LOGTHROTTLE_DEBUG', 'BALL_LOGTHROTTLE_INFO', 'BALL_LOGTHROTTLE_WARN', // 'BALL_LOGTHROTTLE_ERROR', and 'BALL_LOGTHROTTLE_FATAL', are the ones most // commonly used for log throttling. They have the following usage pattern: //.. // BALL_LOGTHROTTLE_<SEVERITY>(MAX_SIMULTANEOUS_MESSAGES, // NANOSECONDS_PER_MESSAGE) << X << Y ... ; // Throttle logging with the specified 'MAX_SIMULTANEOUS_MESSAGES' and // 'NANOSECONDS_PER_MESSAGE' as described in {Throttling Concepts}, where // 'X, Y, ...' represents any sequence of values for which 'operator<<' is // defined. If the throttle permits a message to be logged, the resulting // formatted message is logged with the severity indicated by the name of // the macro (e.g., 'BALL_LOGTHROTTLE_ERROR' logs with severity // 'ball::Severity::e_ERROR'). //.. // For example: //.. // int cpu = 6502; // BALL_LOGTHROTTLE_ERROR(k_MSM, k_NPM) << // "Help, I'm being held prisoner in a " << cpu << " microprocessor!"; //.. // A closely-related macro also based on C++ streams, // 'BALL_LOGTHROTTLE_STREAM', requires that the severity be explicitly supplied // as an argument: //.. // BALL_LOGTHROTTLE_STREAM(SEVERITY, // MAX_SIMULTANEOUS_MESSAGES, // NANOSECONDS_PER_MESSAGE) << X << Y ... ; // Throttle logging with the specified 'MAX_SIMULTANEOUS_MESSAGES' and // 'NANOSECONDS_PER_MESSAGE' as described in {Throttling Concepts}, where // 'X, Y, ...' represents any sequence of values for which 'operator<<' is // defined. If the throttle permits a message to be logged, the resulting // formatted message is logged with the specified 'SEVERITY'. //.. // For example: //.. // int cpu = 6502; // BALL_LOGTHROTTLE_STREAM(ball::Severity::e_ERROR, k_MSM, k_NPM) << // "Help, I'm being held prisoner in a " << cpu << " microprocessor!"; //.. // ///BLOCK-Style Throttling Macros ///- - - - - - - - - - - - - - - // The 'BALL_LOGTHROTTLE_*_BLOCK' macros are analogous to the // 'BALL_LOG_*_BLOCK' macros, except that they take two throttle-related // arguments, 'MAX_SIMULTANEOUS_MESSAGES' and 'NANOSECONDS_PER_MESSAGE', // described in {Throttling Concepts} above. //.. // BALL_LOGTHROTTLE_<SEVERITY>_BLOCK(MAX_SIMULTANEOUS_MESSAGES, // NANOSECONDS_PER_MESSAGE) <block> // Throttle logging with the specified 'MAX_SIMULTANEOUS_MESSAGES' and // 'NANOSECONDS_PER_MESSAGE' as described in {Throttling Concepts}, where // any sequence of values for which 'operator<<' is defined may be // streamed to 'BALL_LOG_OUTPUT_STREAM' within the controlled '<block>'. // If the throttle permits a message to be logged, the resulting formatted // message is logged with the severity indicated by the name of the macro // (e.g., 'BALL_LOGTHROTTLE_WARN_BLOCK' logs with severity // 'ball::Severity::e_WARN'). //.. // For example: //.. // BALL_LOGTHROTTLE_ERROR_BLOCK(k_MSM, k_NPM) { // int cpu = 6502; // if (x < y(z) + 7.2) { // BALL_LOG_OUTPUT_STREAM << "Help, I'm being held prisoner in a " << // cpu << " microprocessor!"; // } // } //.. // A closely-related BLOCK-based macro, 'BALL_LOGTHROTTLE_BLOCK', requires that // the severity be explicitly supplied as an argument: //.. // BALL_LOGTHROTTLE_BLOCK(SEVERITY, // MAX_SIMULTANEOUS_MESSAGES, // NANOSECONDS_PER_MESSAGE) <block> // Throttle logging with the specified 'MAX_SIMULTANEOUS_MESSAGES' and // 'NANOSECONDS_PER_MESSAGE' as described in {Throttling Concepts}, where // any sequence of values for which 'operator<<' is defined may be // streamed to 'BALL_LOG_OUTPUT_STREAM' within the controlled '<block>'. // If the throttle permits a message to be logged, the resulting formatted // message is logged with the specified 'SEVERITY'. //.. // The behavior of the following example is exactly identical to that of the // example above: //.. // BALL_LOGTHROTTLE_BLOCK(ball::Severity::e_ERROR, k_MSM, k_NPM) { // int cpu = 6502; // if (x < y(z) + 7.2) { // BALL_LOG_OUTPUT_STREAM << "Help, I'm being held prisoner in a " << // cpu << " microprocessor!"; // } // } //.. // ///'printf'-Style Throttling Macros /// - - - - - - - - - - - - - - - - // The 'BALL_LOGTHROTTLEVA_*' macros are analogous to the 'BALL_LOGVA_*' // macros, except that they take two additional throttle-related arguments, // 'MAX_SIMULTANEOUS_MESSAGES' and 'NANOSECONDS_PER_MESSAGE', described in // {Throttling Concepts} above. //.. // BALL_LOGTHROTTLEVA_<SEVERITY>(MAX_SIMULTANEOUS_MESSAGE, // NANOSECONDS_PER_MESSAGE, // MSG, // ...); // Throttle logging with the specified 'MAX_SIMULTANEOUS_MESSAGES' and // 'NANOSECONDS_PER_MESSAGE' as described in {Throttling Concepts}. If // the throttle permits a message to be logged, format the specified '...' // optional arguments, if any, according to the 'printf'-style format // specification in the specified 'MSG' (assumed to be of type convertible // to 'const char *'), and log the resulting formatted message with the // severity indicated by the name of the macro (e.g., // 'BALL_LOGTHROTTLEVA_INFO' logs with severity 'ball::Severity::e_INFO'). // The behavior is undefined unless the number and types of the optional // arguments are compatible with the format specification in 'MSG'. Note // that each use of these macros must be terminated by a ';'. //.. // For example: //.. // int cpu = 6502; // BALL_LOGTHROTTLEVA_ERROR(k_MSM, k_NPM, "Help, I'm being held prisoner" // " in a %d microprocessor!", cpu); //.. // A closely related macro is 'BALL_LOGTHROTTLEVA' that takes the 'SEVERITY' as // an argument, rather than being part of the macro name. //.. // BALL_LOGTHROTTLEVA(SEVERITY, // MAX_SIMULTANEOUS_MESSAGES, // NANOSECONDS_PER_MESSAGE, // MSG, // ...); // Throttle logging with the specified 'MAX_SIMULTANEOUS_MESSAGES' and // 'NANOSECONDS_PER_MESSAGE' as described in {Throttling Concepts}. If // the throttle permits a message to be logged, format the specified '...' // optional arguments, if any, according to the 'printf'-style format // specification in the specified 'MSG' (assumed to be of type convertible // to 'const char *'), and log the resulting formatted message with the // specified 'SEVERITY'. The behavior is undefined unless the number and // types of the optional arguments are compatible with the format // specification in 'MSG'. Note that each use of this macro must be // terminated by a ';'. //.. // For example: //.. // int cpu = 6502; // BALL_LOGTHROTTLEVA_ERROR(ball::Severity::e_ERROR, // k_MSM, // k_NPM, // "Help, I'm being held prisoner" // " in a %d microprocessor!", // cpu); //.. // ///Usage ///----- // This section illustrates the intended use of this component. // // The following constants are used throughout the usage examples: //.. // enum { // k_NUM_INFO = 20, // max # of info messages in a very short time // k_NUM_DEBUG = 5, // max # of debug messages in a very short time // k_NUM_TRACE = 1 // max # of trace messages in a very short time // }; // // const Int64 k_NS_PER_HOUR = // BloombergLP::bdlt::TimeUnitRatio::k_NANOSECONDS_PER_HOUR; // // const Int64 k_NS_PER_INFO = k_NS_PER_HOUR / k_NUM_INFO; // // long-term minimum nanoseconds per info message permitted // const Int64 k_NS_PER_DEBUG = k_NS_PER_HOUR / k_NUM_DEBUG; // // long-term minimum nanoseconds per debug message permitted // const Int64 k_NS_PER_TRACE = k_NS_PER_HOUR / k_NUM_TRACE; // // long-term minimum nanoseconds per trace message permitted //.. // ///Example 1: C++ Stream-Style Throttling Macro Usage /// - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose a computer is reading 'double' values from a radio receiver, ten per // second, which represent readings of radiation detected by a Geiger counter // on a spacecraft, and is transmitting them to a ground control at Jet // Propulsion Laboratories in California. // // The readings are returned by the 'double yield()' manipulator of a // 'RadiationMeterReceiver' object (the implementation of which is omitted). // The 'yield' method blocks until it obtains a reading to return. If called // in a tight loop, 'yield' returns ten readings per second. // // Readings range from 0 to 100. //: o Readings above 10 but not greater than 30 are a concern, but are not very //: serious. We will report those with an 'e_TRACE' severity, and at most //: one per hour (i.e., messages will be throttled). //: o Readings above 30 but not greater than 60 are more of a worry. We will //: report those with an 'e_DEBUG' severity, and at most five per hour. //: o Readings above 60 but not greater than 90 are very serious. They will be //: reported with an 'e_INFO' severity, and at most twenty per hour. //: o Readings above 90 are potentially catastrophic, and will be reported with //: an 'e_WARN' severity, with no limit on the number of readings reported //: (i.e., no throttling). // // We are to write a daemon process, which will loop gathering readings. A // reading of an impossible value of -1.0 will indicate termination. //.. // void radiationMonitorStreamDaemon() // // Daemon to run the radiation monitor. // { // BALL_LOG_SET_CATEGORY("RADIATION.MONITOR"); // // RadiationMeterReceiver receiver; // // BALL_LOG_DEBUG << "Start gathering data."; // // double reading; // while (-1.0 != (reading = receiver.yield())) { //.. // First, we deal with 'e_WARN' readings: //.. // if (90 < reading) { // BALL_LOG_WARN << "Serious Radiation reading of " << reading; // } //.. // Next, we deal with 'e_INFO' readings, which aren't as severe as 'e_WARN': //.. // else if (60 < reading) { // BALL_LOGTHROTTLE_INFO(k_NUM_INFO, k_NS_PER_INFO) << // "Radiation reading of " << reading; // } //.. // Now, we deal with 'e_DEBUG' messages less severe than 'e_INFO' readings: //.. // else if (30 < reading) { // BALL_LOGTHROTTLE_DEBUG(k_NUM_DEBUG, k_NS_PER_DEBUG) << // "Radiation reading of " << reading; // } //.. // Finally, we deal with 'e_TRACE' messages less severe than 'e_DEBUG' // readings: //.. // else if (10 < reading) { // BALL_LOGTHROTTLE_TRACE(k_NUM_TRACE, k_NS_PER_TRACE) << // "Radiation reading of " << reading; // } // } // // BALL_LOG_DEBUG << "Finished gathering data."; // } //.. // The values returned by 'receiver.yield()' are: //.. // 0 0 12.3 0 10.5 33.1 11.9 53.7 0 0 46.1 14.7 67.4 43.9 53.3 98.2 0 22.3 // 77.3 36.2 0 17.7 52.5 0 43.2 0 72.9 0 51.9 71.2 92.4 0 0 11.8 33.1 0 47.2 // 15.5 35.7 0 22.3 17.6 0 52.7 0 22.1 -1 //.. // Where: //: o 13 readings of 0.0, which don't produce output, occurred. //: o 9 readings in the range '(10.0 .. 30.0]', which correspond to 'e_TRACE' //: level messages, occurred. //: o 13 readings in the range '(30.0 .. 60.0]', which correspond to 'e_DEBUG' //: level messages, occurred. //: o 5 readings in the range '(60.0 .. 90.0]', which correspond to 'e_INFO' //: level messages, occurred. //: o 2 readings greater than 90.0, which correspond to 'e_WARN' level //: messages, occurred. // // Note that only 1 'e_TRACE' message and 5 'e_DEBUG' messages are permitted by // the throttle within the (very long) time period of one hour, so the other // messages at those levels will be suppressed. // // 'radiationMonitorPrintfDaemon' produces output like: //.. // 24APR2018_16:36:22.791 61260 139907579877152 DEBUG ball_logthrottle.t.cpp // 460 RADIATION.MONITOR Start gathering data. // // 24APR2018_16:36:23.094 61260 139907579877152 TRACE ball_logthrottle.t.cpp // 488 RADIATION.MONITOR Radiation reading of 12.3 // // 24APR2018_16:36:23.396 61260 139907579877152 DEBUG ball_logthrottle.t.cpp // 481 RADIATION.MONITOR Radiation reading of 33.1 // // 24APR2018_16:36:23.597 61260 139907579877152 DEBUG ball_logthrottle.t.cpp // 481 RADIATION.MONITOR Radiation reading of 53.7 // // 24APR2018_16:36:23.901 61260 139907579877152 DEBUG ball_logthrottle.t.cpp // 481 RADIATION.MONITOR Radiation reading of 46.1 // // 24APR2018_16:36:24.102 61260 139907579877152 INFO ball_logthrottle.t.cpp // 474 RADIATION.MONITOR Radiation reading of 67.4 // // 24APR2018_16:36:24.203 61260 139907579877152 DEBUG ball_logthrottle.t.cpp // 481 RADIATION.MONITOR Radiation reading of 43.9 // // 24APR2018_16:36:24.304 61260 139907579877152 DEBUG ball_logthrottle.t.cpp // 481 RADIATION.MONITOR Radiation reading of 53.3 // // 24APR2018_16:36:24.404 61260 139907579877152 WARN ball_logthrottle.t.cpp // 468 RADIATION.MONITOR Serious Radiation reading of 98.2 // // 24APR2018_16:36:24.706 61260 139907579877152 INFO ball_logthrottle.t.cpp // 474 RADIATION.MONITOR Radiation reading of 77.3 // // 24APR2018_16:36:25.513 61260 139907579877152 INFO ball_logthrottle.t.cpp // 474 RADIATION.MONITOR Radiation reading of 72.9 // // 24APR2018_16:36:25.816 61260 139907579877152 INFO ball_logthrottle.t.cpp // 474 RADIATION.MONITOR Radiation reading of 71.2 // // 24APR2018_16:36:25.918 61260 139907579877152 WARN ball_logthrottle.t.cpp // 468 RADIATION.MONITOR Serious Radiation reading of 92.4 // // 24APR2018_16:36:27.429 61260 139907579877152 DEBUG ball_logthrottle.t.cpp // 493 RADIATION.MONITOR Finished gathering data. //.. // Note that 8 'e_TRACE' messages and 8 'e_DEBUG' messages were suppressed by // the throttling. // ///Example 2: BLOCK-Style Throttling Macro Usage ///- - - - - - - - - - - - - - - - - - - - - - - // Here, we just repeat exactly the same code, using the BLOCK-style throttling // macros instead of the stream-style throttling macros: //.. // void radiationMonitorBlockDaemon() // // Daemon to run the radiation monitor. // { // BALL_LOG_SET_CATEGORY("RADIATION.MONITOR"); // // RadiationMeterReceiver receiver; // // BALL_LOGVA_DEBUG("Start gathering data."); // // double reading; // while (-1.0 != (reading = receiver.yield())) { //.. // First, we deal with 'e_WARN' messages: //.. // if (90 < reading) { // BALL_LOG_WARN_BLOCK { // BALL_LOG_OUTPUT_STREAM << // "Serious radiation reading of " << reading; // } // } //.. // Next, we deal with 'e_INFO' messages that aren't as severe as 'e_WARN': //.. // else if (60 < reading) { // BALL_LOGTHROTTLE_INFO_BLOCK(k_NUM_INFO, k_NS_PER_INFO) { // BALL_LOG_OUTPUT_STREAM << // "Radiation reading of " << reading; // } // } //.. // Now, we deal with 'e_DEBUG' messages less severe than 'e_INFO' readings: //.. // else if (30 < reading) { // BALL_LOGTHROTTLE_DEBUG_BLOCK(k_NUM_DEBUG, k_NS_PER_DEBUG) { // BALL_LOG_OUTPUT_STREAM << // "Radiation reading of " << reading; // } // } //.. // Finally, we deal with 'e_TRACE' messages less severe than 'e_DEBUG' // readings. //.. // else if (10 < reading) { // BALL_LOGTHROTTLE_TRACE_BLOCK(k_NUM_TRACE, k_NS_PER_TRACE) { // BALL_LOG_OUTPUT_STREAM << "Radiation reading of " // << reading; // } // } // } // // BALL_LOG_DEBUG << "Finished gathering data."; // } //.. // If the values returned by 'receiver.yield()' match those from Usage Example // 1, then the output will be identical to that example. // ///Example 3: 'printf'-Style Throttling Macro Usage /// - - - - - - - - - - - - - - - - - - - - - - - - // Here, we again repeat exactly the same code, using the 'printf'-style // throttling macros: //.. // void radiationMonitorPrintfDaemon() // // Daemon to run the radiation monitor. // { // BALL_LOG_SET_CATEGORY("RADIATION.MONITOR"); // // RadiationMeterReceiver receiver; // // BALL_LOGVA_DEBUG("Start gathering data."); // // double reading; // while (-1.0 != (reading = receiver.yield())) { //.. // First, we deal with 'e_WARN' messages: //.. // if (90 < reading) { // BALL_LOGVA_WARN("Serious radiation reading of %g", reading); // } //.. // Next, we deal with 'e_INFO' messages that aren't as severe as 'e_WARN': //.. // else if (60 < reading) { // BALL_LOGTHROTTLEVA_INFO( // k_NUM_INFO, // k_NS_PER_INFO, // "Radiation reading of %g", reading); // } //.. // Now, we deal with 'e_DEBUG' messages less severe than 'e_INFO' readings: //.. // else if (30 < reading) { // BALL_LOGTHROTTLEVA_DEBUG( // k_NUM_DEBUG, // k_NS_PER_DEBUG, // "Radiation reading of %g", reading); // } //.. // Finally, we deal with 'e_TRACE' messages less severe than 'e_DEBUG' // readings: //.. // else if (10 < reading) { // BALL_LOGTHROTTLEVA_TRACE( // k_NUM_TRACE, // k_NS_PER_TRACE, // "Radiation reading of %g", reading); // } // } // // BALL_LOGVA_DEBUG("Finished gathering data."); // } //.. // If the values returned by 'receiver.yield()' match those from Usage Example // 1, then the output will be identical to that example. #include <balscm_version.h> #include <ball_category.h> #include <ball_log.h> #include <ball_severity.h> #include <bdlmt_throttle.h> // ==================================== // Implementation Details: Do *NOT* Use // ==================================== #define BALL_LOGTHROTTLE_STREAM_CONST_IMP(SEVERITY, \ MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE) \ for (const BloombergLP::ball::CategoryHolder *ball_logthrottle_cAtEgOrYhOlDeR \ = BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>( \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)); \ ball_logthrottle_cAtEgOrYhOlDeR; \ ball_logthrottle_cAtEgOrYhOlDeR = 0) \ for (static BloombergLP::bdlmt::Throttle ball_logthrottle_tHrOtTlE = \ BDLMT_THROTTLE_INIT((MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)); \ ball_logthrottle_cAtEgOrYhOlDeR \ && ball_logthrottle_tHrOtTlE.requestPermission(); \ ) \ for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm( \ ball_logthrottle_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ ball_logthrottle_cAtEgOrYhOlDeR; \ ball_logthrottle_cAtEgOrYhOlDeR = 0) #define BALL_LOGTHROTTLE_STREAM_IMP(SEVERITY, \ MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE) \ for (const BloombergLP::ball::CategoryHolder *ball_logthrottle_cAtEgOrYhOlDeR \ = ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \ ball_logthrottle_cAtEgOrYhOlDeR \ && ball_logthrottle_cAtEgOrYhOlDeR->threshold() >= (SEVERITY) \ && BloombergLP::ball::Log::isCategoryEnabled( \ ball_logthrottle_cAtEgOrYhOlDeR, \ (SEVERITY)); \ ball_logthrottle_cAtEgOrYhOlDeR = 0) \ for (static BloombergLP::bdlmt::Throttle ball_logthrottle_tHrOtTlE = \ BDLMT_THROTTLE_INIT((MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)); \ ball_logthrottle_cAtEgOrYhOlDeR \ && ball_logthrottle_tHrOtTlE.requestPermission(); \ ) \ for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm( \ ball_logthrottle_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ ball_logthrottle_cAtEgOrYhOlDeR; \ ball_logthrottle_cAtEgOrYhOlDeR = 0) #define BALL_LOGTHROTTLEVA_CONST_IMP(SEVERITY, \ MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ do { \ static BloombergLP::bdlmt::Throttle ball_logthrottle_tHrOtTlE = \ BDLMT_THROTTLE_INIT((MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)); \ const BloombergLP::ball::CategoryHolder \ *ball_logthrottle_cAtEgOrYhOlDeR \ = BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>( \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)); \ if (ball_logthrottle_cAtEgOrYhOlDeR && \ ball_logthrottle_tHrOtTlE.requestPermission()) { \ BloombergLP::ball::Log_Formatter ball_logthrottle_fOrMaTtEr( \ ball_logthrottle_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ BloombergLP::ball::Log::format( \ ball_logthrottle_fOrMaTtEr.messageBuffer(), \ ball_logthrottle_fOrMaTtEr.messageBufferLen(), \ __VA_ARGS__); \ } \ } while(0) // ================================== // C++ Stream-Style throttling macros // ================================== #define BALL_LOGTHROTTLE_STREAM(SEVERITY, \ MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_IMP((SEVERITY), \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGTHROTTLE_TRACE( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGTHROTTLE_DEBUG( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGTHROTTLE_INFO( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGTHROTTLE_WARN( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGTHROTTLE_ERROR( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGTHROTTLE_FATAL( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) \ BALL_LOG_OUTPUT_STREAM // ============================= // BLOCK-Style throttling macros // ============================= #define BALL_LOGTHROTTLE_BLOCK(SEVERITY, \ MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_IMP((SEVERITY), \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) #define BALL_LOGTHROTTLE_TRACE_BLOCK( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) #define BALL_LOGTHROTTLE_DEBUG_BLOCK( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) #define BALL_LOGTHROTTLE_INFO_BLOCK( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) #define BALL_LOGTHROTTLE_WARN_BLOCK( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) #define BALL_LOGTHROTTLE_ERROR_BLOCK( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) #define BALL_LOGTHROTTLE_FATAL_BLOCK( \ MAX_SIMULTANEOUS_MESSAGES, NANOSECONDS_PER_MESSAGE) \ BALL_LOGTHROTTLE_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)) // ================================ // 'printf'-style throttling macros // ================================ #define BALL_LOGTHROTTLEVA(SEVERITY, \ MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ do { \ static BloombergLP::bdlmt::Throttle ball_logthrottle_tHrOtTlE = \ BDLMT_THROTTLE_INIT((MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE)); \ const BloombergLP::ball::CategoryHolder *ball_logthrottle_cAtEgOrYhOlDeR \ = ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \ if (ball_logthrottle_cAtEgOrYhOlDeR->threshold() >= (SEVERITY) \ && BloombergLP::ball::Log::isCategoryEnabled( \ ball_logthrottle_cAtEgOrYhOlDeR, \ (SEVERITY)) \ && ball_logthrottle_tHrOtTlE.requestPermission()) { \ BloombergLP::ball::Log_Formatter ball_logthrottle_fOrMaTtEr( \ ball_logthrottle_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ BloombergLP::ball::Log::format( \ ball_logthrottle_fOrMaTtEr.messageBuffer(), \ ball_logthrottle_fOrMaTtEr.messageBufferLen(), \ __VA_ARGS__); \ } \ } while(0) #define BALL_LOGTHROTTLEVA_TRACE(MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ BALL_LOGTHROTTLEVA_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE), \ __VA_ARGS__) #define BALL_LOGTHROTTLEVA_DEBUG(MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ BALL_LOGTHROTTLEVA_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE), \ __VA_ARGS__) #define BALL_LOGTHROTTLEVA_INFO( MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ BALL_LOGTHROTTLEVA_CONST_IMP(BloombergLP::ball::Severity::e_INFO, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE), \ __VA_ARGS__) #define BALL_LOGTHROTTLEVA_WARN( MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ BALL_LOGTHROTTLEVA_CONST_IMP(BloombergLP::ball::Severity::e_WARN, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE), \ __VA_ARGS__) #define BALL_LOGTHROTTLEVA_ERROR(MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ BALL_LOGTHROTTLEVA_CONST_IMP(BloombergLP::ball::Severity::e_ERROR, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE), \ __VA_ARGS__) #define BALL_LOGTHROTTLEVA_FATAL(MAX_SIMULTANEOUS_MESSAGES, \ NANOSECONDS_PER_MESSAGE, \ ...) \ BALL_LOGTHROTTLEVA_CONST_IMP(BloombergLP::ball::Severity::e_FATAL, \ (MAX_SIMULTANEOUS_MESSAGES), \ (NANOSECONDS_PER_MESSAGE), \ __VA_ARGS__) #endif // ---------------------------------------------------------------------------- // Copyright 2018 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------