//main .cpp
    #include <logger.hpp>
    
    int main() {
        logger _logger(logger::trace);
        _logger.LOG(logger::debug, "test1", "test2", 1, 2, 3.1, 'X');
        return 0;
    }
    
     
//logger.hpp
    #pragma once
    
    #include <memory>
    #include <sstream>
    
    class logger {
    public:
        enum level_enum : int {
            trace = 0,
            debug = 1,
            info = 2,
            warn = 3,
            err = 4,
            critical = 5,
            off = 6,
            n_levels
        };
    
        explicit logger(logger::level_enum level);
        ~logger();
    
        template <typename... T>
        void LOG(level_enum level, T... msg) {
            auto stream = dump(std::forward<T>(msg)...);
            print(level, stream);
        }
    
        template <typename... T>
        std::ostringstream dump(T... msg) {
            std::ostringstream stream;
            ((stream << msg << ' '), ...);
            return stream;
        }
    
        void print(level_enum level, std::ostringstream& stream);
    
    private:
        class Impl;
        std::unique_ptr<Impl> pImpl;
    };
    
     
//spd_logger.cpp
    #include "spdlog/sinks/basic_file_sink.h"
    #include "spdlog/sinks/stdout_color_sinks.h"
    #include "spdlog/spdlog.h"
    #include <logger.hpp>
    
    #include <memory>
    
    class logger::Impl {
        std::shared_ptr<spdlog::logger> logger;
    
    public:
        explicit Impl(spdlog::level::level_enum level) {
            logger = std::make_shared<spdlog::logger>("LOG");
            logger->sinks().push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
            logger->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/basic_sink.txt"));
            logger->set_level(level);
        }
    
        void log(logger::level_enum level, std::ostringstream& msg) {
            logger->log(static_cast<spdlog::level::level_enum>(level), msg.str());
        }
    };
    
    logger::logger(logger::level_enum level)
            : pImpl(std::make_unique<Impl>(static_cast<spdlog::level::level_enum>(level))) {}
    
    logger::~logger() = default;
    
    void logger::print(level_enum level, std::ostringstream& msg) {
        this->pImpl->log(level, msg);
    }