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 // TODO(b/401363381): Remove this once we have a better way to log to 43 // /dev/hvc2 in isolated storage. 44 // Indicate whether we should force logging to /dev/hvc2 for ICING_LOG. 45 void SetForceDebugLogging(bool force); 46 47 // TODO(b/401363381): Remove this once we have a better way to log to 48 // /dev/hvc2 in isolated storage. 49 // Get whether or not we should force logging to /dev/hvc2 for ICING_LOG. 50 bool GetForceDebugLogging(); 51 52 // A tiny code footprint string stream for assembling log messages. 53 struct LoggingStringStream { LoggingStringStreamLoggingStringStream54 explicit LoggingStringStream(bool should_log) : should_log_(should_log) {} streamLoggingStringStream55 LoggingStringStream& stream() { return *this; } 56 57 std::string message; 58 const bool should_log_; 59 }; 60 61 template <typename T> 62 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 63 const T& entry) { 64 if (stream.should_log_) { 65 stream.message.append(std::to_string(entry)); 66 } 67 return stream; 68 } 69 70 template <typename T> 71 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 72 T* const entry) { 73 if (stream.should_log_) { 74 stream.message.append( 75 std::to_string(reinterpret_cast<const uint64_t>(entry))); 76 } 77 return stream; 78 } 79 80 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 81 const char* message) { 82 if (stream.should_log_) { 83 stream.message.append(message); 84 } 85 return stream; 86 } 87 88 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 89 const std::string& message) { 90 if (stream.should_log_) { 91 stream.message.append(message); 92 } 93 return stream; 94 } 95 96 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 97 std::string_view message) { 98 if (stream.should_log_) { 99 stream.message.append(message); 100 } 101 return stream; 102 } 103 104 template <typename T1, typename T2> 105 inline LoggingStringStream& operator<<(LoggingStringStream& stream, 106 const std::pair<T1, T2>& entry) { 107 if (stream.should_log_) { 108 stream << "(" << entry.first << ", " << entry.second << ")"; 109 } 110 return stream; 111 } 112 113 // The class that does all the work behind our ICING_LOG(severity) macros. Each 114 // ICING_LOG(severity) << obj1 << obj2 << ...; logging statement creates a 115 // LogMessage temporary object containing a stringstream. Each operator<< adds 116 // info to that stringstream and the LogMessage destructor performs the actual 117 // logging. The reason this works is that in C++, "all temporary objects are 118 // destroyed as the last step in evaluating the full-expression that (lexically) 119 // contains the point where they were created." For more info, see 120 // http://en.cppreference.com/w/cpp/language/lifetime. Hence, the destructor is 121 // invoked after the last << from that logging statement. 122 class LogMessage { 123 public: 124 LogMessage(LogSeverity::Code severity, uint16_t verbosity, 125 const char* file_name, int line_number) __attribute__((noinline)); 126 127 ~LogMessage() __attribute__((noinline)); 128 129 // Returns the stream associated with the logger object. stream()130 LoggingStringStream& stream() { return stream_; } 131 132 private: 133 const LogSeverity::Code severity_; 134 const uint16_t verbosity_; 135 const bool should_log_; 136 const bool force_debug_logs_; 137 138 // Stream that "prints" all info into a string (not to a file). We construct 139 // here the entire logging message and next print it in one operation. 140 LoggingStringStream stream_; 141 }; 142 143 inline constexpr char kIcingLoggingTag[] = "AppSearchIcing"; 144 145 // Define consts to make it easier to refer to log severities in code. 146 constexpr ::icing::lib::LogSeverity::Code VERBOSE = 147 ::icing::lib::LogSeverity::VERBOSE; 148 149 constexpr ::icing::lib::LogSeverity::Code DBG = ::icing::lib::LogSeverity::DBG; 150 151 constexpr ::icing::lib::LogSeverity::Code INFO = 152 ::icing::lib::LogSeverity::INFO; 153 154 constexpr ::icing::lib::LogSeverity::Code WARNING = 155 ::icing::lib::LogSeverity::WARNING; 156 157 constexpr ::icing::lib::LogSeverity::Code ERROR = 158 ::icing::lib::LogSeverity::ERROR; 159 160 constexpr ::icing::lib::LogSeverity::Code FATAL = 161 ::icing::lib::LogSeverity::FATAL; 162 163 #define ICING_VLOG(verbose_level) \ 164 ::icing::lib::LogMessage(VERBOSE, verbose_level, __FILE__, __LINE__).stream() 165 166 #define ICING_LOG(severity) \ 167 ::icing::lib::LogMessage(severity, /*verbosity=*/0, __FILE__, __LINE__) \ 168 .stream() 169 170 } // namespace lib 171 } // namespace icing 172 173 #endif // ICING_UTIL_LOGGING_H_ 174