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 <cstdint> 20 #include <string> 21 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( 49 std::to_string(reinterpret_cast<const uint64_t>(entry))); 50 return stream; 51 } 52 53 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 54 const char* message) { 55 stream.message.append(message); 56 return stream; 57 } 58 59 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 60 const std::string& message) { 61 stream.message.append(message); 62 return stream; 63 } 64 65 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 66 const std::string_view message) { 67 stream.message.append(message); 68 return stream; 69 } 70 71 template <typename T1, typename T2> 72 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 73 const std::pair<T1, T2>& entry) { 74 stream << "(" << entry.first << ", " << entry.second << ")"; 75 return stream; 76 } 77 78 // The class that does all the work behind our TC3_LOG(severity) macros. Each 79 // TC3_LOG(severity) << obj1 << obj2 << ...; logging statement creates a 80 // LogMessage temporary object containing a stringstream. Each operator<< adds 81 // info to that stringstream and the LogMessage destructor performs the actual 82 // logging. The reason this works is that in C++, "all temporary objects are 83 // destroyed as the last step in evaluating the full-expression that (lexically) 84 // contains the point where they were created." For more info, see 85 // http://en.cppreference.com/w/cpp/language/lifetime. Hence, the destructor is 86 // invoked after the last << from that logging statement. 87 class LogMessage { 88 public: 89 LogMessage(LogSeverity severity, const char* file_name, 90 int line_number) TC3_ATTRIBUTE_NOINLINE; 91 92 ~LogMessage() TC3_ATTRIBUTE_NOINLINE; 93 94 // Returns the stream associated with the logger object. stream()95 LoggingStringStream& stream() { return stream_; } 96 97 private: 98 const LogSeverity severity_; 99 100 // Stream that "prints" all info into a string (not to a file). We construct 101 // here the entire logging message and next print it in one operation. 102 LoggingStringStream stream_; 103 }; 104 105 // Pseudo-stream that "eats" the tokens <<-pumped into it, without printing 106 // anything. 107 class NullStream { 108 public: NullStream()109 NullStream() {} stream()110 NullStream& stream() { return *this; } 111 }; 112 template <typename T> 113 inline NullStream& operator<<(NullStream& str, const T&) { 114 return str; 115 } 116 117 } // namespace logging 118 } // namespace libtextclassifier3 119 120 #define TC3_LOG(severity) \ 121 ::libtextclassifier3::logging::LogMessage( \ 122 ::libtextclassifier3::logging::severity, __FILE__, __LINE__) \ 123 .stream() 124 125 // If condition x is true, does nothing. Otherwise, crashes the program (like 126 // LOG(FATAL)) with an informative message. Can be continued with extra 127 // messages, via <<, like any logging macro, e.g., 128 // 129 // TC3_CHECK(my_cond) << "I think we hit a problem"; 130 #define TC3_CHECK(x) \ 131 (x) || TC3_LOG(FATAL) << __FILE__ << ":" << __LINE__ << ": check failed: \"" \ 132 << #x << "\" " 133 134 #define TC3_CHECK_EQ(x, y) TC3_CHECK((x) == (y)) 135 #define TC3_CHECK_LT(x, y) TC3_CHECK((x) < (y)) 136 #define TC3_CHECK_GT(x, y) TC3_CHECK((x) > (y)) 137 #define TC3_CHECK_LE(x, y) TC3_CHECK((x) <= (y)) 138 #define TC3_CHECK_GE(x, y) TC3_CHECK((x) >= (y)) 139 #define TC3_CHECK_NE(x, y) TC3_CHECK((x) != (y)) 140 141 #define TC3_NULLSTREAM ::libtextclassifier3::logging::NullStream().stream() 142 143 // Debug checks: a TC3_DCHECK<suffix> macro should behave like TC3_CHECK<suffix> 144 // in debug mode an don't check / don't print anything in non-debug mode. 145 #if defined(NDEBUG) && !defined(TC3_DEBUG_LOGGING) && !defined(TC3_DEBUG_CHECKS) 146 147 #define TC3_DCHECK(x) TC3_NULLSTREAM 148 #define TC3_DCHECK_EQ(x, y) TC3_NULLSTREAM 149 #define TC3_DCHECK_LT(x, y) TC3_NULLSTREAM 150 #define TC3_DCHECK_GT(x, y) TC3_NULLSTREAM 151 #define TC3_DCHECK_LE(x, y) TC3_NULLSTREAM 152 #define TC3_DCHECK_GE(x, y) TC3_NULLSTREAM 153 #define TC3_DCHECK_NE(x, y) TC3_NULLSTREAM 154 155 #else // NDEBUG 156 157 // In debug mode, each TC3_DCHECK<suffix> is equivalent to TC3_CHECK<suffix>, 158 // i.e., a real check that crashes when the condition is not true. 159 #define TC3_DCHECK(x) TC3_CHECK(x) 160 #define TC3_DCHECK_EQ(x, y) TC3_CHECK_EQ(x, y) 161 #define TC3_DCHECK_LT(x, y) TC3_CHECK_LT(x, y) 162 #define TC3_DCHECK_GT(x, y) TC3_CHECK_GT(x, y) 163 #define TC3_DCHECK_LE(x, y) TC3_CHECK_LE(x, y) 164 #define TC3_DCHECK_GE(x, y) TC3_CHECK_GE(x, y) 165 #define TC3_DCHECK_NE(x, y) TC3_CHECK_NE(x, y) 166 167 #endif // NDEBUG 168 169 #ifdef TC3_ENABLE_VLOG 170 #define TC3_VLOG(severity) \ 171 ::libtextclassifier3::logging::LogMessage( \ 172 ::libtextclassifier3::logging::INFO, __FILE__, __LINE__) \ 173 .stream() 174 #else 175 #define TC3_VLOG(severity) TC3_NULLSTREAM 176 #endif 177 178 #endif // ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_LOGGING_H_ 179