logger.h
#pragma once

#include <memory>
#include <string>

#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"

namespace flower {
class logger final {
public:
    static logger &instance() {
        static logger ins{};
        return ins;
    }

    logger(const logger &) = delete;

    logger &operator=(const logger &) = delete;

    ~logger() {
        if (logger_) {
            logger_->flush();
        }
    };

private:
    logger() {
        spdlog::set_pattern("%C-%m-%d %T %t %^%l%$ [not init logger] %v");
    };

public:
    bool start(const std::string &process_name);

    template <typename... Args>
    void log(spdlog::level::level_enum level, Args &&... args) {
        if (logger_) {
            logger_->log(level, std::forward<Args>(args)...);
        } else {
            SPDLOG_ERROR(std::forward<Args>(args)...);
        }
    }

    void set_log_level(spdlog::level::level_enum level) {
        if (logger_) {
            logger_->set_level(level);
        }
    }

private:
    template <typename T, typename... Args>
    spdlog::sink_ptr create_logger(uint8_t level, Args &&... args) {
        auto lv = static_cast<spdlog::level::level_enum>(level);
        auto log = std::make_shared<T>(std::forward<Args>(args)...);
        log->set_level(lv);
        return log;
    };

private:
    std::shared_ptr<spdlog::logger> logger_;
};
}  // namespace flower

#define LOG_INFO(...) flower::logger::instance().log(spdlog::level::level_enum::info, ##__VA_ARGS__)
#define LOG_WARN(...) flower::logger::instance().log(spdlog::level::level_enum::warn, ##__VA_ARGS__)
#define LOG_DEBUG(...) flower::logger::instance().log(spdlog::level::level_enum::debug, ##__VA_ARGS__)
#define LOG_ERROR(...) flower::logger::instance().log(spdlog::level::level_enum::err, ##__VA_ARGS__)
logger.cpp
#include "logger.h"

#include "config.h"
#include "comm_func.h"

namespace flower {
bool logger::start(const std::string& process_name) {
    bool ret{};
    try {
        auto file_name = config::instance().get<std::string>(process_name + "_log_file_name");
        auto name = config::instance().get<std::string>(process_name + "_log_name");
        auto pattern = config::instance().get<std::string>(process_name + "_log_pattern");
        auto file_level = config::instance().get<uint32_t>(process_name + "_log_file_level");
        auto console_level = config::instance().get<uint32_t>(process_name + "_log_console_level");

        file_name = comm_func::get_current_exe_dir().append("/").append(file_name);
        std::vector<spdlog::sink_ptr> sinks;
        // file logger
        sinks.push_back(create_logger<spdlog::sinks::daily_file_sink_mt>(file_level, file_name, 0, 0));

        // console logger
        sinks.push_back(create_logger<spdlog::sinks::stdout_color_sink_mt>(console_level));

        auto log = std::make_shared<spdlog::logger>(name, sinks.begin(), sinks.end());
        log->set_level(spdlog::level::trace);
        spdlog::register_logger(log);

        // set option
        log->set_pattern(pattern);

        spdlog::set_error_handler([](const std::string& msg) {
            LOG_ERROR("logger error handler: {}", msg);
        });

        spdlog::flush_every(std::chrono::seconds(5));

        logger_ = log;
        ret = true;
    } catch (spdlog::spdlog_ex& e) {
        LOG_ERROR("logger::start exception message:{}", e.what());
    } catch (...) {
        LOG_ERROR("logger::start other exception");
    }

    return ret;
}
}  // namespace flower