什么是spdlog
spdlog是一个高性能的C++日志库,支持跨平台,兼容C++11,是一款轻量级日志文件, header-only。可以支持多线程、异步、可以将日志输出到控制台或者文件里面
编译
$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && make -j
$ make install
可能出现的问题
有两个可能的原因:
- 构建spdlog源码包之后没有make install
- 安装一下fmt
使用
控制台输出
#include <spdlog/spdlog.h>
int main()
{
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// Compile time log levels
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
}
文件输出
#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
int main()
{
try
{
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
logger->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
}
catch (const spdlog::spdlog_ex& ex)
{
std::cout << "Log initialization failed: " << ex.what() << std::endl;
}
}
循环日志
日志达到一定长度之后会分割,而且不会无限增长
#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include<spdlog/sinks/rotating_file_sink.h>
int main()
{
try
{
auto max_size = 1048576 * 5;
auto max_files = 3;
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
for(int i=0;i<1000000;i++){
logger->info("Support for floats {:03.2f}", 1.23456);
}
}
catch (const spdlog::spdlog_ex& ex)
{
std::cout << "Log initialization failed: " << ex.what() << std::endl;
}
}
异步日志
#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/rotating_file_sink.h>
int main(){
spdlog::init_thread_pool(8192, 1);
auto logger = spdlog::rotating_logger_mt<spdlog::async_factory>("file_logger", "mylogs", 1024 * 1024 * 5, 100);
int i = 0;
while (i < 1000000)
{
logger->info("Async message #{}", i);
i++;
}
spdlog::drop_all();
return 0;
}
封装成单例异步模式
log.h
// logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include<spdlog/async.h>
// 日志的配置项
struct LogConfig {
std::string level;
std::string path;
int64_t size;
int count;
};
// 日志的单例模式
class Logger {
public:
static Logger* getInstance() {
static Logger instance;
return &instance;
}
//c++14返回值可设置为auto
std::shared_ptr<spdlog::logger> getLogger() {
return loggerPtr;
}
void Init(const LogConfig& conf);
std::string GetLogLevel();
void SetLogLevel(const std::string& level);
private:
Logger() = default;
std::shared_ptr<spdlog::logger> loggerPtr;
};
// 日志相关操作的宏封装
#define INITLOG(conf) Logger::getInstance()->Init(conf)
#define GETLOGLEVEL() Logger::getInstance()->GetLogLevel()
#define SETLOGLEVEL(level) Logger::getInstance()->SetLogLevel(level)
#define BASELOG(logger, level, ...) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, __func__}, level, __VA_ARGS__)
#define LOG_TRACE(...) BASELOG(Logger::getInstance()->getLogger(), spdlog::level::trace, __VA_ARGS__)
#define LOG_DEBUG(...) BASELOG(Logger::getInstance()->getLogger(), spdlog::level::debug, __VA_ARGS__)
#define LOG_INFO(...) BASELOG(Logger::getInstance()->getLogger(), spdlog::level::info, __VA_ARGS__)
#define LOG_WARN(...) BASELOG(Logger::getInstance()->getLogger(), spdlog::level::warn, __VA_ARGS__)
#define LOG_ERROR(...) BASELOG(Logger::getInstance()->getLogger(), spdlog::level::err, __VA_ARGS__)
#define LOG_CRITICAL(...) BASELOG(Logger::getInstance()->getLogger(), spdlog::level::critical, __VA_ARGS__)
#endif // LOGGER_H
log.cpp
// logger.cpp
#include "log.h"
void Logger::Init(const LogConfig& conf) {
//自定义的sink,日志的名字是file_logger
loggerPtr = spdlog::rotating_logger_mt<spdlog::async_factory>("file_logger", conf.path.c_str(), conf.size, conf.count);
//设置格式
//参见文档 https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
//[%Y-%m-%d %H:%M:%S.%e] 时间
//[%l] 日志级别
//[%t] 线程
//[%s] 文件
//[%#] 行号
//[%!] 函数
//[%v] 实际文本
loggerPtr->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] [%s %!:%#] %v");
// 设置日志级别
loggerPtr->set_level(spdlog::level::from_str(conf.level));
// 设置刷新日志的日志级别,当出现level或更高级别日志时,立刻刷新日志到 disk
loggerPtr->flush_on(spdlog::level::from_str(conf.level));
}
/*
* trace 0
* debug 1
* info 2
* warn 3
* error 4
* critical 5
* off 6 (not use)
*/
std::string Logger::GetLogLevel() {
auto level = loggerPtr->level();
return spdlog::level::to_string_view(level).data();
}
void Logger::SetLogLevel(const std::string& log_level) {
auto level = spdlog::level::from_str(log_level);
if (level == spdlog::level::off) {
LOG_WARN("Given invalid log level {}", log_level);
} else {
loggerPtr->set_level(level);
loggerPtr->flush_on(level);
}
}
threadpool.h
#include <functional>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include<assert.h>
#include <condition_variable>
class ThreadPool
{
private:
struct Pool {
//互斥锁
std::mutex mtx;
//条件变量
std::condition_variable cond;
//是否关闭
bool isClosed;
//任务队列
std::queue<std::function<void()>> tasks;
};
//指向线程池的指针
std::shared_ptr<Pool> pool_;
public:
explicit ThreadPool(int threadCount = std::thread::hardware_concurrency()): pool_(std::make_shared<Pool>()) {
assert(threadCount > 0);
for(int i = 0; i < threadCount; i++) {
//lambada函数
std::thread([pool = pool_] {
std::unique_lock<std::mutex> locker(pool->mtx);
while(true) {
if(!pool->tasks.empty()) {
auto task = std::move(pool->tasks.front());
pool->tasks.pop();
locker.unlock();
task();
locker.lock();
}
else if(pool->isClosed) break;
else pool->cond.wait(locker);
}
}).detach();
}
}
ThreadPool() = default;
ThreadPool(ThreadPool&&) = default;
~ThreadPool() {
if(static_cast<bool>(pool_)) {
{
std::lock_guard<std::mutex> locker(pool_->mtx);
pool_->isClosed = true;
}
pool_->cond.notify_all();
}
}
template<class F>
void AddTask(F&& task) {
{
std::lock_guard<std::mutex> locker(pool_->mtx);
pool_->tasks.emplace(std::forward<F>(task));
}
pool_->cond.notify_one();
}
};
test.cpp
#include "log.h"
#include "threadpool.h"
#include <features.h>
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif
void ThreadLogTask(int i, int cnt) {
for(int j = 0; j < 10000000; j++ ){
LOG_INFO("PID:[{:08d}]======= {:08d} ========= ", gettid(), cnt++);
}
}
void TestThreadPool() {
LogConfig conf2 = {
.level = "trace",
.path = "../log/thread_test.log",
.size = 5 * 1024 * 1024,
.count = 10,
};
INITLOG(conf2);
ThreadPool threadpool(6);
for(int i = 0; i < 18; i++) {
threadpool.AddTask(std::bind(ThreadLogTask, i % 4, i * 10000));
}
getchar();
}
int main() {
TestThreadPool();
}
运行结果