1. log output macro
Here we take the simplest day to output as an example:
LOG(WARNING) << "This is a warning message";
Here log is a macro, and its definition is as follows (logging.h line 487):
#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
Here, according to the different Severity in LOG macro, there are four macroes separately. Among them, the Severity has four predefined meaning (log_severity.h line 51-59), which represent different levels of log outputs, including INFO and Warning , ERROR, FATAL, take warning as an example, log (warning) is extended to compact_google_log_warning.stream (). The compact_google_log_ warning is another macro (logging.h line 391):
You can basically see the doorway at
1 #if GOOGLE_STRIP_LOG <= 1
2 #define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \
3 __FILE__, __LINE__, google::GLOG_WARNING)
4 #define LOG_TO_STRING_WARNING(message) google::LogMessage( \
5 __FILE__, __LINE__, google::GLOG_WARNING, message)
6 #else
7 #define COMPACT_GOOGLE_LOG_WARNING google::NullStream()
8 #define LOG_TO_STRING_WARNING(message) google::NullStream()
9 #endif
here. Google :: logMessage and Google :: NullStream are class. According to the different definitions of Google_Strip_log, compact_google_log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_Log_LOG. It is an output without output (just re -loads Operator <<, but it does not actually output any information) to achieve some level log information that is not explicitly output). Here is the main look at logMessage.
At this time, according to the file name, line number, log level, construct a logMessage class (logging.cc line 1153):
LogMessage::LogMessage(const char* file, int line, LogSeverity severity) : allocated_(NULL) {
Init(file, line, severity, &LogMessage::SendToLog);
}
Logmessage has a lot of heavy -duty structures, which will not be listed one by one here. Pay attention to the initialization function in the structure, in addition to the file name, line number, log level, there is an additional parameter, the init statement is as follows:
void Init(const char* file, int line, LogSeverity severity, void (LogMessage::*send_method)());
that is, the last parameter is a function pointer, which can be configured to set up real log output, such as output to files, console, etc., and may even be configured to output to the remote network end. Intit is used to initialize the flow buffer area input, initialize logging time, format, determine the print log file name, and so on.
At this time, the object of a complete logMessage was created and initialized. Back to the LOG (Severity) macro -oriented place. At this time, the log macro can be expressed as the following definition:
#define LOG(severity) google::LogMessage().stream()
, also the return value of the member function of the Google :: Logmessage class, the return value of the member function of the Google :: logMessage class.
std::ostream& LogMessage::stream() {
return data_->stream_;
}
data _-> stream_ is a logstream object, which is defined as follows:
1 class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream {
2 public:
3 LogStream(char *buf, int len, int ctr);
4 //................................
5 private:
6 base_logging::LogStreamBuf streambuf_;
7 int ctr_; // Counter hack (for the LOG_EVERY_X() macro)
8 LogStream *self_; // Consistency check hack
9 };
The Google :: nullstream mentioned above is inherited from logstream, so it is also a std :: OSTREAM object.
to this log output statement,
LOG(WARNING) << "This is a warning message";
can be expressed as:
google:: LogStream() << "This is a warning message";
Here you will find that this is the same as the COUT output we are familiar with:
std::cout << "This is a warning message";
A Google :: Logstream object and STD :: COUT are all std :: OSTREAM objects.
From the above, it can also be seen that every time a log message is output, you must create a Google :: logMessage object. After each output, the LOGMESSAGE object is released. The following code in its destructor:
1 LogMessage::~LogMessage() {
2 Flush();
3 delete allocated_;
4 }
FLUSH member function is a refresh log cache area, which is equivalent to the Flush or FFLush operated by C ++. In addition, pay attention to the following code in Flush implementation:
1 //......
2 {
3 MutexLock l(&log_mutex);
4 (this->*(data_->send_method_))();
5 ++num_messages_[static_cast<int>(data_->severity_)];
6 }
7 //......
This is to ensure that multiple logs are exported to the same medium at the same time. Note that there are {} surrounds before and after the use of locks. Oh, I actually use this usage occasionally. The advantage is to create a smaller object of the scope in a relatively large statement block, so that the object can be released early and avoid using the same scope as the entire statement block. For example, in the above code, a smaller scope is used when locking, and the lock will be released immediately after the end of the scope is over, instead of being released until the Flush function returns, it will further increase the response time (in fact, there is also here that there are also here. Other methods, such as the article I wrote before:Do {…} While (0) wonderful use)。
In this way, the log output is complete. Other macro -like macro, vLOG, VLOG_IF (output with conditional detection) are expanded according to this idea, and no longer introduce them one by one.
2. Check_xx macro
I personally feel that this kind of check_xx macro is more obscure than the LOG macro above the above. Of course, the design is very clever. It is worth learning. I try to make an analysis.
In the test project’s logging_unittest.cc file LINE 535’s testcheck () function, there are the following code:
1 CHECK_NE(1, 2);
2 CHECK_GE(1, 1);
3 CHECK_GE(2, 1);
4 CHECK_LE(1, 1);
5 CHECK_LE(1, 2);
These macros can also guess what it is used, their definitions are as follows:
1 #define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2)
2 #define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2)
3 #define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2)
4 #define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2)
5 #define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2)
6 #define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2)
Among them, check_op macro is as follows:
#define CHECK_OP(name, op, val1, val2) \
CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal)
and check_op_log macro definition as follows:
1 typedef std::string _Check_string;
2 #define CHECK_OP_LOG(name, op, val1, val2, log) \
3 while (google::_Check_string* _result = \
4 google::Check##name##Impl( \
5 google::GetReferenceableValue(val1), \
6 google::GetReferenceableValue(val2), \
7 #val1 " " #op " " #val2)) \
8 log(__FILE__, __LINE__, \
9 google::CheckOpString(_result)).stream()
Next, we take check_le (1, 2); for example, gradually expand it:
1 CHECK_LE(1, 2)
2 ------> CHECK_OP(_LE, <=, 1, 2)
3 ------> CHECK_OP_LOG(_LE, <=, 1, 2, google::LogMessageFatal)
4 ------> #define CHECK_OP_LOG(_LE, <=, 1, 2, google::LogMessageFatal) \
5 while (std::string * _result = \
6 google::Check_LEImpl( \
7 1, \
8 2, \
9 "1 <= 2")) \
10 log(__FILE__, __LINE__, \
11 google::CheckOpString(_result)).stream()
Among them, Google :: Check_leimPl is also implemented through macro first. This macro is define_check_op_impl (check_le, <=):
After the development of
1 #define DEFINE_CHECK_OP_IMPL(name, op) \
2 template <typename T1, typename T2> \
3 inline std::string* name##Impl(const T1& v1, const T2& v2, \
4 const char* exprtext) { \
5 if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \
6 else return MakeCheckOpString(v1, v2, exprtext); \
7 } \
8 inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \
9 return name##Impl<int, int>(v1, v2, exprtext); \
10 }
, the Google :: Check_leimPl function is implemented (other is similar to this, only “<=” as an example here):
CHECK_LE(1, 2) ------>
while (std::string * _result = google::Check_LEImpl(1, 2, "1 <= 2"))
log(__FILE__, __LINE__,google::CheckOpString(_result)).stream()
Among them, Google :: Check_leimPl also called the cHECK_LEIMPL implemented by the template, which determines the either by the two parameters V1, V2, and operator OP. Output, otherwise the log information is output.
Among them, macro_predict_true, internal functions GetreFeferenceableableValue, function makeCheckopString, function Checkopstring, structure Checkopstring is relatively simple, so no longer talk about it.
At this point, the check_le (1, 2) extension is completed. If the detection is TRUE, it returns NULL, otherwise it will return a string pointer with a clear prompt message, and output the information, and then the program is down.
For example, if you verify check_le (1, 0), because for False, the log output is triggered:
F0503 17:39:09.961318 4232 logging_unittest.cc:203] Check failed: 1 <= 0 (1 vs. 0)
*** Check failure stack trace: ***
Then the program is abnormal exit.
3. Use Do-WHILE (0) in Hongzhong
For example, there is the following macro definition in the 817 line of Logging.h:
1 #define CHECK_DOUBLE_EQ(val1, val2) \
2 do { \
3 CHECK_LE((val1), (val2)+0.000000000000001L); \
4 CHECK_GE((val1), (val2)-0.000000000000001L); \
5 } while (0)
Here we mainly follow the use of Do-WHILE (0). Coincidentally, I also wrote an article to introduce the wonderful use of Do-WHILE (0). Please see:do {…} While (0) wonderful use, no more words here.
4. Use macro for global initialization
Please see the macro usage below:
REGISTER_MODULE_INITIALIZER(utilities, yUserNameInitializer());
register_module_initializer in Googleinit.h 44 line definition:
1 #define REGISTER_MODULE_INITIALIZER(name, body) \
2 namespace { \
3 static void google_init_module_##name () { body; } \
4 GoogleInitializer google_initializer_module_##name(#name, \
5 google_init_module_##name); \
6 }
Among them, the implementation of Googleinitializer is as follows:
1 class GoogleInitializer {
2 public:
3 typedef void (*void_function)(void);
4 GoogleInitializer(const char*, void_function f) {
5 f();
6 }
7 };
This is relatively simple. In fact, it is to do some work such as registration and global initialization. The corresponding macro can be set to clean up when the program exits.