深入解析高性能日志系统设计与实现

引言

在大型软件开发中,日志系统往往被当作"辅助工具"而轻视,直到线上问题出现、性能瓶颈难以定位、崩溃现场无法复现时,我们才会意识到一个完善的日志系统有多么重要。本文将深入解析一个核心库设计的工业级C++日志系统,它不仅提供了基础的日志记录功能,还集成了性能分析、崩溃处理、模块化控制等特性。

一、设计理念与架构概述

1.1 核心设计目标

这个日志系统的设计围绕以下几个核心理念展开:

  • 性能至上:采用异步日志、编译期优化、阈值过滤等多重手段
  • 模块化控制:支持按模块独立开关日志,便于生产环境部署
  • 可观测性:内置性能分析工具,支持代码级耗时统计
  • 健壮性:完善的崩溃处理机制,确保关键信息不丢失
  • 易用性:简洁的宏接口,RAII风格的设计模式

1.2 整体架构

系统采用分层架构设计,从上到下依次是:

┌─────────────────────────────────────┐
│       应用层(宏接口)               │
│  PDFCORE_INFO / FONTMANAGER_DEBUG   │
├─────────────────────────────────────┤
│       工具层(性能分析)              │
│  ScopeProfiler / AccumulativeProfiler│
├─────────────────────────────────────┤
│       核心层(日志管理器)            │
│     LoggerManager (单例)             │
├─────────────────────────────────────┤
│       底层(spdlog + 崩溃处理)       │
│   异步Sink / 信号处理 / SEH处理       │
└─────────────────────────────────────┘

二、核心模块详解

2.1 编译期开关设计

// 编译期开关
#define ENABLE_PDFCORE_DEBUG        0
#define ENABLE_FONTMANAGER_DEBUG    0
#define ENABLE_COMPRESSION_DEBUG    0
#define ENABLE_IMAGE_DEBUG          0

这种设计有两大优势:

  1. 零开销抽象:当开关关闭时,所有日志宏展开为空,编译器可以完全优化掉相关代码,不会产生任何运行时开销。

  2. 细粒度控制:可以为PDF解析核心、字体管理、压缩算法、图像处理等不同模块独立开关日志,便于在调试特定模块时减少日志干扰。

2.2 RAII风格的作用域性能分析器

class ScopeProfiler {
    // 构造函数记录开始时间
    // 析构函数自动计算并记录耗时
};

这是一个典型的RAII应用:

  • 自动计时:对象创建时开始计时,销毁时自动结束并记录
  • 阈值过滤:可以设置时间阈值,只有超过阈值的操作才被记录
  • 无侵入性:只需要在代码块开始处声明对象即可

使用示例:

void process_pdf_page() {
    PDFCORE_PROFILE_SCOPE(PageProcessing);
    // 处理PDF页面的代码...
} // 离开作用域时自动输出执行时间

2.3 线程安全的累积性能分析器

class AccumulativeProfiler {
    std::atomic<double> total_;
    std::atomic<uint64_t> calls_;
};

这个类解决了一个常见痛点:统计多次调用的性能数据。比如需要统计"解析1000个PDF页面平均耗时"时,可以:

PDFCORE_PROFILE_ACC_DECLARE(PageParse);

void parse_page() {
    PDFCORE_PROFILE_ACC_START(PageParse);
    // 解析逻辑...
    PDFCORE_PROFILE_ACC_END(PageParse);
}

void report() {
    PDFCORE_PROFILE_ACC_REPORT(PageParse, LogLevel::INFO);
}

2.4 多文件日志管理器

LoggerManager采用单例模式,初始化时会创建四个独立的日志文件:

日志文件 记录级别 用途
full.log TRACE ~ CRITICAL 完整调试信息
error.log ERROR, CRITICAL 错误监控
warn.log WARN及以上 警告预警
perf.log INFO 性能数据

这种分离设计的优势:

  • 快速定位问题:查找错误时不需要在庞大的日志文件中翻找
  • 磁盘空间优化:错误日志通常较小,可以保留更长时间
  • 监控系统集成:可以单独监控error.log文件

2.5 跨平台崩溃处理机制

崩溃处理是生产环境系统的必备功能。这个实现同时支持三种崩溃类型:

class CrashHandler {
    // 1. 未捕获的C++异常
    std::set_terminate()
    
    // 2. 致命信号(Linux/macOS)
    std::signal(SIGSEGV)  // 段错误
    std::signal(SIGABRT)  // 异常终止
    std::signal(SIGFPE)   // 浮点异常
    std::signal(SIGILL)   // 非法指令
    
    // 3. Windows结构化异常(SEH)
    SetUnhandledExceptionFilter()
};

当程序崩溃时,系统会:

  1. 记录崩溃原因和详细信息
  2. 输出完整的调用堆栈(使用Boost.Stacktrace)
  3. 刷新所有日志缓冲区
  4. 优雅退出

三、spdlog的高级应用

3.1 异步日志优化

spdlog::init_thread_pool(8192, 2);
  • 8192:队列大小,可根据日志量调整
  • 2:后台线程数,平衡CPU核心利用

异步日志将日志写入操作转移到后台线程,主线程只需将日志消息放入队列,大幅降低了日志对业务逻辑的性能影响。

3.2 多Sink组合设计

auto logger = std::make_shared<spdlog::async_logger>(
    "pdfcore",
    spdlog::sinks_init_list{
        console_sink,    // 控制台输出
        full_sink,       // 完整日志
        error_sink,      // 错误日志
        warn_sink,       // 警告日志
        perf_sink        // 性能日志
    },
    // ...
);

每个sink可以独立设置:

  • 日志格式:full日志包含文件名、行号、线程ID,error日志保持简洁
  • 日志级别:error sink只记录ERROR及以上级别
  • 输出目标:控制台、文件、网络等

四、智能的日志宏设计

4.1 条件编译宏

#if ENABLE_PDFCORE_DEBUG
#define PDFCORE_INFO(...) SPDLOG_LOGGER_CALL(...)
#else
#define PDFCORE_INFO(...)
#endif

这种设计确保:

  • 生产环境:所有日志宏展开为空,零开销
  • 开发环境:通过修改头文件中的宏定义即可开启日志

4.2 防重复日志宏

#define PDFCORE_LOG_ONCE(lv, ...) \
    do { static bool _once=false; \
    if(!_once){_once=true; PDFCORE_LOG(lv,__VA_ARGS__);} } while(0)

#define PDFCORE_LOG_EVERY_N(n, lv, ...) \
    do { static std::atomic<uint32_t> _c{0}; \
    if((_c.fetch_add(1)%(n))==0) PDFCORE_LOG(lv,__VA_ARGS__);} while(0)

这两个宏解决了高频日志的痛点:

  • LOG_ONCE:初始化信息、警告提示等只需记录一次的场景
  • LOG_EVERY_N:循环中每N次记录一次,既保留信息又避免刷屏

五、实际应用场景

5.1 性能瓶颈定位

void render_complex_page() {
    PDFCORE_PROFILE_SCOPE(RenderPage);
    
    PDFCORE_PROFILE_SCOPE(ParseContent);
    parse_page_content();
    
    PDFCORE_PROFILE_SCOPE(RenderGraphics);
    render_vector_graphics();
    
    PDFCORE_PROFILE_SCOPE(RenderText);
    render_text_elements();
}

通过嵌套的作用域分析,可以精确定位哪个环节最耗时。

5.2 内存问题排查

void process_large_image() {
    IMAGE_DEBUG("Processing image: {}x{}, {} bytes", 
                width, height, file_size);
    
    auto* buffer = allocate_memory(size);
    IMAGE_TRACE("Buffer allocated at: {:p}, size: {}", 
                (void*)buffer, size);
    
    // 处理图像...
    
    free(buffer);
    IMAGE_TRACE("Buffer freed");
}

开启IMAGE_DEBUG时,可以追踪内存分配和释放,帮助排查内存泄漏。

5.3 线上问题诊断

try {
    parse_pdf_file(filename);
} catch (const std::exception& e) {
    PDFCORE_ERROR("Failed to parse PDF: {}, error: {}", 
                  filename, e.what());
    // 记录额外上下文信息
    PDFCORE_LOG_ONCE(LogLevel::WARN, 
                     "This may be caused by corrupted file");
}

六、性能优化技巧

6.1 编译期优化

// 取消不需要的元数据
#define SPDLOG_NO_THREAD_ID
#define SPDLOG_NO_DATETIME

在不需要线程ID和时间戳的场景,可以通过这些宏减少日志格式化的开销。

6.2 阈值过滤

class ScopeProfiler {
    void log_if_needed() {
        if (threshold_ > 0.0 && elapsed < threshold_)
            return;  // 低于阈值不记录
        // 记录日志...
    }
};

避免记录大量短时间操作,只关注真正耗时的性能瓶颈。

6.3 异步刷新策略

// 错误级别立即刷新
spdlog::flush_on(spdlog::level::err);

// 定期刷新(可选)
std::thread flusher([]{
    while(running) {
        std::this_thread::sleep_for(1s);
        spdlog::default_logger()->flush();
    }
});

错误日志立即刷新确保不丢失关键信息,普通日志批量写入提高性能。

七、最佳实践建议

7.1 日志级别使用指南

级别 使用场景 示例
TRACE 最详细的执行流程 函数进入/退出、变量值变化
DEBUG 调试信息 算法中间结果、数据结构状态
INFO 关键业务流程 文件打开、处理完成
WARN 潜在问题 兼容性警告、降级处理
ERROR 可恢复错误 解析失败、资源不足
CRITICAL 致命错误 内存损坏、不可恢复异常

7.2 生产环境配置建议

// 生产环境通常关闭DEBUG日志
#define ENABLE_PDFCORE_DEBUG 0

// 但保留WARN和ERROR级别
LoggerManager::instance().init_async_file(
    "logs/pdfcore.log", 
    10 * 1024 * 1024,  // 10MB
    5,                  // 保留5个文件
    LogLevel::WARN      // 只记录WARN及以上
);

7.3 性能敏感代码处理

void high_frequency_function() {
    // 避免在高频函数中记录INFO日志
    // PDFCORE_INFO("Enter function"); // 不要这样做
    
    // 使用EVERY_N宏控制频率
    PDFCORE_LOG_EVERY_N(1000, LogLevel::DEBUG, 
                        "Processed {} items", count);
    
    // 或者使用ONCE宏
    PDFCORE_LOG_ONCE(LogLevel::INFO, 
                     "First call initialization");
}

八、总结与展望

这个日志系统的设计充分体现了工业级C++项目的工程智慧:通过编译期开关实现零开销抽象,通过RAII简化性能分析,通过多文件分离提高可观测性,通过崩溃处理增强健壮性。

在实际应用中,这样的日志系统能够显著提升开发和运维效率:

  • 开发阶段:快速定位问题,精确测量性能
  • 测试阶段:全面记录执行轨迹,辅助自动化分析
  • 生产环境:故障可追溯,性能可监控,崩溃可还原

未来可以考虑的扩展方向:

  • 支持日志采样,进一步降低性能开销
  • 集成分布式追踪ID,便于微服务架构
  • 提供日志分析工具,自动检测异常模式
  • 支持动态开关日志,无需重启服务

日志系统就像飞机的黑匣子,平时默默无闻,关键时刻却能提供无可替代的价值。希望本文的分析能帮助读者在自己的项目中构建更好的日志系统。

/*
 * Copyright (c) 2025 Sylar Ding
 *
 * All rights reserved.
 *
 * This source file is part of a core runtime logging and profiling system.
 * Unauthorized copying, modification, or distribution of this file,
 * via any medium, is strictly prohibited without prior written permission.
 */


 // 防止头文件被重复包含
#pragma once

// =====================================================
// 编译期开关
// 这些宏定义允许在编译时按模块开启或关闭调试日志功能
// =====================================================
// 控制PDF核心模块的调试日志
#define ENABLE_PDFCORE_DEBUG        0
// 控制字体管理模块的调试日志
#define ENABLE_FONTMANAGER_DEBUG    0
// 控制压缩模块的调试日志
#define ENABLE_COMPRESSION_DEBUG    0
// 控制图像处理模块的调试日志
#define ENABLE_IMAGE_DEBUG          0

// spdlog 编译期优化(可按需启用)
// 以下宏定义可以优化spdlog的性能和输出格式
// #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
// #define SPDLOG_NO_THREAD_ID
// #define SPDLOG_NO_DATETIME

// =====================================================
// 包含spdlog日志库的相关头文件
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/stopwatch.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/daily_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>

// 包含标准库头文件
#include <atomic>
#include <chrono>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include <boost/stacktrace.hpp>
#include <csignal>   // 必须添加此头文件以支持 std::signal 和 SIGSEGV 等宏
#ifdef _WIN32
#include <windows.h>
#include <direct.h>  // 用于 _mkdir
#else
#include <sys/stat.h> // 用于 mkdir
#include <sys/types.h>
#endif


// =====================================================
// PDF核心库的日志命名空间
namespace pdfcore::logging {

    // =====================================================
    // 日志级别枚举
    // 包装spdlog的日志级别,避免Windows平台ERROR宏定义冲突
    // =====================================================
    enum class LogLevel : int {
        TRACE = SPDLOG_LEVEL_TRACE,     // 最详细的跟踪信息
        DEBUG = SPDLOG_LEVEL_DEBUG,     // 调试信息
        INFO = SPDLOG_LEVEL_INFO,       // 一般信息
        WARN = SPDLOG_LEVEL_WARN,       // 警告信息
        ERROR_ = SPDLOG_LEVEL_ERROR,    // 错误信息(注意下划线避免冲突)
        CRITICAL = SPDLOG_LEVEL_CRITICAL, // 严重错误
        OFF = SPDLOG_LEVEL_OFF    // 关闭所有日志
    };

    // =====================================================
    // RAII作用域性能分析器
    // 自动记录代码块的执行时间并在析构时输出
    // =====================================================
    class ScopeProfiler {
    public:
        // 构造函数,记录开始时间
        ScopeProfiler(std::string tag,
            LogLevel level = LogLevel::DEBUG,
            double threshold_sec = 0.0)
            : tag_(std::move(tag)),
            level_(level),
            threshold_(threshold_sec),
            start_(clock::now()) {
        }

        // 析构函数,自动记录执行时间
        ~ScopeProfiler() { log_if_needed(); }

        // 手动停止并记录
        void stop_and_log() {
            log_if_needed();
            stopped_ = true;
        }

    private:
        using clock = std::chrono::high_resolution_clock; // 使用高精度时钟

        // 根据阈值判断是否需要记录日志
        void log_if_needed() {
            if (stopped_) return; // 如果已手动停止,则不重复记录

            // 计算从开始到现在的耗时
            const double elapsed =
                std::chrono::duration<double>(clock::now() - start_).count();

            // 如果设置了时间阈值且耗时未超过阈值,则不记录
            if (threshold_ > 0.0 && elapsed < threshold_)
                return;

            // 调用spdlog记录性能信息
            SPDLOG_LOGGER_CALL(
                spdlog::default_logger(),
                static_cast<spdlog::level::level_enum>(level_),
                "[PERF] [{}] {:.6f}s", tag_, elapsed);
        }

        // 成员变量
        std::string tag_;             // 性能分析标签
        LogLevel level_;              // 日志级别
        double threshold_;           // 时间阈值(秒),超过此阈值才记录
        clock::time_point start_;    // 开始时间点
        bool stopped_{ false };      // 是否已手动停止
    };

    // =====================================================
    // 累积性能分析器(线程安全)
    // 用于统计多次调用的总耗时和平均耗时
    // =====================================================
    class AccumulativeProfiler {
    public:
        // 构造函数
        explicit AccumulativeProfiler(std::string tag)
            : tag_(std::move(tag)) {
        }

        // 开始计时
        void start() {
            start_ = clock::now();
        }

        // 停止计时并累积数据
        void stop() {
            // 计算单次耗时
            const double elapsed =
                std::chrono::duration<double>(clock::now() - start_).count();

            // 线程安全地更新累积数据
            std::lock_guard<std::mutex> lock(mutex_);
            total_ += elapsed;
            calls_++;
        }

        // 重置统计信息
        void reset() {
            std::lock_guard<std::mutex> lock(mutex_);
            total_ = 0.0;
            calls_ = 0;
        }

        // 输出统计报告
        void report(LogLevel level = LogLevel::INFO) const {
            std::lock_guard<std::mutex> lock(mutex_);
            if (calls_ == 0) return; // 没有调用记录时不输出

            // 记录累积性能信息
            SPDLOG_LOGGER_CALL(
                spdlog::default_logger(),
                static_cast<spdlog::level::level_enum>(level),
                "[PERF-ACC] [{}] calls={}, total={:.3f}s avg={:.3f}s",
                tag_, calls_, total_, total_ / calls_);
        }

    private:
        using clock = std::chrono::high_resolution_clock; // 高精度时钟

        // 成员变量
        std::string tag_;             // 分析器标签
        mutable std::mutex mutex_;    // 保护统计数据的互斥锁
        double total_{ 0.0 };         // 总耗时
        uint64_t calls_{ 0 };         // 调用次数
        clock::time_point start_;     // 单次计时的开始时间
    };

    // =====================================================
    // 日志管理器
    // 单例类,负责初始化和管理spdlog日志系统
    // =====================================================
    class LoggerManager {
    public:
        // 获取单例实例
        static LoggerManager& instance() {
            static LoggerManager inst;
            return inst;
        }

        // =====================================================
        // 初始化异步文件日志(多文件版本)
        // 自动创建完整日志、错误日志、警告日志和性能日志
        // =====================================================
        bool init_async_file(const std::string& file,
            size_t max_size = 5 * 1024 * 1024,   // 单个日志文件最大5MB
            size_t max_files = 3,                // 最多保留3个日志文件
            LogLevel level = LogLevel::DEBUG) {  // 默认日志级别

            try {
                // 初始化异步线程池
                spdlog::init_thread_pool(8192, 2);

                // 创建控制台sink
                auto console_sink =
                    std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
                console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
                console_sink->set_level(spdlog::level::trace);

                // 解析基础文件名和路径
                std::string base_path = file;
                std::string ext = ".log";
                size_t dot_pos = file.find_last_of('.');
                if (dot_pos != std::string::npos) {
                    base_path = file.substr(0, dot_pos);
                    ext = file.substr(dot_pos);
                }

                // 确保日志目录存在
                std::string dir_path = extract_directory(base_path);
                if (!dir_path.empty()) {
                    create_directory(dir_path);
                }

                // =====================================================
                // 1. 完整日志sink - 记录所有级别(包含详细信息)
                // =====================================================
                auto full_sink = std::make_shared<
                    spdlog::sinks::rotating_file_sink_mt>(
                        base_path + "_full" + ext, max_size, max_files);

                // 设置完整日志的格式(包含文件名、行号、线程ID)
                full_sink->set_pattern(
                    "[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%s:%#] [t:%t] %v");
                full_sink->set_level(spdlog::level::trace);

                // =====================================================
                // 2. 错误日志sink - 只记录ERROR和CRITICAL
                // =====================================================
                auto error_sink = std::make_shared<
                    spdlog::sinks::rotating_file_sink_mt>(
                        base_path + "_error" + ext, max_size, max_files);

                // 设置错误日志格式(简洁版本)
                error_sink->set_pattern(
                    "[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
                error_sink->set_level(spdlog::level::err);  // 只记录err及以上

                // =====================================================
                // 3. 警告日志sink - 只记录WARN和以上级别
                // =====================================================
                auto warn_sink = std::make_shared<
                    spdlog::sinks::rotating_file_sink_mt>(
                        base_path + "_warn" + ext, max_size, max_files);

                // 设置警告日志格式(简洁版本)
                warn_sink->set_pattern(
                    "[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
                warn_sink->set_level(spdlog::level::warn);  // 记录warn及以上

                // =====================================================
                // 4. 性能日志sink - 单独记录性能数据
                // =====================================================
                auto perf_sink = std::make_shared<
                    spdlog::sinks::rotating_file_sink_mt>(
                        base_path + "_perf" + ext, max_size, max_files);

                perf_sink->set_pattern(
                    "[%Y-%m-%d %H:%M:%S.%e] %v");
                perf_sink->set_level(spdlog::level::info);

                // 创建异步logger,组合所有sink
                auto logger = std::make_shared<spdlog::async_logger>(
                    "pdfcore",
                    spdlog::sinks_init_list{
                        console_sink,    // 控制台输出
                        full_sink,       // 完整日志文件
                        error_sink,      // 错误日志文件
                        warn_sink,       // 警告日志文件
                        perf_sink        // 性能日志文件
                    },
                    spdlog::thread_pool(),
                    spdlog::async_overflow_policy::block);

                // 设置logger的全局级别(控制哪些级别的日志会被处理)
                logger->set_level(
                    static_cast<spdlog::level::level_enum>(level));

                // 设置为默认logger
                spdlog::set_default_logger(logger);

                // 设置错误级别日志立即刷新
                spdlog::flush_on(spdlog::level::err);

                // 安装崩溃处理
                CrashHandler::install();

                initialized_ = true;

                // 记录初始化成功的日志
                logger->info("==========================================");
                logger->info("Logger initialized successfully");
                logger->info("Log files:");
                logger->info("  - Full log: {}{}", base_path, "_full" + ext);
                logger->info("  - Error log: {}{}", base_path, "_error" + ext);
                logger->info("  - Warning log: {}{}", base_path, "_warn" + ext);
                logger->info("  - Performance log: {}{}", base_path, "_perf" + ext);
                logger->info("Max file size: {} MB, Max files: {}", max_size / (1024 * 1024), max_files);
                logger->info("==========================================");

                return true;
            }
            catch (const std::exception& e) {
                // 初始化失败时回退到基本控制台logger
                try {
                    auto fallback = spdlog::stdout_color_mt("fallback");
                    fallback->error("Failed to initialize logger: {}", e.what());
                    spdlog::set_default_logger(fallback);
                }
                catch (...) {
                    // 最后的回退方案
                }
                return false;
            }
        }

        // =====================================================
        // 设置全局日志级别
        // =====================================================
        void set_level(LogLevel level) {
            spdlog::set_level(
                static_cast<spdlog::level::level_enum>(level));
        }

        // =====================================================
        // 刷新日志缓冲区
        // =====================================================
        void flush() {
            if (auto lg = spdlog::default_logger())
                lg->flush();
        }

        // =====================================================
        // 检查是否已初始化
        // =====================================================
        bool is_initialized() const {
            return initialized_;
        }

    private:
        // 私有构造函数,确保单例模式
        LoggerManager() = default;

        // 析构时关闭spdlog
        ~LoggerManager() {
            if (initialized_) {
                spdlog::shutdown();
            }
        }

        // =====================================================
        // 辅助函数:从文件路径中提取目录
        // =====================================================
        std::string extract_directory(const std::string& filepath) {
            size_t last_sep = filepath.find_last_of("/\\");
            if (last_sep != std::string::npos) {
                return filepath.substr(0, last_sep + 1);
            }
            return "";
        }

        // =====================================================
        // 辅助函数:创建目录(跨平台)
        // =====================================================
        void create_directory(const std::string& path) {
            if (path.empty()) return;

#ifdef _WIN32
            // Windows平台
            _mkdir(path.c_str());
#else
            // Linux/Unix平台
            mkdir(path.c_str(), 0755);
#endif
        }

        // =====================================================
        // CrashHandler 崩溃处理类
        // =====================================================
        class CrashHandler {
        public:
            static void install() {
                install_terminate();
                install_signals();
#ifdef _WIN32
                install_seh();
#endif
            }

        private:
            // 1️⃣ 处理未捕获异常
            static void install_terminate() {
                std::set_terminate([]() {
                    try {
                        auto ex = std::current_exception();
                        if (ex) std::rethrow_exception(ex);
                    }
                    catch (const std::exception& e) {
                        log("Unhandled std::exception", e.what());
                    }
                    catch (...) {
                        log("Unknown exception", "");
                    }
                    std::_Exit(EXIT_FAILURE);
                    });
            }

            // 2️⃣ Linux/macOS 信号
            static void install_signals() {
                std::signal(SIGSEGV, signal_handler);
                std::signal(SIGABRT, signal_handler);
                std::signal(SIGFPE, signal_handler);
                std::signal(SIGILL, signal_handler);
            }

            static void signal_handler(int sig) {
                log("Fatal signal", std::to_string(sig));
                std::_Exit(EXIT_FAILURE);
            }

#ifdef _WIN32
            // 3️⃣ Windows SEH
            static void install_seh() {
                SetUnhandledExceptionFilter(seh_handler);
            }

            static LONG WINAPI seh_handler(EXCEPTION_POINTERS*) {
                log("Windows SEH exception", "");
                return EXCEPTION_EXECUTE_HANDLER;
            }
#endif

            // 4️⃣ 输出堆栈
            static void log(const std::string& reason,
                const std::string& detail)
            {
                try {
                    auto logger = spdlog::default_logger();
                    if (!logger)
                        return;

                    logger->critical("========== CRASH DETECTED ==========");
                    logger->critical("Reason: {}", reason);

                    if (!detail.empty())
                        logger->critical("Detail: {}", detail);

                    logger->critical("Stacktrace:\n{}",
                        boost::stacktrace::to_string(boost::stacktrace::stacktrace()));

                    spdlog::apply_all([](std::shared_ptr<spdlog::logger> l) {
                        l->flush();
                        });
                }
                catch (...) {
                    // 崩溃日志记录失败时的静默处理
                }
            }
        };

        bool initialized_{ false }; // 标记是否已初始化
    };

    // =====================================================
    // 通用日志宏定义
    // 当ENABLE_PDFCORE_DEBUG开启时生效
    // =====================================================
#if ENABLE_PDFCORE_DEBUG

    // 基础日志宏,调用spdlog记录日志
#define PDFCORE_LOG(lv, ...) \
        SPDLOG_LOGGER_CALL( \
            spdlog::default_logger(), \
            static_cast<spdlog::level::level_enum>(lv), \
            __VA_ARGS__)

    // 各级别日志的快捷宏
#define PDFCORE_TRACE(...)    PDFCORE_LOG(pdfcore::logging::LogLevel::TRACE, __VA_ARGS__)
#define PDFCORE_DEBUG(...)    PDFCORE_LOG(pdfcore::logging::LogLevel::DEBUG, __VA_ARGS__)
#define PDFCORE_INFO(...)     PDFCORE_LOG(pdfcore::logging::LogLevel::INFO,  __VA_ARGS__)
#define PDFCORE_WARN(...)     PDFCORE_LOG(pdfcore::logging::LogLevel::WARN,  __VA_ARGS__)
#define PDFCORE_ERROR(...)    PDFCORE_LOG(pdfcore::logging::LogLevel::ERROR_,__VA_ARGS__)
#define PDFCORE_CRITICAL(...) PDFCORE_LOG(pdfcore::logging::LogLevel::CRITICAL,__VA_ARGS__)

// 只记录一次的日志宏
#define PDFCORE_LOG_ONCE(lv, ...) \
        do { static bool _once=false; \
        if(!_once){_once=true; PDFCORE_LOG(lv,__VA_ARGS__);} } while(0)

    // 每N次记录一次的日志宏
#define PDFCORE_LOG_EVERY_N(n, lv, ...) \
        do { static std::atomic<uint32_t> _c{0}; \
        if((_c.fetch_add(1)%(n))==0) PDFCORE_LOG(lv,__VA_ARGS__);} while(0)

    // 作用域性能分析宏
#define PDFCORE_PROFILE_SCOPE(name) \
        pdfcore::logging::ScopeProfiler _prof_##name(#name)

    // 带时间阈值的作用域性能分析宏
#define PDFCORE_PROFILE_SCOPE_MS(name, ms) \
        pdfcore::logging::ScopeProfiler _prof_##name(#name, \
            pdfcore::logging::LogLevel::DEBUG, (ms)/1000.0)

    // 累积性能分析器声明宏
#define PDFCORE_PROFILE_ACC_DECLARE(name) \
        static pdfcore::logging::AccumulativeProfiler _acc_##name(#name)

    // 累积性能分析器操作宏
#define PDFCORE_PROFILE_ACC_START(name) _acc_##name.start()
#define PDFCORE_PROFILE_ACC_END(name)   _acc_##name.stop()
#define PDFCORE_PROFILE_ACC_REPORT(name, lv) _acc_##name.report(lv)

#else
    // 当PDFCORE_DEBUG关闭时,所有日志宏定义为空
#define PDFCORE_TRACE(...)
#define PDFCORE_DEBUG(...)
#define PDFCORE_INFO(...)
#define PDFCORE_WARN(...)
#define PDFCORE_ERROR(...)
#define PDFCORE_CRITICAL(...)
#define PDFCORE_LOG(...)
#define PDFCORE_LOG_ONCE(...)
#define PDFCORE_LOG_EVERY_N(...)
#define PDFCORE_PROFILE_SCOPE(...)
#define PDFCORE_PROFILE_SCOPE_MS(...)
#define PDFCORE_PROFILE_ACC_DECLARE(...)
#define PDFCORE_PROFILE_ACC_START(...)
#define PDFCORE_PROFILE_ACC_END(...)
#define PDFCORE_PROFILE_ACC_REPORT(...)
#endif

    // =====================================================
    // 字体管理器专用日志宏
    // 当ENABLE_FONTMANAGER_DEBUG开启时生效
    // =====================================================
#if ENABLE_FONTMANAGER_DEBUG
#define FONTMANAGER_INFO(...) PDFCORE_INFO("[Font] " __VA_ARGS__)
#define FONTMANAGER_DEBUG(...) PDFCORE_DEBUG("[Font] " __VA_ARGS__)
#define FONTMANAGER_WARN(...)  PDFCORE_WARN ("[Font] " __VA_ARGS__)
#define FONTMANAGER_ERROR(...)  PDFCORE_ERROR ("[Font] " __VA_ARGS__)
#define FONTMANAGER_PROFILE_SCOPE(name) \
        PDFCORE_PROFILE_SCOPE(Font_##name)
#else
#define FONTMANAGER_INFO(...) 
#define FONTMANAGER_DEBUG(...)
#define FONTMANAGER_WARN(...)
#define FONTMANAGER_ERROR(...)
#define FONTMANAGER_PROFILE_SCOPE(...)
#endif

// =====================================================
// 压缩模块专用日志宏
// 当ENABLE_COMPRESSION_DEBUG开启时生效
// =====================================================
#if ENABLE_COMPRESSION_DEBUG
#define COMPRESSION_DEBUG(...) PDFCORE_DEBUG("[Compress] " __VA_ARGS__)
#define COMPRESSION_PROFILE_SCOPE(name) \
        PDFCORE_PROFILE_SCOPE(Compress_##name)
#else
#define COMPRESSION_DEBUG(...)
#define COMPRESSION_PROFILE_SCOPE(...)
#endif

// =====================================================
// 图像模块专用日志宏
// 当ENABLE_IMAGE_DEBUG开启时生效
// =====================================================
#if ENABLE_IMAGE_DEBUG
#define IMAGE_DEBUG(...) PDFCORE_DEBUG("[Image] " __VA_ARGS__)
#define IMAGE_TRACE(...) PDFCORE_TRACE("[Image] " __VA_ARGS__)
#define IMAGE_INFO(...)  PDFCORE_INFO("[Image] " __VA_ARGS__)
#define IMAGE_WARN(...)  PDFCORE_WARN("[Image] " __VA_ARGS__)
#define IMAGE_ERROR(...) PDFCORE_ERROR("[Image] " __VA_ARGS__)
#else
#define IMAGE_DEBUG(...)
#define IMAGE_TRACE(...)
#define IMAGE_INFO(...)
#define IMAGE_WARN(...)
#define IMAGE_ERROR(...)
#endif

} // namespace pdfcore::logging

 
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐