交叉编译spdlpg 参数详解

本文涉及的产品
语种识别,语种识别 100万字符
文本翻译,文本翻译 100万字符
日志服务 SLS,月写入数据量 50GB 1个月
简介: 交叉编译spdlpg 参数详解

cmake 选项

以下是一个Markdown表格,展示了在编译时可能用到的CMake参数及其作用:

Parameter Description
CMAKE_BUILD_TYPE 设置构建类型。可以是“Release”或“Debug”。如果未设置,默认为“Release”。
SPDLOG_USE_STD_FORMAT 如果设置为ON,则使用std::format而非fmt库。
CMAKE_CXX_STANDARD 设置C++标准版本。如果SPDLOG_USE_STD_FORMAT为ON,使用C++20;否则,默认为C++11。
MSVC 针对Microsoft Visual C++特有的编译器标志,比如/Zc:__cplusplus /MP
CMAKE_CXX_EXTENSIONS 设置是否使用编译器特定扩展。在CYGWIN或MSYS系统上为ON,其他系统默认为OFF。
SPDLOG_MASTER_PROJECT 如果spdlog是主项目,则设置为ON;如果作为子项目通过add_subdirectory包含,则设置为OFF。
SPDLOG_BUILD_SHARED 设置为ON以构建共享库;否则构建静态库。
SPDLOG_ENABLE_PCH 设置为ON以使用预编译头来加速编译时间。
SPDLOG_BUILD_PIC 设置为ON以构建位置独立代码(-fPIC)。
SPDLOG_BUILD_EXAMPLE 如果设置为ON,将构建示例。
SPDLOG_BUILD_EXAMPLE_HO 如果设置为ON,将构建仅头文件版本的示例。
SPDLOG_BUILD_TESTS 设置为ON以构建测试。
SPDLOG_BUILD_TESTS_HO 设置为ON以构建测试使用的仅头文件版本。
SPDLOG_BUILD_BENCH 设置为ON以构建基准测试(需安装Google Benchmark库)。
SPDLOG_SANITIZE_ADDRESS 设置为ON以在测试中启用地址消毒器。
SPDLOG_BUILD_WARNINGS 设置为ON以启用编译器警告。
SPDLOG_INSTALL 设置为ON以生成安装目标。
SPDLOG_USE_STD_FORMAT 设置为ON以使用std::format而非fmt库。
SPDLOG_FMT_EXTERNAL 设置为ON以使用外部fmt库而非捆绑的版本。
SPDLOG_FMT_EXTERNAL_HO 设置为ON以使用外部fmt库的头文件版本。
SPDLOG_NO_EXCEPTIONS 设置为ON以编译时禁用异常处理,改为在spdlog异常时调用abort()。
SPDLOG_WCHAR_SUPPORT Windows专用,设置为ON以支持宽字符API。
SPDLOG_WCHAR_FILENAMES Windows专用,设置为ON以支持宽字符文件名。
SPDLOG_CLOCK_COARSE Linux专用,设置为ON以使用CLOCK_REALTIME_COARSE而非常规时钟。
SPDLOG_PREVENT_CHILD_FD 设置为ON以防止子进程继承日志文件描述符。
SPDLOG_NO_THREAD_ID 设置为ON以阻止spdlog在每次日志调用时查询线程ID(如果线程ID不需要)。
SPDLOG_NO_TLS 设置为ON以阻止spdlog使用线程本地存储。
SPDLOG_NO_ATOMIC_LEVELS 设置为ON以防止spdlog使用std::atomic日志级别(仅当代码不会并发修改日志级别时使用)。
SPDLOG_DISABLE_DEFAULT_LOGGER 设置为ON以禁用默认日志器的创建。
SPDLOG_TIDY 如果CMake版本大于3.5,设置为ON以运行clang-tidy。

这个表格涵盖了在CMake中构建spdlog时可能会用到的主要参数,以及它们对构建过程的影响。

编译步骤(按需选择)

可以使用以下CMake命令行参数来构建spdlog

  1. 进入构建目录:首先,您需要创建一个build目录(如果还没有的话),然后进入该目录。例如:
mkdir build
cd build
  1. 运行CMake:使用下面的命令来配置和生成构建系统。请确保替换[PathToYourToolchainFile]为您的ToolchainFile.cmake文件的实际路径。
cmake .. -DCMAKE_BUILD_TYPE=Release \
         -DSPDLOG_MASTER_PROJECT=ON \
         -DSPDLOG_BUILD_SHARED=ON \
         -DSPDLOG_BUILD_PIC=ON \
         -DSPDLOG_BUILD_WARNINGS=ON \
         -DCMAKE_TOOLCHAIN_FILE=[PathToYourToolchainFile]
  1. 这条命令做了以下几点:
  • 设置构建类型为Release
  • SPDLOG_MASTER_PROJECT设置为ON,表示spdlog是主项目。
  • SPDLOG_BUILD_SHARED设置为ON,以构建共享库。
  • SPDLOG_BUILD_PIC设置为ON,以生成位置独立的代码。
  • SPDLOG_BUILD_WARNINGS设置为ON,以启用编译器警告。
  • 指定交叉编译的工具链文件。
  1. 构建项目:在配置完成后,使用以下命令来构建项目:
cmake --build .

指定安装目录

如果您想指定一个自定义的安装目录,您可以使用CMAKE_INSTALL_PREFIX变量来设置这个目录。这个变量定义了项目安装的根目录。例如,如果您想将spdlog安装到/my/custom/path目录,您可以在CMake命令中加入-DCMAKE_INSTALL_PREFIX=/my/custom/path

代码测试

简单的spdlog使用实例

当然可以。以下是一个简单的spdlog使用示例。这个例子展示了如何初始化一个基本的日志记录器,并使用它来记录不同级别的消息。

首先,确保您已经安装了spdlog。如果您还没有安装,可以通过类似这样的命令进行安装(取决于您的系统和包管理器):

# 使用vcpkg(Windows/Linux/macOS)
vcpkg install spdlog
# 使用Homebrew(macOS)
brew install spdlog
# 使用Conan
conan install spdlog/1.12.0@

接下来,创建一个C++文件(例如main.cpp),并添加以下代码:

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
int main() {
    // 创建并初始化一个控制台日志记录器
    auto console = spdlog::stdout_color_mt("console");
    // 设置日志级别为debug(这将记录所有级别的日志)
    console->set_level(spdlog::level::debug);
    // 记录不同级别的日志
    console->error("这是一条错误信息");
    console->warn("这是一条警告信息");
    console->info("这是一条普通信息");
    console->debug("这是一条调试信息");
    console->trace("这是一条追踪信息");
    return 0;
}

然后,您需要编译这个程序。在编译时,确保链接到spdlog库。如果您使用的是CMake,您的CMakeLists.txt可能看起来像这样:

cmake_minimum_required(VERSION 3.1)
project(spdlog_example)
# 查找spdlog库
find_package(spdlog REQUIRED)
# 创建可执行文件
add_executable(spdlog_example main.cpp)
# 链接spdlog库
target_link_libraries(spdlog_example PRIVATE spdlog::spdlog)

运行这个程序将在控制台上输出不同级别的日志信息,每条信息都有不同的颜色以区分级别。这是spdlog的一个基本示例,但spdlog也支持更高级的功能,如文件日志记录、日志格式自定义、多线程和异步日志记录等。

宏定义 LOG(INFO)

spdlog 中,日志输出通常不是通过 LOG(INFO) 这种宏来实现的,而是通过直接调用具体的日志级别函数,如 info(), warn(), error() 等。但是,你可以通过定义自己的宏来模拟类似 LOG(INFO) 这样的语法。

例如,你可以定义一系列简单的宏来封装 spdlog 的日志调用:

#include <spdlog/spdlog.h>
// 定义宏
#define LOG_INFO(logger, ...) logger->info(__VA_ARGS__)
#define LOG_WARN(logger, ...) logger->warn(__VA_ARGS__)
#define LOG_ERROR(logger, ...) logger->error(__VA_ARGS__)
// 以此类推...
int main() {
    // 创建一个日志记录器
    auto logger = spdlog::stdout_color_mt("logger");
    // 使用宏进行日志记录
    LOG_INFO(logger, "这是一条信息消息");
    LOG_WARN(logger, "这是一条警告消息");
    LOG_ERROR(logger, "这是一条错误消息");
    return 0;
}

这种方法允许你用类似 LOG(INFO) 的方式来记录日志,只不过你需要指定要使用的日志记录器。这样做的好处是你可以根据需要轻松地定义不同类型的日志记录器,并在应用程序中灵活使用它们。

spdlog 本身没有提供 LOG(INFO) 这样的宏,因为它倾向于更明确的函数调用风格,这在C++中是更常见的做法。但是,通过自定义宏,你可以很容易地实现类似的功能。

spdlog包裹类

可以为您创建一个头文件,以封装 spdlog 的日志接口,使其可以以 LOG(INFO)LOG(ERROR) 的形式使用。这个头文件将定义必要的宏,并设置一个默认的日志记录器。

创建一个名为 SpdlogWrapper.h 的头文件,并添加以下内容:

#ifndef SPDLOG_WRAPPER_H
#define SPDLOG_WRAPPER_H
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <memory>
#include <sstream>
// 初始化默认日志记录器
inline std::shared_ptr<spdlog::logger> InitDefaultLogger() {
    auto logger = spdlog::stdout_color_mt("default_logger");
    logger->set_level(spdlog::level::debug); // 设置默认日志级别
    return logger;
}
// 获取默认日志记录器
inline std::shared_ptr<spdlog::logger>& DefaultLogger() {
    static auto logger = InitDefaultLogger();
    return logger;
}
// 定义LOG宏
enum class LogLevel {
    INFO,
    WARN,
    ERROR,
    DEBUG,
    TRACE
};
class LogMessage {
public:
    LogMessage(LogLevel level) : level_(level) {}
    ~LogMessage() {
        auto logger = DefaultLogger();
        switch (level_) {
            case LogLevel::INFO:
                logger->info(stream_.str());
                break;
            case LogLevel::WARN:
                logger->warn(stream_.str());
                break;
            case LogLevel::ERROR:
                logger->error(stream_.str());
                break;
            case LogLevel::DEBUG:
                logger->debug(stream_.str());
                break;
            case LogLevel::TRACE:
                logger->trace(stream_.str());
                break;
        }
    }
    std::ostringstream& stream() { return stream_; }
private:
    LogLevel level_;
    std::ostringstream stream_;
};
#define LOG(level) LogMessage(LogLevel::level).stream()
#endif // SPDLOG_WRAPPER_H

包裹类的改进

在提供的示例中,每次使用 LOG 宏时,都会创建一个 LogMessage 实例,这确实会带来一定的开销。不过,由于 spdlog 的设计非常高效,对于大多数场景,这种额外开销通常不会成为性能瓶颈。

但是,如果您的应用程序对性能要求非常高,尤其是在高频日志记录的场景下,这种方法可能不是最优的。在这种情况下,可以考虑更直接地使用 spdlog 的 API,或者设计一种更高效的宏实现。

为了改进性能,您可以考虑以下策略:

  1. 使用全局或静态日志记录器:减少创建和销毁日志记录器的次数,使用全局或静态实例。
  2. 直接调用 spdlog 的函数:直接使用 spdloginfo, warn, error 等函数,而不是通过宏和中间类。
  3. 条件编译:对于调试或追踪日志,可以通过预处理器指令(如 #ifdef DEBUG)在编译时禁用它们,以减少生产环境中的开销。

如果性能是一个关键因素,建议进行适当的基准测试,以评估不同方法在您的具体用例中的性能表现。在大多数情况下,spdlog 的性能已经足够优秀,不太可能成为瓶颈。

静态日志记录器

要将 LogMessage 类修改为使用静态的日志记录器,我们需要改变日志记录器的初始化和存储方式。这样可以确保日志记录器在应用程序的整个生命周期内只被创建一次。下面是修改后的 SpdlogWrapper.h 文件内容:

#ifndef SPDLOG_WRAPPER_H
#define SPDLOG_WRAPPER_H
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <memory>
#include <sstream>
// 初始化并获取静态日志记录器
inline std::shared_ptr<spdlog::logger>& GetStaticLogger() {
    static auto logger = spdlog::stdout_color_mt("default_logger");
    logger->set_level(spdlog::level::debug); // 设置默认日志级别
    return logger;
}
// 定义LOG宏
enum class LogLevel {
    INFO,
    WARN,
    ERROR,
    DEBUG,
    TRACE
};
class LogMessage {
public:
    LogMessage(LogLevel level) : level_(level) {}
    ~LogMessage() {
        auto logger = GetStaticLogger();
        switch (level_) {
            case LogLevel::INFO:
                logger->info(stream_.str());
                break;
            case LogLevel::WARN:
                logger->warn(stream_.str());
                break;
            case LogLevel::ERROR:
                logger->error(stream_.str());
                break;
            case LogLevel::DEBUG:
                logger->debug(stream_.str());
                break;
            case LogLevel::TRACE:
                logger->trace(stream_.str());
                break;
        }
    }
    std::ostringstream& stream() { return stream_; }
private:
    LogLevel level_;
    std::ostringstream stream_;
};
#define LOG(level) LogMessage(LogLevel::level).stream()
#endif // SPDLOG_WRAPPER_H

在这个修改中:

  • GetStaticLogger 函数负责初始化和返回一个静态的日志记录器实例。
  • LogMessage 类在其析构函数中使用这个静态日志记录器来记录消息。
  • LOG 宏的行为保持不变。

这种方式确保日志记录器作为一个静态局部变量初始化,从而在第一次调用 GetStaticLogger 时创建,并在程序的剩余生命周期内复用。这样减少了对象创建和销毁的开销,同时保留了简洁的日志记录语法。

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

目录
相关文章
|
NoSQL 编译器 Linux
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解(三)
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解
279 0
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解(三)
|
自然语言处理 编译器 Linux
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解(二)
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解
542 0
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解(二)
|
存储 Ubuntu Unix
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解(一)
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解
325 0
【三、深入浅出GCC编译器】一个源文件到可执行文件是如何生成的:GCC编译工具链及编译参数详解(一)
|
Shell 编译器 Linux
zlib-1.2.11库、libpng-1.6.36库编译及交叉编译 —— 附带shell编译脚本及源码
zlib-1.2.11库、libpng-1.6.36库编译及交叉编译 —— 附带shell编译脚本及源码
487 0
zlib-1.2.11库、libpng-1.6.36库编译及交叉编译 —— 附带shell编译脚本及源码
|
Java Linux 算法框架/工具
Linux下gcc编译依赖库可能出现的系列问题以及对应解决方法(以GDAL库为例)
Linux下gcc编译依赖库可能出现的系列问题以及对应解决方法(以GDAL库为例)
431 0
Linux下gcc编译依赖库可能出现的系列问题以及对应解决方法(以GDAL库为例)
|
Linux
LINUX编译spandsp
LINUX编译spandsp
121 0
|
Ubuntu Linux
LINUX UBUNTU安装依赖库编译freeswitch
LINUX UBUNTU安装依赖库编译freeswitch
218 0
|
Linux
LINUX下载编译nasm
LINUX下载编译nasm
302 0
|
数据库连接 Linux 网络安全
LINUX下载编译FreeSwitch
LINUX下载编译FreeSwitch
77 0
|
前端开发 关系型数据库 MySQL
为tinycolinux创建应用包-toolchain和编译方法
本文关键字:tinycorelinux编译gcc套件,live,vhd二合一colinux,tinycorelinux lnmp
321 0
为tinycolinux创建应用包-toolchain和编译方法