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_UTIL_LOGGING_H_ 16 #define ICING_UTIL_LOGGING_H_ 17 18 #include <atomic> 19 #include <cstdint> 20 #include <string> 21 22 #include "icing/proto/debug.pb.h" 23 24 // This header provides base/logging.h style macros, ICING_LOG and ICING_VLOG, 25 // for logging in various platforms. The macros use __android_log_write on 26 // Android, and log to stdout/stderr on others. It also provides a function 27 // SetLoggingLevel to control the log severity level for ICING_LOG and verbosity 28 // for ICING_VLOG. 29 namespace icing { 30 namespace lib { 31 32 // Whether we should log according to the current logging level. 33 // The function will always return false when verbosity is negative. 34 bool ShouldLog(LogSeverity::Code severity, int16_t verbosity = 0); 35 36 // Set the minimal logging severity to be enabled, and the verbose level to see 37 // from the logs. 38 // Return false if severity is set higher than VERBOSE but verbosity is not 0. 39 // The function will always return false when verbosity is negative. 40 bool SetLoggingLevel(LogSeverity::Code severity, int16_t verbosity = 0); 41 42 // A tiny code footprint string stream for assembling log messages. 43 struct LoggingStringStream { LoggingStringStreamLoggingStringStream44 explicit LoggingStringStream(bool should_log) : should_log_(should_log) {} streamLoggingStringStream45 LoggingStringStream& stream() { return *this; } 46 47 std::string message; 48 const bool should_log_; 49 }; 50 51 template <typename T> 52 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 53 const T& entry) { 54 if (stream.should_log_) { 55 stream.message.append(std::to_string(entry)); 56 } 57 return stream; 58 } 59 60 template <typename T> 61 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 62 T* const entry) { 63 if (stream.should_log_) { 64 stream.message.append( 65 std::to_string(reinterpret_cast<const uint64_t>(entry))); 66 } 67 return stream; 68 } 69 70 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 71 const char* message) { 72 if (stream.should_log_) { 73 stream.message.append(message); 74 } 75 return stream; 76 } 77 78 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 79 const std::string& message) { 80 if (stream.should_log_) { 81 stream.message.append(message); 82 } 83 return stream; 84 } 85 86 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 87 std::string_view message) { 88 if (stream.should_log_) { 89 stream.message.append(message); 90 } 91 return stream; 92 } 93 94 template <typename T1, typename T2> 95 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 96 const std::pair<T1, T2>& entry) { 97 if (stream.should_log_) { 98 stream << "(" << entry.first << ", " << entry.second << ")"; 99 } 100 return stream; 101 } 102 103 // The class that does all the work behind our ICING_LOG(severity) macros. Each 104 // ICING_LOG(severity) << obj1 << obj2 << ...; logging statement creates a 105 // LogMessage temporary object containing a stringstream. Each operator<< adds 106 // info to that stringstream and the LogMessage destructor performs the actual 107 // logging. The reason this works is that in C++, "all temporary objects are 108 // destroyed as the last step in evaluating the full-expression that (lexically) 109 // contains the point where they were created." For more info, see 110 // http://en.cppreference.com/w/cpp/language/lifetime. Hence, the destructor is 111 // invoked after the last << from that logging statement. 112 class LogMessage { 113 public: 114 LogMessage(LogSeverity::Code severity, uint16_t verbosity, 115 const char* file_name, int line_number) __attribute__((noinline)); 116 117 ~LogMessage() __attribute__((noinline)); 118 119 // Returns the stream associated with the logger object. stream()120 LoggingStringStream& stream() { return stream_; } 121 122 private: 123 const LogSeverity::Code severity_; 124 const uint16_t verbosity_; 125 const bool should_log_; 126 127 // Stream that "prints" all info into a string (not to a file). We construct 128 // here the entire logging message and next print it in one operation. 129 LoggingStringStream stream_; 130 }; 131 132 inline constexpr char kIcingLoggingTag[] = "AppSearchIcing"; 133 134 // Define consts to make it easier to refer to log severities in code. 135 constexpr ::icing::lib::LogSeverity::Code VERBOSE = 136 ::icing::lib::LogSeverity::VERBOSE; 137 138 constexpr ::icing::lib::LogSeverity::Code DBG = ::icing::lib::LogSeverity::DBG; 139 140 constexpr ::icing::lib::LogSeverity::Code INFO = 141 ::icing::lib::LogSeverity::INFO; 142 143 constexpr ::icing::lib::LogSeverity::Code WARNING = 144 ::icing::lib::LogSeverity::WARNING; 145 146 constexpr ::icing::lib::LogSeverity::Code ERROR = 147 ::icing::lib::LogSeverity::ERROR; 148 149 constexpr ::icing::lib::LogSeverity::Code FATAL = 150 ::icing::lib::LogSeverity::FATAL; 151 152 #define ICING_VLOG(verbose_level) \ 153 ::icing::lib::LogMessage(VERBOSE, verbose_level, __FILE__, __LINE__).stream() 154 155 #define ICING_LOG(severity) \ 156 ::icing::lib::LogMessage(severity, /*verbosity=*/0, __FILE__, __LINE__) \ 157 .stream() 158 159 } // namespace lib 160 } // namespace icing 161 162 #endif // ICING_UTIL_LOGGING_H_ 163