Program Listing for File logger.cpp

Return to documentation for file (PrismEngine/src/logger.cpp)

#include "logger.h"

namespace prism::logger {
    namespace {
        Level logLevel = Level::INFO;
        std::unique_ptr<std::ofstream> logFileStream;
        bool useCustomOutput = false;

        // Стандартная функция вывода SDL
        static SDL_LogOutputFunction defaultOutputFunction = nullptr;
        static void* defaultUserdata = nullptr;

        std::optional<Error> lastError;

        // функция вывода
        void CustomOutputFunction(void* userdata, int category,
            SDL_LogPriority priority, const char* message) {
            if (logFileStream && logFileStream->is_open()) {
                *logFileStream << message << std::endl;
            }

            // Всегда выводим в стандартный вывод SDL
            if (defaultOutputFunction) {
                defaultOutputFunction(defaultUserdata, category, priority, message);
            }
        }

        const std::unordered_map<Error, std::string> errorMessages = {
            {Error::TEST_ERROR, "It is test error"},
            {Error::FAILED_TO_CREATE_VK_INSTANCE, "failed to create Vulkan instance!"},
            {Error::FAILED_TO_CREATE_WINDOW_SURFACE, "failed to create window surface!"},
            {Error::PRISM_ENGINE_NOT_INITIALIZE, "the part of PrismEngine that requires initialization has been called, please call prism::init() at the beginning of your program" },
            {Error::WINDOW_INSTANCE_CREATE_ERROR, "Window instance creation failed for unspecified reasons"},
            {Error::NOT_CALL_CLEANUP_IN_PGC_CORE, "Cleanup was not called for an instance of the PGC::core::Core class"},
            {Error::SDL_VULKAN_EXTENSIONS_COUNT_FAILED, "Failed to get SDL Vulkan instance extensions count"},
            {Error::VULKAN_VALIDATION_LAYERS_UNAVAILABLE, "Vulkan validation layers requested, but not available!"},
            {Error::VULKAN_DEBUG_MESSENGER_SETUP_FAILED, "Failed to set up debug messenger!"},
            {Error::VULKAN_NO_GPU_SUPPORT, "Failed to find GPUs with Vulkan support!"},
            {Error::VULKAN_NO_SUITABLE_GPU, "Failed to find a suitable GPU!"},
            {Error::VULKAN_LOGICAL_DEVICE_CREATION_FAILED, "Failed to create logical device!"},
            {Error::VULKAN_SWAP_CHAIN_FAILED, "Failed to create swap chain!"},
            {Error::FAILED_TO_OPEN_FILE, "Failed to open file"},
            {Error::FAILED_TO_DETERMINE_FILE_SIZE, "Failed to determine file size"},
            {Error::FAILED_TO_READ_FILE, "Failed to read file"}
        };
    }

    void setLevel(Level level) {
        logLevel = level;
        SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
            static_cast<SDL_LogPriority>(level));
    }

    bool setOutputFile(const std::string& filename) {
        try {
            // Сохраняем стандартный вывод, если ещё не сделали этого
            if (!defaultOutputFunction) {
                SDL_LogGetOutputFunction(&defaultOutputFunction, &defaultUserdata);
            }

            logFileStream = std::make_unique<std::ofstream>();
            logFileStream->open(filename, std::ios::app);

            if (!logFileStream->is_open()) {
                logFileStream.reset();
                return false;
            }

            SDL_LogSetOutputFunction(CustomOutputFunction, nullptr);
            useCustomOutput = true;
            return true;
        }
        catch (...) {
            logFileStream.reset();
            return false;
        }
    }

    void setOutputConsole() {
        if (logFileStream) {
            logFileStream->close();
            logFileStream.reset();
        }

        if (useCustomOutput && defaultOutputFunction) {
            SDL_LogSetOutputFunction(defaultOutputFunction, defaultUserdata);
        }
        useCustomOutput = false;
    }

    void log(Level level, const std::string& message) {
        if (static_cast<int>(level) < static_cast<int>(logLevel)) return;
        SDL_LogPriority priority = static_cast<SDL_LogPriority>(level);
        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", message.c_str());
    }

    void logError(Error err, const std::string& details) {
        std::string fullMsg = "[ENGINE ERROR] " + errorMessages.at(err);
        if (!details.empty()) {
            fullMsg += " | Details: " + details;
        }

        // Сохраняем ошибку
        lastError = err;

        log(Level::ERROR, fullMsg);
    }

    void verbose(const std::string& message) { log(Level::VERBOSE, message); }
    void debug(const std::string& message) { log(Level::DEBUG, message); }
    void info(const std::string& message) { log(Level::INFO, message); }
    void warning(const std::string& message) { log(Level::WARNING, message); }
    void error(const std::string& message) { log(Level::ERROR, message); }
    void critical(const std::string& message) { log(Level::CRITICAL, message); }
    std::optional<Error> getLastError()
    {
        return lastError;
    }

    bool hasLastError() {
        return lastError.has_value();
    }

    bool checkLastErrorType(Error errorType) {
        return lastError.has_value() && lastError == errorType;
    }

    void clearLastError() {
        lastError.reset();
    }
}