1 // Copyright (C) 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_LOGGING_H_ 16 #define ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_LOGGING_H_ 17 18 #include <cassert> 19 #include <string> 20 21 #include "icing/text_classifier/lib3/utils/base/integral_types.h" 22 #include "icing/text_classifier/lib3/utils/base/logging_levels.h" 23 #include "icing/text_classifier/lib3/utils/base/port.h" 24 25 namespace libtextclassifier3 { 26 namespace logging { 27 28 // A tiny code footprint string stream for assembling log messages. 29 struct LoggingStringStream { LoggingStringStreamLoggingStringStream30 LoggingStringStream() {} streamLoggingStringStream31 LoggingStringStream& stream() { return *this; } 32 // Needed for invocation in TC3_CHECK macro. 33 explicit operator bool() const { return true; } 34 35 std::string message; 36 }; 37 38 template <typename T> 39 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 40 const T& entry) { 41 stream.message.append(std::to_string(entry)); 42 return stream; 43 } 44 45 template <typename T> 46 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 47 T* const entry) { 48 stream.message.append(std::to_string(reinterpret_cast<const uint64>(entry))); 49 return stream; 50 } 51 52 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 53 const char* message) { 54 stream.message.append(message); 55 return stream; 56 } 57 58 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 59 const std::string& message) { 60 stream.message.append(message); 61 return stream; 62 } 63 64 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 65 const std::string_view message) { 66 stream.message.append(message); 67 return stream; 68 } 69 70 template <typename T1, typename T2> 71 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 72 const std::pair<T1, T2>& entry) { 73 stream << "(" << entry.first << ", " << entry.second << ")"; 74 return stream; 75 } 76 77 // The class that does all the work behind our TC3_LOG(severity) macros. Each 78 // TC3_LOG(severity) << obj1 << obj2 << ...; logging statement creates a 79 // LogMessage temporary object containing a stringstream. Each operator<< adds 80 // info to that stringstream and the LogMessage destructor performs the actual 81 // logging. The reason this works is that in C++, "all temporary objects are 82 // destroyed as the last step in evaluating the full-expression that (lexically) 83 // contains the point where they were created." For more info, see 84 // http://en.cppreference.com/w/cpp/language/lifetime. Hence, the destructor is 85 // invoked after the last << from that logging statement. 86 class LogMessage { 87 public: 88 LogMessage(LogSeverity severity, const char* file_name, 89 int line_number) TC3_ATTRIBUTE_NOINLINE; 90 91 ~LogMessage() TC3_ATTRIBUTE_NOINLINE; 92 93 // Returns the stream associated with the logger object. stream()94 LoggingStringStream& stream() { return stream_; } 95 96 private: 97 const LogSeverity severity_; 98 99 // Stream that "prints" all info into a string (not to a file). We construct 100 // here the entire logging message and next print it in one operation. 101 LoggingStringStream stream_; 102 }; 103 104 // Pseudo-stream that "eats" the tokens <<-pumped into it, without printing 105 // anything. 106 class NullStream { 107 public: NullStream()108 NullStream() {} stream()109 NullStream& stream() { return *this; } 110 }; 111 template <typename T> 112 inline NullStream& operator<<(NullStream& str, const T&) { 113 return str; 114 } 115 116 } // namespace logging 117 } // namespace libtextclassifier3 118 119 #define TC3_LOG(severity) \ 120 ::libtextclassifier3::logging::LogMessage( \ 121 ::libtextclassifier3::logging::severity, __FILE__, __LINE__) \ 122 .stream() 123 124 // If condition x is true, does nothing. Otherwise, crashes the program (like 125 // LOG(FATAL)) with an informative message. Can be continued with extra 126 // messages, via <<, like any logging macro, e.g., 127 // 128 // TC3_CHECK(my_cond) << "I think we hit a problem"; 129 #define TC3_CHECK(x) \ 130 (x) || TC3_LOG(FATAL) << __FILE__ << ":" << __LINE__ << ": check failed: \"" \ 131 << #x << "\" " 132 133 #define TC3_CHECK_EQ(x, y) TC3_CHECK((x) == (y)) 134 #define TC3_CHECK_LT(x, y) TC3_CHECK((x) < (y)) 135 #define TC3_CHECK_GT(x, y) TC3_CHECK((x) > (y)) 136 #define TC3_CHECK_LE(x, y) TC3_CHECK((x) <= (y)) 137 #define TC3_CHECK_GE(x, y) TC3_CHECK((x) >= (y)) 138 #define TC3_CHECK_NE(x, y) TC3_CHECK((x) != (y)) 139 140 #define TC3_NULLSTREAM ::libtextclassifier3::logging::NullStream().stream() 141 142 // Debug checks: a TC3_DCHECK<suffix> macro should behave like TC3_CHECK<suffix> 143 // in debug mode an don't check / don't print anything in non-debug mode. 144 #if defined(NDEBUG) && !defined(TC3_DEBUG_LOGGING) && !defined(TC3_DEBUG_CHECKS) 145 146 #define TC3_DCHECK(x) TC3_NULLSTREAM 147 #define TC3_DCHECK_EQ(x, y) TC3_NULLSTREAM 148 #define TC3_DCHECK_LT(x, y) TC3_NULLSTREAM 149 #define TC3_DCHECK_GT(x, y) TC3_NULLSTREAM 150 #define TC3_DCHECK_LE(x, y) TC3_NULLSTREAM 151 #define TC3_DCHECK_GE(x, y) TC3_NULLSTREAM 152 #define TC3_DCHECK_NE(x, y) TC3_NULLSTREAM 153 154 #else // NDEBUG 155 156 // In debug mode, each TC3_DCHECK<suffix> is equivalent to TC3_CHECK<suffix>, 157 // i.e., a real check that crashes when the condition is not true. 158 #define TC3_DCHECK(x) TC3_CHECK(x) 159 #define TC3_DCHECK_EQ(x, y) TC3_CHECK_EQ(x, y) 160 #define TC3_DCHECK_LT(x, y) TC3_CHECK_LT(x, y) 161 #define TC3_DCHECK_GT(x, y) TC3_CHECK_GT(x, y) 162 #define TC3_DCHECK_LE(x, y) TC3_CHECK_LE(x, y) 163 #define TC3_DCHECK_GE(x, y) TC3_CHECK_GE(x, y) 164 #define TC3_DCHECK_NE(x, y) TC3_CHECK_NE(x, y) 165 166 #endif // NDEBUG 167 168 #ifdef TC3_ENABLE_VLOG 169 #define TC3_VLOG(severity) \ 170 ::libtextclassifier3::logging::LogMessage( \ 171 ::libtextclassifier3::logging::INFO, __FILE__, __LINE__) \ 172 .stream() 173 #else 174 #define TC3_VLOG(severity) TC3_NULLSTREAM 175 #endif 176 177 #endif // ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_LOGGING_H_ 178