Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • fb985066c4eb89489d2deaa9d5cea18129c7a596
  • master par défaut
  • script
  • new-devel
  • devel
  • timingView-edit
  • fix-mpv
7 résultats

Log.hh

Blame
  • Log.hh 9,44 Kio
    #pragma once
    
    #include "Utils.hh"
    
    // Create a logger with a category
    #define VIVY_GET_LOGGER(sink, cat)                (sink)->newLogger(std::string_view{ #cat })
    #define VIVY_GET_LOGGER_BY_STORED_NAME(sink, cat) (sink)->newLogger(std::string_view{ cat })
    
    // Log something in a logger
    #define VIVY_LOG_WITH_LEVEL(log, level) (log)->logEvent(__FILE__, __func__, __LINE__, level)
    #define VIVY_LOG_WARN(log)              VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Warning)
    #define VIVY_LOG_DEBUG(log)             VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Debug)
    #define VIVY_LOG_INFO(log)              VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Info)
    #define VIVY_LOG_ERR(log)               VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Error)
    #define VIVY_LOG_FATAL(log)             VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Critical)
    
    // Declare a sink, with the utility function/method `flushLogSink`. This is
    // intended to be used in an object to not polluate namespaces.
    #define VIVY_DCL_LOG_SINK(sink)                          \
        std::shared_ptr<LogSink> sink{ LogSink::newSink() }; \
        void flushLogSink() const noexcept { (sink)->flush(); }
    
    // Declare a dispatch for a sink with no arguments in the constructor.
    #define VIVY_DCL_LOG_DISPATCH(sink, name, dispatch) \
        std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>() };
    
    // Declare a dispatch for a sink with arguments in the constructor.
    #define VIVY_DCL_LOG_DISPATCH_WITH(sink, name, dispatch, ...) \
        std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>(__VA_ARGS__) };
    
    #define VIVY_LOGGABLE_OBJECT_METHODS_ONLY(logger)                            \
        LogMessage logFatal() const noexcept { return VIVY_LOG_FATAL(logger); }  \
        LogMessage logError() const noexcept { return VIVY_LOG_ERR(logger); }    \
        LogMessage logWarning() const noexcept { return VIVY_LOG_WARN(logger); } \
        LogMessage logInfo() const noexcept { return VIVY_LOG_INFO(logger); }    \
        LogMessage logDebug() const noexcept { return VIVY_LOG_DEBUG(logger); }
    
    // Install logger for the object.
    #define VIVY_LOGGABLE_OBJECT(sink, name, logger)                   \
        std::shared_ptr<Logger> logger{ VIVY_GET_LOGGER(sink, name) }; \
        VIVY_LOGGABLE_OBJECT_METHODS_ONLY(logger)
    
    // Install logger for the object. Here `name` must be a variable that can be
    // used to create a std::stding_view.
    #define VIVY_LOGGABLE_OBJECT_BY_STORED_NAME(sink, name, logger)                   \
        std::shared_ptr<Logger> logger{ VIVY_GET_LOGGER_BY_STORED_NAME(sink, name) }; \
        VIVY_LOGGABLE_OBJECT_METHODS_ONLY(logger)
    
    #define VIVY_LOG_CTOR() logDebug() << "<<CTOR>> "
    #define VIVY_LOG_DTOR() logDebug() << "<<DTOR>> "
    
    #define VIVY_LOG_QUOTED(something) '\'' << (something) << '\''
    
    // Install logger, use the global LogSink
    #define VIVY_APP_LOGGABLE_OBJECT(name, logger) \
        VIVY_LOGGABLE_OBJECT(vivyApp->getLogSink(), name, logger)
    
    // Install logger, use the global LogSink, with a stored name like in the
    // VIVY_LOGGABLE_OBJECT_BY_STORED_NAME macro.
    #define VIVY_APP_LOGGABLE_OBJECT_BY_STORED_NAME(name, logger) \
        VIVY_LOGGABLE_OBJECT_BY_STORED_NAME(vivyApp->getLogSink(), name, logger)
    
    namespace Vivy
    {
    class LogSinkDispatcher;
    class LogSink;
    class Logger;
    class LogMessage;
    
    // The severity of an event. Critical will cause the LogSink to flush all
    // messages to its emeters and then abort.
    struct LogLevel final {
        enum Level : int {
            None, // In option setup to disable logs
            Debug,
            Info,
            Warning,
            Error,
            Critical, // Will trigger qFatal
            ___MaxAndUnused
        };
    
        static const std::string_view toStdStringView(const LogLevel::Level) noexcept;
    
    private:
        using Array = std::array<const std::string_view, ___MaxAndUnused>;
        static inline constexpr Array LevelsStringViews = { "None",   "Debug", "Info",
                                                            "Warnin", "Error", "Critical" };
    
        LogLevel() {}
    };
    
    // A LogSinkDispatcher will excavate LogMessages from the Sink and do something
    // with them (save to a file, display in stderr/Vivy console, etc).
    class LogSinkDispatcher {
        VIVY_UNMOVABLE_OBJECT(LogSinkDispatcher)
    
        const std::string dispatcherName;
    
    protected:
        explicit LogSinkDispatcher(const std::string_view name) noexcept
            : dispatcherName(name)
        {
        }
    
    public:
        const std::string_view getDispatcherName() const noexcept { return dispatcherName; }
        virtual ~LogSinkDispatcher() noexcept;
    
        virtual void handleLogMessage(const std::string_view, const LogMessage &) noexcept = 0;
    };
    
    // The stderr dispatcher for logs
    class StderrLogSinkDispatcher : public LogSinkDispatcher {
        VIVY_UNMOVABLE_OBJECT(StderrLogSinkDispatcher)
    
        static const std::string_view trunkFileName(const char *) noexcept;
        static std::string reduceFileName(const std::string_view) noexcept;
    
    public:
        explicit StderrLogSinkDispatcher() noexcept;
        void handleLogMessage(const std::string_view, const LogMessage &) noexcept override;
    };
    
    // LogSink is the parent logger. It will recieve messages and display them to a
    // console or to std::cerr or to a file, etc. The save to over file, etc will
    // be donne by the LogSinkDispatcher.
    class LogSink {
        VIVY_UNMOVABLE_OBJECT(LogSink)
    
        explicit LogSink() noexcept = default;
    
    public:
        static std::shared_ptr<LogSink> newSink() noexcept;
        ~LogSink() noexcept;
    
        void registerLogDispatcher(std::shared_ptr<LogSinkDispatcher>) noexcept;
        void registerLogger(std::shared_ptr<Logger>) noexcept;
        void recieveLogMessage(const Logger *const, LogMessage &&) noexcept;
        void flush() noexcept;
    
        template <Derived<LogSinkDispatcher> DispatcherType, typename... Args>
        std::shared_ptr<DispatcherType> newDispatcher(Args &&...args) noexcept
        {
            std::shared_ptr<DispatcherType> dispatch =
                std::make_shared<DispatcherType>(std::forward<Args>(args)...);
            registerLogDispatcher(dispatch);
            return dispatch;
        }
    
        std::shared_ptr<Logger> newLogger(const StringType auto &category) noexcept
        {
            std::shared_ptr<Logger> logger = std::make_shared<Logger>(this, category);
            registerLogger(logger);
            return logger;
        }
    
    private:
        std::mutex messageQueueLock{};
        std::vector<std::tuple<const std::string_view, LogMessage>> messageQueue;
        std::vector<std::shared_ptr<LogSinkDispatcher>> logDispatchers;
        std::vector<std::shared_ptr<Logger>> loggers;
    };
    
    // Message to be logged, constructed by a logger then send
    class LogMessage final {
    public:
        using TimeStamp = chrono::milliseconds;
    
        // The header of the LogMessage
        struct Header final {
            const char *fileName;
            const char *functionName;
            const LogLevel::Level severity;
            const int lineNumberInFile;
        };
    
    private:
        const Header messageHeader;
        const TimeStamp timeStamp{ duration_cast<TimeStamp>(
            chrono::system_clock::now().time_since_epoch()) };
    
        static constexpr inline size_t messageBufferLength = 2048;
        std::array<char, messageBufferLength> textBuffer{};
        std::size_t indexInArray{ 0 };
        const Logger *parentLogger{ nullptr };
    
        static std::string pointerToString(const auto *ptr) noexcept
        {
            std::stringstream stream;
            stream << "0x" << std::setfill('0') << std::setw(sizeof(std::intptr_t) * 2) << std::hex
                   << reinterpret_cast<const std::intptr_t>(ptr);
            return stream.str();
        }
    
    public:
        VIVY_DISABLE_COPY_CTOR(LogMessage)
        VIVY_DISABLE_ASSIGN_OPERATORS(LogMessage)
    
        explicit LogMessage(const Logger *const, const Header) noexcept;
        explicit LogMessage(LogMessage &&) noexcept;
        ~LogMessage() noexcept; // The message will be send on destruction
    
        Header const &getHeader() const noexcept { return messageHeader; }
        const std::string_view getTextBuffer() const noexcept;
        const TimeStamp getTimeStamp() const noexcept { return timeStamp; }
    
        LogMessage &&sink() noexcept;
    
        LogMessage &operator<<(const std::string &) noexcept;
        LogMessage &operator<<(const std::string_view) noexcept;
        LogMessage &operator<<(const QString &) noexcept;
        LogMessage &operator<<(const QVariant &) noexcept;
        LogMessage &operator<<(const QFileInfo &) noexcept;
        LogMessage &operator<<(const char *) noexcept;
        LogMessage &operator<<(const double *) noexcept;
        LogMessage &operator<<(const char) noexcept;
        LogMessage &operator<<(const int) noexcept;
        LogMessage &operator<<(const long) noexcept;
        LogMessage &operator<<(const long long) noexcept;
        LogMessage &operator<<(const unsigned char) noexcept;
        LogMessage &operator<<(const unsigned int) noexcept;
        LogMessage &operator<<(const unsigned long) noexcept;
        LogMessage &operator<<(const unsigned long long) noexcept;
    };
    
    // A logger class, a client to LogSink. Will generate and send instances of
    // LogMessage.
    class Logger final : public std::enable_shared_from_this<Logger> {
        VIVY_UNMOVABLE_OBJECT(Logger)
    
        LogSink *const parentLogSink;
        const std::string logCategory;
    
    public:
        // Templated constructor, construct from anything that can be considered as
        // a string.
        explicit Logger(LogSink *const sink, const StringType auto category) noexcept
            : parentLogSink(sink)
            , logCategory(Utils::anyStringToStdString(category))
        {
        }
    
        const std::string_view getCategoryView() const noexcept { return logCategory; }
    
        void sendLogMessage(LogMessage &&) const noexcept;
        LogMessage logEvent(const char *fileName, const char *functionName, const int lineNumber,
                            const LogLevel::Level) noexcept;
    };
    }