c++跨平台实现日志重定向

目录

使用场景

  1. 需要保存日志到本地,以便在程序异常时Debug
  2. 使用供应商的代码库,无法另存其日志

示例代码

新建一个文件命名为redirect_log.cpp,并将以下代码拷入文件。

cpp 复制代码
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>

// 跨平台头文件
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#else
#include <unistd.h>
#include <fcntl.h>
#endif

// 全局变量:保存原始标准输出/标准错误的文件描述符
static int OriginalStdout = -1;
static int OriginalStderr = -1;
static FILE* LogFilePtr = nullptr;

/**
 * @brief 开启日志重定向(控制台 + 文件双输出,无缓冲模式)
 * @param logPath 日志文件路径
 * @return 成功返回 true,失败返回 false
 */
static bool StartLogRedirect(const std::string& logPath) {
    // 如果日志文件已打开,先关闭
    if (LogFilePtr != nullptr) {
        fclose(LogFilePtr);
        LogFilePtr = nullptr;
    }

    // 直接用 C 文件接口打开,彻底避免 C++ fstream 兼容性问题
    LogFilePtr = fopen(logPath.c_str(), "a+");
    if (LogFilePtr == nullptr) {
        std::cerr << "[Error] Failed to open log file: " << logPath << std::endl;
        return false;
    }

    // ====================== 核心:关闭所有缓冲区 ======================
    // 关闭 C++ 流的缓冲区
    std::cout.rdbuf()->pubsetbuf(nullptr, 0);
    std::cerr.rdbuf()->pubsetbuf(nullptr, 0);

    // 关闭 C 标准IO的缓冲区,确保无换行也能立即输出
    setvbuf(stdout, nullptr, _IONBF, 0);
    setvbuf(stderr, nullptr, _IONBF, 0);
    setvbuf(LogFilePtr, nullptr, _IONBF, 0);
    // =================================================================

    // 保存原始 stdout/stderr 文件描述符
#ifdef _WIN32
    OriginalStdout = _dup(_fileno(stdout));
    OriginalStderr = _dup(_fileno(stderr));
#else
    OriginalStdout = dup(fileno(stdout));
    OriginalStderr = dup(fileno(stderr));
#endif

    if (OriginalStdout == -1 || OriginalStderr == -1) {
        std::cerr << "[Error] Failed to save original file descriptors" << std::endl;
        return false;
    }

    // 将标准输出/标准错误重定向到日志文件
#ifdef _WIN32
    _dup2(_fileno(LogFilePtr), _fileno(stdout));
    _dup2(_fileno(LogFilePtr), _fileno(stderr));
#else
    dup2(fileno(LogFilePtr), STDOUT_FILENO);
    dup2(fileno(LogFilePtr), STDERR_FILENO);
#endif

    // 同步 C/C++ 流
    std::ios::sync_with_stdio(true);
    std::cout.clear();
    std::cerr.clear();

    std::cout << "========================================" << std::endl;
    std::cout << "Log redirection started, output file: " << logPath << std::endl;
    std::cout << "========================================" << std::endl;

    return true;
}

/**
 * @brief 停止日志重定向,恢复控制台原始输出
 */
static void StopLogRedirect() {
    if (LogFilePtr == nullptr) {
        return;
    }

    // 恢复原始的标准输出/标准错误
#ifdef _WIN32
    _dup2(OriginalStdout, _fileno(stdout));
    _dup2(OriginalStderr, _fileno(stderr));
    _close(OriginalStdout);
    _close(OriginalStderr);
#else
    dup2(OriginalStdout, STDOUT_FILENO);
    dup2(OriginalStderr, STDERR_FILENO);
    close(OriginalStdout);
    close(OriginalStderr);
#endif

    // 关闭日志文件
    fclose(LogFilePtr);
    LogFilePtr = nullptr;

    std::cout << "\n========================================" << std::endl;
    std::cout << "Log redirection stopped, console output restored" << std::endl;
    std::cout << "========================================" << std::endl;
}

// 测试主函数
int main() {
    // 开启重定向
    StartLogRedirect("runtime.log");

    // 测试:没有换行也能立即输出
    std::cout << "[cout] No newline message --> ";
    std::cerr << "[cerr] No newline message --> ";
    printf("[printf] No newline message --> ");

    // 连续输出,实时写入文件
    std::cout << "Test message 1 ";
    std::cout << "Test message 2 ";
    std::cerr << "Error message ";

    // 循环输出测试
    for (int i = 0; i < 3; ++i) {
        std::cout << "\nLoop count: " << i;
    }

    // 停止重定向,恢复控制台
    StopLogRedirect();

    // 仅输出到控制台
    std::cout << "\n[After Restore] This message only shows on console" << std::endl;

    return 0;
}

编译代码

执行以下命令编译代码

powershell 复制代码
g++ redirect_log.cpp -o log.exe -std=c++11

功能测试

使用以下命令执行应用程序

powershell 复制代码
$ ./log.exe

控制台输出如下

html 复制代码
========================================
Log redirection stopped, console output restored
========================================

[After Restore] This message only shows on console

日志文件runtime.log输出如下

html 复制代码
========================================
Log redirection started, output file: runtime.log
========================================
[cout] No newline message --> [cerr] No newline message --> [printf] No newline message --> Test message 1 Test message 2 Error message 
Loop count: 0
Loop count: 1
Loop count: 2
相关推荐
企鹅的蚂蚁30 分钟前
LubanCat RK3588 实时 Linux 开发(六):启用 gs_usb 驱动并完成 USB-CAN 收发
linux·rk3588·linux驱动·socketcan·gs_usb·usb-can
2601_9622998842 分钟前
Linux执行Python脚本方法
linux·python·脚本·解释器·shebang
snow@li1 小时前
服务器运维:Linux命令速查手册(全景梳理)
linux·服务器·网络
handler011 小时前
【Linux】线程与虚拟内存的底层世界
linux·运维·服务器·c++·线程·虚拟地址空间·共享内存
郝学胜-神的一滴1 小时前
C++11 工程级应用 09:告别无谓拷贝,解锁高性能移动语义
开发语言·数据结构·c++·vscode·软件工程·visual studio
新时代牛马1 小时前
实时 Linux:PREEMPT_RT、延迟来源与观测
linux·运维·服务器
微露清风2 小时前
# Linux 进程全景:从 PCB 到 O(1) 调度队列
linux·服务器
H_oRIZoN_2 小时前
Linux入门DAY38(手写 HTTP 客户端抓取天气 + 简易 HTTP 服务器实现元器件 Web 系统)
linux·linux应用编程
fangjianj2 小时前
windows环境反弹shell
linux·windows·web安全
有味道的男人2 小时前
得物详情 API|支持实时价格、成色、鉴别服务数据
windows·python·api