// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #pragma once #include #include #include namespace anki { // Forward class File; /// @addtogroup util_other /// @{ /// Logger message type. /// @memberof Logger enum class LoggerMessageType : U8 { kNormal, kVerbose, kError, kWarning, kFatal, kCount }; inline constexpr Array kLoggerMessageTypeText = {"I", "V", "E", "W", "F"}; /// Used as parammeter when emitting the signal. /// @memberof Logger class LoggerMessageInfo { public: const Char* m_file; I32 m_line; const Char* m_func; LoggerMessageType m_type; const Char* m_msg; const Char* m_subsystem; const Char* m_threadName; }; /// The message handler callback. /// @memberof Logger using LoggerMessageHandlerCallback = void (*)(void*, const LoggerMessageInfo& info); /// The logger singleton class. The logger cannot print errors or throw exceptions, it has to recover somehow. It's thread safe. /// To add a new signal: /// @code logger.addMessageHandler((void*)obj, &function) @endcode class Logger : public MakeSingleton { template friend class MakeSingleton; public: /// Add a new message handler. void addMessageHandler(void* data, LoggerMessageHandlerCallback callback); /// Remove a message handler. void removeMessageHandler(void* data, LoggerMessageHandlerCallback callback); /// Add file message handler. void addFileMessageHandler(File* file); /// Send a message. void write(const Char* file, int line, const Char* func, const Char* subsystem, LoggerMessageType type, const Char* threadName, const Char* msg) { // Note: m_verbosityEnabled is not accessed in a thread-safe way. It doesn't really matter though if(type == LoggerMessageType::kVerbose && !m_verbosityEnabled) { return; } writeInternal(file, line, func, subsystem, type, threadName, msg); } /// Send a formated message. ANKI_CHECK_FORMAT(7, 8) void writeFormated(const Char* file, int line, const Char* func, const Char* subsystem, LoggerMessageType type, const Char* threadName, const Char* fmt, ...); /// Enable or disable logger verbosity. void enableVerbosity(Bool enable) { m_verbosityEnabled = enable; } Bool verbosityEnabled() const { return m_verbosityEnabled; } private: class Handler { public: void* m_data = nullptr; LoggerMessageHandlerCallback m_callback = nullptr; }; Mutex m_mutex; ///< For thread safety Array m_handlers; U32 m_handlersCount = 0; Bool m_verbosityEnabled = false; /// Initialize the logger and add the default message handler Logger(); ~Logger(); static void defaultSystemMessageHandler(void*, const LoggerMessageInfo& info); static void fileMessageHandler(void* file, const LoggerMessageInfo& info); void writeInternal(const Char* file, int line, const Char* func, const Char* subsystem, LoggerMessageType type, const Char* threadName, const Char* msg); }; #define ANKI_LOG(subsystem_, t, ...) \ do \ { \ Logger::getSingleton().writeFormated(ANKI_FILE, __LINE__, ANKI_FUNC, subsystem_, LoggerMessageType::t, Thread::getCurrentThreadName(), \ __VA_ARGS__); \ } while(false) /// @} /// @addtogroup util_logging /// @{ /// Log information message. #define ANKI_LOGI(...) ANKI_LOG(nullptr, kNormal, __VA_ARGS__) /// Log verbose information message. #define ANKI_LOGV(...) ANKI_LOG(nullptr, kVerbose, __VA_ARGS__) /// Log warning message. #define ANKI_LOGW(...) ANKI_LOG(nullptr, kWarning, __VA_ARGS__) /// Log error message. #define ANKI_LOGE(...) ANKI_LOG(nullptr, kError, __VA_ARGS__) /// Log fatal message. It will will abort. #define ANKI_LOGF(...) ANKI_LOG(nullptr, kFatal, __VA_ARGS__) /// @} } // end namespace anki