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