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