spdlog 封装为 DLL

项目中需要快速写入巨量日志,一开始用的是boost::log,但遇到崩溃问题,程序负载大了,滚动日志时偶尔会崩溃,总是报:
boost::filesystem_errorbad_year, bad_month 一些问题。在boost官网,和github上的 boostlog 项目 issue中窥见些端倪,说是boost::filesystem存在问题blabla
枉我还一直很信任 boost ,还是换个日志库吧。

github 上 spdlog 星星比 boost::log 多多了,boost::log 好像用的人挺少。

这一换发现:相比boost::log,spdlog 速度又快,占内存又小(取决于队列长度)

使用 spdlog1.10.0 封装的 dll

  • 使用这个dll,你的项目不用再依赖 spdlog 头文件
  • C 风格的字符串格式化语法
  • 同步/异步、滚动日志
  • 程序退出前需要调用静态方法 shutdown()
  • VS2019 编译通过
    spdlog 封装为 DLL_第1张图片
// log_spd.h
#pragma once
#include 
#ifndef LOG_LIB_H
#define LOG_LIB_H

#ifdef _MSC_VER
#define LOG_API _declspec(dllexport)
#else
#define LOG_API 
#endif

class SpdlogWrapper;

enum class lv {
    trace,
    debug,
    info,
    warn,
    error,
    fatal
};

class LOG_API Logger
{
public:
    Logger();
    Logger(const std::string& file, lv level=lv::trace, int rotateFileSize=100 /* MB */, int rotateFileCount=5);
    Logger(const Logger&);
    Logger& operator=(const Logger&);
    ~Logger();

    // 返回一个日志对象
    static Logger createLogger(const std::string& filename, lv level=lv::trace, int rotateFileSize=100 /* MB */, int rotateFileCount=5);

    // 全局设置。定时写日志到文件。0 秒则不启用
    static void setFlushEverySec(int sec);

    // 设置日志文件
    void setFileName(const std::string&);

    // 设置日志前缀格式;default: [%Y-%m-%d %H:%M:%S.%e][%t][%l] %v
    void setFormat(const std::string&);

    // 设置日志等级,大于此等级才写入文件
    void setLevel(lv);

    // 设置滚动日志文件
    void setRotateFile(int size /* MB */, int count);

    // 设置立即写到文件的日志等级
    void setFlushOnLevel(lv);

    // 是否异步日志,default: true
    void setAsyncMode(bool);

    // 设置异步日志队列长度。默认 256。经测试,队列更长,占用内存更大,未见性能明显提升
    void setAsyncQueueSize(int);

    // 在调用 setXX 类函数之后,最后调用 init(); 赋值操作后,被赋值的对象需要重新 init()
    void init();
    void init(const std::string& file, lv level=lv::trace, int rotateFileSize=100 /* MB */, int rotateFileCount=5);

    // 格式化后的字符串最大长度: 1024
    void trace(const char* fmt, ...);
    void info(const char* fmt, ...);
    void debug(const char* fmt, ...);
    void warn(const char* fmt, ...);
    void error(const char* fmt, ...);
    void fatal(const char* fmt, ...);

    // 异步模式:通知缓冲, 要求写入日志文件;
    // 同步模式:立即写入日志文件
    void flush();

    // EXE 主程序退出前需要调用。因为 spdlog 在 windows 上存在问题关不掉程序
    // 如果是在 dll 中使用此日志库,你的 dll 需要提供接口来让 EXE 程序在退出前主动调用到此函数
    static void shutdown();

private:
    SpdlogWrapper* m_logWrapper;
};


#endif // LOG_LIB_H

// log_spd.cpp
#pragma once
#include "log_spd.h"
#include "log_wrapper.h"


Logger::Logger() : m_logWrapper(nullptr)
{
    m_logWrapper = new SpdlogWrapper(DEFAULT_FILE, lv::trace, 100, 5);
}

Logger::Logger(const std::string& file,
    lv level,
    int rotateFileSize, // MB
    int rotateFileCount) : m_logWrapper(nullptr)
{
    m_logWrapper = new SpdlogWrapper(file, level, rotateFileSize, rotateFileCount);
}

Logger::Logger(const Logger& logger)
{
    m_logWrapper = new SpdlogWrapper(*logger.m_logWrapper);
}

Logger& Logger::operator=(const Logger& logger)
{
    delete m_logWrapper;
    m_logWrapper = new SpdlogWrapper(*logger.m_logWrapper);
    return *this;
}

Logger::~Logger()
{
    delete m_logWrapper;
}

void Logger::init()
{
    m_logWrapper->init();
}

void Logger::init(const std::string& file, lv level, int rotateFileSize, int rotateFileCount)
{
    m_logWrapper->m_fileName = file;
    m_logWrapper->m_logLevel = level;
    m_logWrapper->m_fileSize = rotateFileSize;
    m_logWrapper->m_fileCount = rotateFileCount;
    m_logWrapper->init();
}

Logger Logger::createLogger(const std::string& filename, lv level, int rotateFileSize, int rotateFileCount)
{
    return Logger(filename, level, rotateFileSize, rotateFileCount);
}
void Logger::setAsyncMode(bool enabled)
{
    m_logWrapper->m_async = enabled;
}

void Logger::flush()
{
    m_logWrapper->flush();
}

void Logger::setFileName(const std::string& file)
{
    m_logWrapper->m_fileName = file;
}

void Logger::setFormat(const std::string& fmt)
{
    m_logWrapper->m_fmt = fmt;
}

void Logger::setLevel(lv level)
{
    m_logWrapper->m_logLevel = level;
}

void Logger::setFlushOnLevel(lv level)
{
    m_logWrapper->m_flushOnLevel = (int)level;
}

void Logger::setFlushEverySec(int sec)
{
    SpdlogWrapper::FlushEverySec = sec;
}

void Logger::setAsyncQueueSize(int size)
{
    m_logWrapper->m_queueSize = size;
}

void Logger::setRotateFile(int rotateFileSize, int rotateFileCount)
{
    m_logWrapper->m_fileSize = rotateFileSize;
    m_logWrapper->m_fileCount = rotateFileCount;
}

void Logger::shutdown()
{
    SpdlogWrapper::shutdown();
}

void Logger::trace(const char* fmt, ...)
{
    if (m_logWrapper->m_logLevel > lv::trace)
        return;

    char buff[BUFF_SIZE];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf_s(buff, BUFF_SIZE, fmt, ap);
    va_end(ap);
    m_logWrapper->write(buff, lv::trace);
}

void Logger::info(const char* fmt, ...)
{
    if (m_logWrapper->m_logLevel > lv::info)
        return;

    char buff[BUFF_SIZE];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf_s(buff, BUFF_SIZE, fmt, ap);
    va_end(ap);
    m_logWrapper->write(buff, lv::info);
}

void Logger::debug(const char* fmt, ...)
{
    if (m_logWrapper->m_logLevel > lv::debug)
        return;

    char buff[BUFF_SIZE];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf_s(buff, BUFF_SIZE, fmt, ap);
    va_end(ap);
    m_logWrapper->write(buff, lv::debug);
}

void Logger::warn(const char* fmt, ...)
{
    if (m_logWrapper->m_logLevel > lv::warn)
        return;

    char buff[BUFF_SIZE];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf_s(buff, BUFF_SIZE, fmt, ap);
    va_end(ap);
    m_logWrapper->write(buff, lv::warn);
}

void Logger::error(const char* fmt, ...)
{
    if (m_logWrapper->m_logLevel > lv::error)
        return;

    char buff[BUFF_SIZE];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf_s(buff, BUFF_SIZE, fmt, ap);
    va_end(ap);
    m_logWrapper->write(buff, lv::error);
}

void Logger::fatal(const char* fmt, ...)
{
    if (m_logWrapper->m_logLevel > lv::fatal)
        return;

    char buff[BUFF_SIZE];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf_s(buff, BUFF_SIZE, fmt, ap);
    va_end(ap);
    m_logWrapper->write(buff, lv::fatal);
}
// log_wrapper.h
#pragma once
#ifndef LOG_API_H
#define LOG_API_H
#include 
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/details/thread_pool.h"

static const int BUFF_SIZE = 1024;
const std::string DEFAULT_FILE = "log.log";
const std::string DEFAULT_FMT = "[%Y-%m-%d %H:%M:%S.%e][%t][%l] %v";

class SpdlogWrapper
{

public:
    SpdlogWrapper(const std::string& file, lv level, int rotateFileSize, int rotateFileCount);
    SpdlogWrapper(const SpdlogWrapper&);
    ~SpdlogWrapper();

    void init();
    void write(const char* msg, lv level);
    void flush();

    static void shutdown();

    static int FlushEverySec;
    static int Id;
    static bool GlobalAttr;

    bool m_async;
    int m_queueSize;
    lv m_logLevel;
    int m_flushOnLevel;
    int m_fileCount;
    int m_fileSize;
    std::string m_fileName;
    std::string m_fmt;

private:
    std::string m_logName;
    std::shared_ptr<spdlog::logger> m_spdLogger;
    std::shared_ptr<spdlog::details::thread_pool> m_tp;
    std::shared_ptr<spdlog::sinks::rotating_file_sink_mt> m_sink;

    void initGlobalAttr();
};
#endif //LOG_API_H

// log_wrapper.cpp
#pragma once
#include "log_spd.h"
#include "log_wrapper.h"


int SpdlogWrapper::Id = 0;
int SpdlogWrapper::FlushEverySec = 3;
bool SpdlogWrapper::GlobalAttr = false;
SpdlogWrapper::SpdlogWrapper(const std::string& file, lv level, int rotateFileSize, int rotateFileCount)
    : m_fileCount(rotateFileCount), 
    m_fileSize(rotateFileSize), 
    m_logLevel(level), 
    m_fileName(file), 
    m_flushOnLevel(-1),
    m_fmt(DEFAULT_FMT), 
    m_logName(""), 
    m_queueSize(256),
    m_async(true)
{

}

SpdlogWrapper::SpdlogWrapper(const SpdlogWrapper& log)
{
    m_fileCount = log.m_fileCount;
    m_fileSize = log.m_fileSize;
    m_logLevel = log.m_logLevel;
    m_fileName = log.m_fileName;
    m_flushOnLevel = log.m_flushOnLevel;
    m_fmt = log.m_fmt;
    m_logName = "";
    m_queueSize = log.m_queueSize;
    m_async = log.m_async;

    m_spdLogger = nullptr;
    m_tp = nullptr;
    m_sink = nullptr;
}

void SpdlogWrapper::init()
{
    if (m_spdLogger)
        spdlog::drop(m_logName);

    initGlobalAttr();

    m_logName = "__log__" + std::to_string(SpdlogWrapper::Id++);
    if (m_async) {
        m_tp = std::make_shared<spdlog::details::thread_pool>(m_queueSize, 1U);
        m_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(m_fileName, m_fileSize * 1024 * 1024, m_fileCount);
        m_spdLogger = std::make_shared<spdlog::async_logger>(m_logName, m_sink, m_tp, spdlog::async_overflow_policy::block);
        spdlog::register_logger(m_spdLogger);
    }
    else 
        m_spdLogger = spdlog::rotating_logger_mt(m_logName, m_fileName, (size_t)m_fileSize * 1024 * 1024, m_fileCount);

    m_spdLogger->set_pattern(m_fmt);
    m_spdLogger->set_level((spdlog::level::level_enum)m_logLevel);

    if (m_flushOnLevel >= 0)
        m_spdLogger->flush_on((spdlog::level::level_enum)m_flushOnLevel);
}

void SpdlogWrapper::initGlobalAttr()
{
    if (!SpdlogWrapper::GlobalAttr) {
        spdlog::flush_every(std::chrono::seconds(SpdlogWrapper::FlushEverySec));
        SpdlogWrapper::GlobalAttr = true;
    }
}

void SpdlogWrapper::write(const char* msg, lv level)
{
    SPDLOG_LOGGER_CALL(m_spdLogger, (spdlog::level::level_enum)level, msg);
}

void SpdlogWrapper::flush()
{
    if (m_spdLogger)
        m_spdLogger->flush();
}

void SpdlogWrapper::shutdown()
{
    spdlog::drop_all();
    spdlog::shutdown();
}

SpdlogWrapper::~SpdlogWrapper()
{
    if (m_spdLogger)
        spdlog::drop(m_logName);
}

使用

// main.cpp

#include "log_spd.h"

int main(int argc, char  *argv[])
{
	Logger logger, logger2;
	// Logger logger = Logger::createLogger("mylog.log", lv::trace, 100, 5);
	logger.setAsyncMode(false); 
	logger.init();
	logger.trace("Hello %s, you are the %dth visitor.", "Tony", 1);
	logger.flush();
	
	logger2 = logger;
	logger2.setAsyncMode(true);
	logger2.setFormat("%+");
	logger2.init("mylog.log");
	logger2.info("blasa bad w");
	logger2.flush();
	
	Logger::shutdown();
	return 0;
}

log.log

[2022-09-30 14:32:25.065][9532][trace] Hello Tony, you are the 1th visitor.

mylog.log

[2022-09-30 14:32:25.069] [__log__1] [info] [log_wrapper.cpp:75] blasa bad woa

缺点

  • 输出日志所在的代码文件、行号不是调用封装接口的行。比如上面的 [log_wrapper.cpp:75]

编译好的库

你可能感兴趣的:(C++,c++,c语言)