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