为什么使用日志库而不是控制台输出?
日志库通常提供了更丰富的功能,比如可以设置日志输出级别、输出到不同的目标(比如控制台、文件、网络等),以及格式化输出等。
使用日志库可以使代码更易于维护。通过统一的日志接口,可以更容易地对日志输出进行修改、调整和管理,而不需要在代码的各个地方进行修改。
使用日志库可以优化性能,比如通过缓冲输出、异步写入等方式来减少对程序性能的影响。而直接使用 cout 可能会导致频繁的 IO 操作,影响程序的性能。
Spdlog是一个高效的日志库,具有以下优点:它只包含一个头文件,因此易于集成;其速度快且无需依赖第三方库,支持跨平台和多线程操作,保证线程安全;可对日志文件进行循环输出并每日生成新的日志文件;支持控制台输出,可选的异步输出,同时允许用户自定义日志格式。
编译安装
环境是Linux Ubuntu 20.04, 采取源码安装方式。Cmake构建项目。
首先在Spdlog的github仓库中下载最新的源代码
git clone https://github.com/gabime/spdlog.git
进入源代码目录然后创建"build"目录:
cd spdlog
mkdir build
cd build
在build目录中执行编译命令:
cmake ..
make
sudo make install
将编译生成的库文件安装到系统指定的目录下,这样你可以在任何地方使用这个库,或者也可以以源码形式引入,拷贝include文件夹到你的构建树。
cmake_minimum_required(VERSION 3.10)
project(spdlog_examples CXX)if(NOT TARGET spdlog)# Stand-alone buildfind_package(spdlog REQUIRED)
endif()# ---------------------------------------------------------------------------------------
# 使用预编译的库
# ---------------------------------------------------------------------------------------
add_executable(example example.cpp)
target_link_libraries(example PRIVATE spdlog::spdlog)# ---------------------------------------------------------------------------------------
# 使用头文件库
# ---------------------------------------------------------------------------------------
if(SPDLOG_BUILD_EXAMPLE_HO)add_executable(example_header_only example.cpp)target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only)
endif()
在example.cpp中编写以下代码:
#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/cfg/env.h> // support for loading levels from the environment variable
#include <spdlog/fmt/ostr.h> // support for user defined types
using namespace std;
using namespace spdlog;
int main()
{spdlog::cfg::load_env_levels();spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);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("{:>8} aligned, {:<8} aligned", "right", "left");return 0;
}
项目构成
spdlog项目采用CMake构建,其一级目录结构如下
$ tree -L 1
.
├── CMakeLists.txt // 根目录CMake文件
├── INSTALL // 安装说明
├── LICENSE // license声明文件
├── README.md // 项目介绍文档
├── appveyor.yml // 用于自动化构建, Windows构建平台的配置
├── bench // benchmark, 用于综合测试
├── build // 用于存放编译过程产生的中间文件
├── cmake // 用于存放与cmake构建项目有关的文件
├── example // 用户例程
├── include // 头文件根目录
├── logos // 用于存放logo
├── scripts // 存放脚本文件
├── src // 源码目录
└── tests // 单元测试目录
源码解读见本人其他blog
- 线程池thread_pool
- 自定义异常类spdlog_ex
- 格式化类formatter
- 日志记录器logger类
- 输出通道sink
- 全局管理类registry
代码编写
spdlog中logger对象和sink对象都有两种版本,一种是以st结尾的单线程版本,以及以mt结尾的多线程版本。
st:单线程版本,不用加锁,效率更高。
mt:多线程版本,用于多线程程序是线程安全的。
写入控制台
#include<iostream>
#include<spdlog/spdlog.h>
#include<spdlog/sinks/stdout_sinks.h>
void stdout_easy()
{// 根据参数“consoel”的名字在内部创建了一个logger,函数返回值就是这个logger的智能指针。//stdout_logger_mt就是控制台输出auto console = spdlog::stdout_logger_mt("console");// 直接通过智能指针调用名字对应“console”的logger对象的函数进行输出。console->info("hello world");//下面代码一样的效果//spdlog::get("console")->info("hello world");
}
int main()
{stdout_easy();return 0;
}
写入文件
#include<iostream>
#include<spdlog/spdlog.h>
#include<spdlog/logger.h>
#include<spdlog/sinks/basic_file_sink.h>
int main()
{// 创建一个名为basic_logger的日志记录器,并且返回指针。// mt表示多线程,意味着多个线程中使用同一个日志记录器。// 需要注意的是在文件内添加数据是通过追加的形式进行的,并且如果没有创建文件会自动创建文件。auto basic_logger = spdlog::basic_logger_mt("basic_logger", "basic_log.txt");for(int i=0; i<10; i++){basic_logger->info("Test file logger{}", i);}return 0;
}
剩余功能均可在源码阅读中,或项目构成中的example中找到,不多赘述。