/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * 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. */ #ifndef LIBPANDABASE_UTILS_LOGGER_H #define LIBPANDABASE_UTILS_LOGGER_H #include "macros.h" #include "os/error.h" #include "os/mutex.h" #include "os/thread.h" #include <array> #include <cstdint> #include <bitset> #include <fstream> #include <map> #include <string> #include <sstream> #include <atomic> #ifdef ENABLE_HILOG #include <hilog/log.h> #endif namespace panda { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define LOG_COMPONENT_ELEM(D, NAME, STR) D(NAME, NAME, STR) using FUNC_MOBILE_LOG_PRINT = int (*)(int, int, const char *, const char *, const char *); constexpr int LOG_ID_MAIN = 0; extern FUNC_MOBILE_LOG_PRINT mlog_buf_print; namespace base_options { class Options; } // namespace base_options class Logger { public: #include <logger_enum_gen.h> using ComponentMask = std::bitset<Component::LAST>; enum PandaLog2MobileLog : int { UNKNOWN = 0, DEFAULT, VERBOSE, DEBUG, INFO, WARN, ERROR, FATAL, SILENT, }; class Buffer { public: constexpr static size_t BUFFER_SIZE = 4096; public: Buffer() : buffer {} {} public: const char *data() const noexcept { return buffer.data(); } char *data() noexcept { return buffer.data(); } public: constexpr size_t size() const noexcept { return BUFFER_SIZE; } public: // always overwrites buffer data Buffer &printf(const char *format, ...); public: friend std::ostream &operator<<(std::ostream &os, const Buffer &b) { return os << b.data(); } private: std::array<char, BUFFER_SIZE> buffer; }; class Message { public: Message(Level level, Component component, bool print_system_error) : level_(level), component_(component), print_system_error_(print_system_error) { #ifndef NDEBUG Logger::LogNestingInc(); #endif } ~Message(); std::ostream &GetStream() { return stream_; } private: Level level_; Component component_; bool print_system_error_; std::ostringstream stream_; NO_COPY_SEMANTIC(Message); NO_MOVE_SEMANTIC(Message); }; static void Initialize(const base_options::Options &options); static void InitializeFileLogging(const std::string &log_file, Level level, ComponentMask component_mask, bool is_fast_logging = false); #ifdef ENABLE_HILOG static void InitializeHiLogging(Level level, ComponentMask component_mask); #endif static void InitializeStdLogging(Level level, ComponentMask component_mask); static void InitializeDummyLogging(Level level = Level::DEBUG, ComponentMask component_mask = 0); static void Destroy(); static void SetMobileLogPrintEntryPointByPtr(void *mlog_buf_print_ptr) { mlog_buf_print = reinterpret_cast<FUNC_MOBILE_LOG_PRINT>(mlog_buf_print_ptr); } static uint32_t GetLevelNumber(Logger::Level level); void WriteMobileLog(Level level, const char *component, const char *message) { if (mlog_buf_print == nullptr || !is_mlog_opened_) { return; } PandaLog2MobileLog mlog_level = PandaLog2MobileLog::UNKNOWN; switch (level) { case Level::DEBUG: mlog_level = PandaLog2MobileLog::DEBUG; break; case Level::INFO: mlog_level = PandaLog2MobileLog::INFO; break; case Level::ERROR: mlog_level = PandaLog2MobileLog::ERROR; break; case Level::FATAL: mlog_level = PandaLog2MobileLog::FATAL; break; case Level::WARNING: mlog_level = PandaLog2MobileLog::WARN; break; default: UNREACHABLE(); } std::string panda_component = "Ark " + std::string(component); mlog_buf_print(LOG_ID_MAIN, mlog_level, panda_component.c_str(), "%s", message); } static bool IsLoggingOn(Level level, Component component) { return IsInitialized() && level <= logger->level_ && (logger->component_mask_.test(component) || level == Level::FATAL); } static bool IsLoggingOnOrAbort(Level level, Component component) { if (IsLoggingOn(level, component)) { return true; } if (level == Level::FATAL) { std::abort(); } return false; } #ifndef NDEBUG static void LogNestingInc(); static void LogNestingDec(); static bool IsMessageSuppressed([[maybe_unused]] Level level, [[maybe_unused]] Component component); #endif static void Log(Level level, Component component, const std::string &str); static void Sync() { if (IsInitialized()) { logger->SyncOutputResource(); } } static Level LevelFromString(std::string_view s); static ComponentMask ComponentMaskFromString(std::string_view s); static std::string StringfromDfxComponent(LogDfxComponent dfx_component); static void SetLevel(Level level) { ASSERT(IsInitialized()); logger->level_ = level; } static Level GetLevel() { ASSERT(IsInitialized()); return logger->level_; } static void EnableComponent(Component component) { ASSERT(IsInitialized()); logger->component_mask_.set(component); } static void EnableComponent(ComponentMask component) { ASSERT(IsInitialized()); logger->component_mask_ |= component; } static void DisableComponent(Component component) { ASSERT(IsInitialized()); logger->component_mask_.reset(component); } static void ResetComponentMask() { ASSERT(IsInitialized()); logger->component_mask_.reset(); } static void SetMobileLogOpenFlag(bool is_mlog_opened) { ASSERT(IsInitialized()); logger->is_mlog_opened_ = is_mlog_opened; } static bool IsInLevelList(std::string_view s); static bool IsInComponentList(std::string_view s); static void ProcessLogLevelFromString(std::string_view s); static void ProcessLogComponentsFromString(std::string_view s); static bool IsInitialized() { return logger != nullptr; } protected: Logger(Level level, ComponentMask component_mask) : level_(level), component_mask_(component_mask) #ifndef NDEBUG , // Means all the LOGs are allowed just as usual nested_allowed_level_(Level::LAST) #endif { } Logger(Level level, ComponentMask component_mask, [[maybe_unused]] Level nested_allowed_level) : level_(level), component_mask_(component_mask) #ifndef NDEBUG , nested_allowed_level_(nested_allowed_level) #endif { } virtual void LogLineInternal(Level level, Component component, const std::string &str) = 0; /** * Flushes all the output buffers of LogLineInternal to the output resources * Sometimes nothinig shall be done, if LogLineInternal flushes everything by itself statelessl */ virtual void SyncOutputResource() = 0; virtual ~Logger() = default; static Logger *logger; static os::memory::Mutex mutex; static thread_local int nesting; private: Level level_; ComponentMask component_mask_; #ifndef NDEBUG // These are utilized by Fast* logger types. // For every thread, we trace events of staring shifting to a log (<<) and finishing doing it, // incrementing a log invocation depth variable bound to a thread, or decrementing it correspondingly. // Such variables we're doing as thread-local. // All the LOGs with levels < nested_allowed_level_ are only allowed to have depth of log == 1 Level nested_allowed_level_; // Log level to suppress LOG triggering within << to another LOG #endif bool is_mlog_opened_ {true}; NO_COPY_SEMANTIC(Logger); NO_MOVE_SEMANTIC(Logger); }; static Logger::ComponentMask LoggerComponentMaskAll = ~Logger::ComponentMask(); class FileLogger : public Logger { protected: FileLogger(std::ofstream &&stream, Level level, ComponentMask component_mask) : Logger(level, component_mask), stream_(std::forward<std::ofstream>(stream)) { } void LogLineInternal(Level level, Component component, const std::string &str) override; void SyncOutputResource() override {} ~FileLogger() override = default; NO_COPY_SEMANTIC(FileLogger); NO_MOVE_SEMANTIC(FileLogger); private: std::ofstream stream_; friend Logger; }; class FastFileLogger : public Logger { protected: // Uses advanced Logger constructor, so we tell to suppress all nested messages below WARNING severity FastFileLogger(std::ofstream &&stream, Level level, ComponentMask component_mask) : Logger(level, component_mask, Logger::Level::WARNING), stream_(std::forward<std::ofstream>(stream)) { } void LogLineInternal(Level level, Component component, const std::string &str) override; void SyncOutputResource() override; ~FastFileLogger() override = default; NO_COPY_SEMANTIC(FastFileLogger); NO_MOVE_SEMANTIC(FastFileLogger); private: std::ofstream stream_; friend Logger; }; #ifdef ENABLE_HILOG class HiLogger : public Logger { protected: HiLogger(Level level, ComponentMask component_mask) : Logger(level, component_mask) {} void LogLineInternal(Level level, Component component, const std::string &str) override; void SyncOutputResource() override {} ~HiLogger() override = default; NO_COPY_SEMANTIC(HiLogger); NO_MOVE_SEMANTIC(HiLogger); private: std::ostringstream stream_; constexpr static unsigned int ARK_DOMAIN = 0xD003F00; constexpr static auto TAG = "ArkCompiler"; constexpr static OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, ARK_DOMAIN, TAG}; friend Logger; }; #endif class StderrLogger : public Logger { private: StderrLogger(Level level, ComponentMask component_mask) : Logger(level, component_mask) {} void LogLineInternal(Level level, Component component, const std::string &str) override; void SyncOutputResource() override {} friend Logger; ~StderrLogger() override = default; NO_COPY_SEMANTIC(StderrLogger); NO_MOVE_SEMANTIC(StderrLogger); }; class DummyLogger : public Logger { private: DummyLogger(Level level, ComponentMask component_mask) : Logger(level, component_mask) {} void LogLineInternal([[maybe_unused]] Level level, [[maybe_unused]] Component component, [[maybe_unused]] const std::string &str) override { } void SyncOutputResource() override {} friend Logger; ~DummyLogger() override = default; NO_COPY_SEMANTIC(DummyLogger); NO_MOVE_SEMANTIC(DummyLogger); }; class DummyStream { public: explicit operator bool() const { return true; } }; template <class T> DummyStream operator<<(DummyStream s, [[maybe_unused]] const T &v) { return s; } class LogOnceHelper { public: bool IsFirstCall() { flag_ >>= 1U; return flag_ != 0; } private: uint8_t flag_ = 0x03; }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define LOG_ONCE_HELPER() static LogOnceHelper MERGE_WORDS(log_once_helper, __LINE__); // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define LOG_ONCE(level, component) \ LOG_ONCE_HELPER() \ MERGE_WORDS(log_once_helper, __LINE__).IsFirstCall() && LOG(level, component) #ifndef NDEBUG // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_SUPPRESSION_CHECK(level, component) \ !panda::Logger::IsMessageSuppressed(panda::Logger::Level::level, panda::Logger::Component::component) #else // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_SUPPRESSION_CHECK(level, component) true #endif // Explicit namespace is specified to allow using the logger out of panda namespace. // For example, in the main function. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG(level, component, p) \ panda::Logger::IsLoggingOnOrAbort(panda::Logger::Level::level, panda::Logger::Component::component) && \ _LOG_SUPPRESSION_CHECK(level, component) && \ panda::Logger::Message(panda::Logger::Level::level, panda::Logger::Component::component, p).GetStream() // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define LOG(level, component) _LOG_##level(component, false) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GET_LOG_STREAM(level, component) \ panda::Logger::Message(panda::Logger::Level::level, panda::Logger::Component::component, false).GetStream() // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define PLOG(level, component) _LOG_##level(component, true) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define LOG_IF(cond, level, component) (cond) && LOG(level, component) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define PLOG_IF(cond, level, component) (cond) && PLOG(level, component) #ifndef NDEBUG // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_DEBUG(component, p) _LOG(DEBUG, component, p) #else // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_DEBUG(component, p) false && panda::DummyStream() #endif // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_INFO(component, p) _LOG(INFO, component, p) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_WARNING(component, p) _LOG(WARNING, component, p) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_ERROR(component, p) _LOG(ERROR, component, p) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _LOG_FATAL(component, p) _LOG(FATAL, component, p) } // namespace panda #endif // LIBPANDABASE_UTILS_LOGGER_H