// Copyright (C) 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "icing/util/logging.h" #include #include #include #include "icing/proto/debug.pb.h" #include "icing/util/logging_raw.h" namespace icing { namespace lib { namespace { // Returns pointer to beginning of last /-separated token from file_name. // file_name should be a pointer to a zero-terminated array of chars. // E.g., "foo/bar.cc" -> "bar.cc", "foo/" -> "", "foo" -> "foo". const char *JumpToBasename(const char *file_name) { if (file_name == nullptr) { return nullptr; } // Points to the beginning of the last encountered token. size_t last_token_start = std::string_view(file_name).find_last_of('/'); if (last_token_start == std::string_view::npos) { return file_name; } return file_name + last_token_start + 1; } // Calculate the logging level value based on severity and verbosity. constexpr uint32_t CalculateLoggingLevel(LogSeverity::Code severity, uint16_t verbosity) { uint32_t logging_level = static_cast(severity); logging_level = (logging_level << 16) | verbosity; return logging_level; } #if defined(ICING_DEBUG_LOGGING) #define DEFAULT_LOGGING_LEVEL CalculateLoggingLevel(LogSeverity::VERBOSE, 1) #else #define DEFAULT_LOGGING_LEVEL CalculateLoggingLevel(LogSeverity::INFO, 0) #endif // The current global logging level for Icing, which controls which logs are // printed based on severity and verbosity. // // This needs to be global so that it can be easily accessed from ICING_LOG and // ICING_VLOG macros spread throughout the entire code base. // // The first 16 bits represent the minimal log severity. // The last 16 bits represent the current verbosity. std::atomic global_logging_level = DEFAULT_LOGGING_LEVEL; } // namespace // Whether we should log according to the current logging level. bool ShouldLog(LogSeverity::Code severity, int16_t verbosity) { if (verbosity < 0) { return false; } // Using the relaxed order for better performance because we only need to // guarantee the atomicity for this specific statement, without the need to // worry about reordering. uint32_t curr_logging_level = global_logging_level.load(std::memory_order_relaxed); // If severity is less than the the threshold set. if (static_cast(severity) < (curr_logging_level >> 16)) { return false; } if (severity == LogSeverity::VERBOSE) { // return whether the verbosity is within the current verbose level set. return verbosity <= (curr_logging_level & 0xffff); } return true; } bool SetLoggingLevel(LogSeverity::Code severity, int16_t verbosity) { if (verbosity < 0) { return false; } if (severity > LogSeverity::VERBOSE && verbosity > 0) { return false; } // Using the relaxed order for better performance because we only need to // guarantee the atomicity for this specific statement, without the need to // worry about reordering. global_logging_level.store(CalculateLoggingLevel(severity, verbosity), std::memory_order_relaxed); return true; } LogMessage::LogMessage(LogSeverity::Code severity, uint16_t verbosity, const char *file_name, int line_number) : severity_(severity), verbosity_(verbosity), should_log_(ShouldLog(severity_, verbosity_)), stream_(should_log_) { if (should_log_) { stream_ << JumpToBasename(file_name) << ":" << line_number << ": "; } } LogMessage::~LogMessage() { if (should_log_) { LowLevelLogging(severity_, kIcingLoggingTag, stream_.message); } if (severity_ == LogSeverity::FATAL) { std::terminate(); // Will print a stacktrace (stdout or logcat). } } } // namespace lib } // namespace icing