通过流重定向方式实现日志多路输出,设计为可扩展架构,支持动态添加多个输出目标(控制台、文件、调试窗口等)。其中C++方案更符合面向对象设计,而C方案则更轻量、更接近系统底层。
std::streambuf
实现流重定向MultiStreambuf
类将数据分发到多个目标缓冲区#include
#include
#include
#include
#include
#include
// 多重输出流缓冲区类
class MultiStreambuf : public std::streambuf {
private:
std::vector<std::streambuf*> buffers;
protected:
// 重写overflow函数,当缓冲区满时调用
int overflow(int c) override {
bool success = true;
for (auto buf : buffers) {
if (buf->sputc(c) == traits_type::eof()) {
success = false;
}
}
return success ? c : traits_type::eof();
}
// 重写sync函数,刷新缓冲区
int sync() override {
bool success = true;
for (auto buf : buffers) {
if (buf->pubsync() == -1) {
success = false;
}
}
return success ? 0 : -1;
}
public:
void addBuffer(std::streambuf* buf) {
if (buf) buffers.push_back(buf);
}
};
// 调试输出流缓冲区类
class DebugStreambuf : public std::streambuf {
protected:
int overflow(int c) override {
if (c != traits_type::eof()) {
char ch = static_cast<char>(c);
OutputDebugStringA(&ch);
}
return c;
}
int sync() override {
return 0;
}
};
// 日志管理器(单例模式)
class Logger {
private:
static Logger instance;
MultiStreambuf multiBuf;
std::streambuf* originalCoutBuf;
std::ofstream fileStream;
DebugStreambuf debugBuf;
Logger() : originalCoutBuf(std::cout.rdbuf()) {
// 默认输出到控制台
multiBuf.addBuffer(originalCoutBuf);
}
~Logger() {
// 恢复原始流缓冲区
std::cout.rdbuf(originalCoutBuf);
}
public:
static Logger& get() {
return instance;
}
void addConsoleOutput() {
multiBuf.addBuffer(originalCoutBuf);
}
void addFileOutput(const std::string& filename) {
fileStream.open(filename, std::ios::out | std::ios::app);
if (fileStream.is_open()) {
multiBuf.addBuffer(fileStream.rdbuf());
}
}
void addDebugOutput() {
multiBuf.addBuffer(&debugBuf);
}
void startLogging() {
std::cout.rdbuf(&multiBuf);
}
};
Logger Logger::instance;
// 使用示例
int main() {
Logger::get().addFileOutput("D:/res/log.txt");
Logger::get().addDebugOutput();
Logger::get().startLogging();
std::cout << "Hello, World!" << std::endl;
return 0;
}
Logger_Log
函数实现格式化输出#include
#include
#include
#include
// 输出目标类型
typedef enum {
OUTPUT_CONSOLE,
OUTPUT_FILE,
OUTPUT_DEBUG
} OutputType;
// 输出目标结构体
typedef struct {
OutputType type;
union {
FILE* file;
};
} OutputTarget;
// 日志管理器结构体
typedef struct {
OutputTarget* targets;
int count;
int capacity;
} Logger;
// 创建日志管理器
Logger* Logger_Create() {
Logger* logger = (Logger*)malloc(sizeof(Logger));
logger->count = 0;
logger->capacity = 4;
logger->targets = (OutputTarget*)malloc(sizeof(OutputTarget) * logger->capacity);
return logger;
}
// 销毁日志管理器
void Logger_Destroy(Logger* logger) {
for (int i = 0; i < logger->count; i++) {
if (logger->targets[i].type == OUTPUT_FILE) {
fclose(logger->targets[i].file);
}
}
free(logger->targets);
free(logger);
}
// 添加控制台输出
void Logger_AddConsole(Logger* logger) {
if (logger->count >= logger->capacity) {
logger->capacity *= 2;
logger->targets = (OutputTarget*)realloc(
logger->targets, sizeof(OutputTarget) * logger->capacity);
}
logger->targets[logger->count].type = OUTPUT_CONSOLE;
logger->count++;
}
// 添加文件输出
void Logger_AddFile(Logger* logger, const char* filename) {
if (logger->count >= logger->capacity) {
logger->capacity *= 2;
logger->targets = (OutputTarget*)realloc(
logger->targets, sizeof(OutputTarget) * logger->capacity);
}
OutputTarget target;
target.type = OUTPUT_FILE;
target.file = fopen(filename, "a");
if (target.file) {
logger->targets[logger->count] = target;
logger->count++;
}
}
// 添加调试输出
void Logger_AddDebug(Logger* logger) {
if (logger->count >= logger->capacity) {
logger->capacity *= 2;
logger->targets = (OutputTarget*)realloc(
logger->targets, sizeof(OutputTarget) * logger->capacity);
}
logger->targets[logger->count].type = OUTPUT_DEBUG;
logger->count++;
}
// 日志输出函数
void Logger_Log(Logger* logger, const char* format, ...) {
va_list args;
char buffer[1024];
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
for (int i = 0; i < logger->count; i++) {
OutputTarget* target = &logger->targets[i];
switch (target->type) {
case OUTPUT_CONSOLE:
printf("%s", buffer);
fflush(stdout);
break;
case OUTPUT_FILE:
fputs(buffer, target->file);
fflush(target->file);
break;
case OUTPUT_DEBUG:
OutputDebugStringA(buffer);
break;
}
}
}
// 使用示例
int main() {
Logger* logger = Logger_Create();
Logger_AddConsole(logger);
Logger_AddFile(logger, "log.txt");
Logger_AddDebug(logger);
Logger_Log(logger, "Hello, World!\n");
Logger_Destroy(logger);
return 0;
}
最直接的方式:在日志函数中显式调用多个输出接口(如printf
输出到控制台、fprintf
输出到文件、OutputDebugString
输出到调试窗口),一次性完成多路输出。
#include
#include
#include
// 日志级别
typedef enum { LOG_DEBUG, LOG_INFO, LOG_ERROR } LogLevel;
// 多路输出日志函数
void log_write(LogLevel level, const char* format, ...) {
va_list args;
va_start(args, format);
// 1. 输出到控制台
vprintf(format, args);
// 2. 输出到文件(追加模式)
FILE* f = fopen("log.txt", "a");
if (f) {
// 带时间戳
time_t t = time(NULL);
fprintf(f, "[%s] ", ctime(&t));
vfprintf(f, format, args);
fclose(f);
}
// 3. 输出到调试窗口(Windows)
char debug_buf[1024];
vsnprintf(debug_buf, sizeof(debug_buf), format, args);
OutputDebugStringA(debug_buf);
va_end(args);
}
// 使用示例
int main() {
log_write(LOG_INFO, "程序启动\n");
log_write(LOG_DEBUG, "调试信息:x = %d\n", 100);
return 0;
}
#include
#include
#include
#include
#include
// 抽象观察者(输出目标)
class LogObserver {
public:
virtual void write(const std::string& msg) = 0;
virtual ~LogObserver() = default;
};
// 控制台观察者
class ConsoleObserver : public LogObserver {
public:
void write(const std::string& msg) override {
std::cout << msg;
}
};
// 文件观察者
class FileObserver : public LogObserver {
private:
std::ofstream file;
public:
FileObserver(const std::string& path) : file(path, std::ios::app) {}
void write(const std::string& msg) override {
if (file.is_open()) {
file << msg;
}
}
};
// 调试窗口观察者(Windows)
class DebugObserver : public LogObserver {
public:
void write(const std::string& msg) override {
OutputDebugStringA(msg.c_str());
}
};
// 日志管理器(主题)
class Logger {
private:
std::vector<LogObserver*> observers;
std::mutex mtx; // 多线程安全
public:
// 注册观察者(添加输出目标)
void addObserver(LogObserver* obs) {
std::lock_guard<std::mutex> lock(mtx);
observers.push_back(obs);
}
// 日志输出(通知所有观察者)
void log(const std::string& msg) {
std::lock_guard<std::mutex> lock(mtx);
for (auto obs : observers) {
obs->write(msg); // 每个观察者自行处理输出
}
}
};
// 使用示例
int main() {
Logger logger;
logger.addObserver(new ConsoleObserver());
logger.addObserver(new FileObserver("log.txt"));
logger.addObserver(new DebugObserver());
logger.log("程序启动\n");
logger.log("调试信息:x = 100\n");
return 0;
}
LogObserver
接口,无需修改日志核心逻辑;扩展性强,适合中大型项目。#include
#include
#include
#include
#include
#include
#include
class AsyncLogger {
private:
std::queue<std::string> msgQueue;
std::mutex mtx;
std::condition_variable cv;
std::thread bgThread;
bool running = true;
// 后台线程:处理日志并输出到多目标
void backgroundThread() {
while (running) {
std::string msg;
// 等待队列中有消息
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return !msgQueue.empty() || !running; });
if (!running && msgQueue.empty()) break;
msg = msgQueue.front();
msgQueue.pop();
}
// 输出到多目标
std::cout << msg; // 控制台
std::ofstream("log.txt", std::ios::app) << msg; // 文件(简化版)
OutputDebugStringA(msg.c_str()); // 调试窗口
}
}
public:
AsyncLogger() {
bgThread = std::thread(&AsyncLogger::backgroundThread, this);
}
~AsyncLogger() {
running = false;
cv.notify_one();
bgThread.join();
}
// 前台线程调用:添加日志到队列
void log(const std::string& msg) {
std::lock_guard<std::mutex> lock(mtx);
msgQueue.push(msg);
cv.notify_one(); // 通知后台线程
}
};
// 使用示例
int main() {
AsyncLogger logger;
logger.log("程序启动\n");
logger.log("调试信息:x = 100\n");
return 0;
}
#include
#include
// 宏定义:输出到控制台+文件+调试窗口
#define LOG(...) do { \
printf(__VA_ARGS__); \
FILE* f = fopen("log.txt", "a"); \
if (f) { fprintf(f, __VA_ARGS__); fclose(f); } \
char buf[1024]; \
snprintf(buf, sizeof(buf), __VA_ARGS__); \
OutputDebugStringA(buf); \
} while(0)
// 使用示例
int main() {
LOG("程序启动\n");
LOG("调试信息:x = %d\n", 100);
return 0;
}
#ifdef
)灵活开关某个输出目标(如#ifdef DEBUG
才输出到调试窗口)。spdlog(C++):
std::vector
配置多个输出目标(控制台、文件、调试窗口等)。#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
int main() {
// 创建多个输出目标(sink)
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("log.txt");
// 多路输出
spdlog::logger logger("multi_sink", {console_sink, file_sink});
logger.info("程序启动");
logger.debug("调试信息:x = {}", 100);
return 0;
}
glog(Google Log,C++):
log4c(C语言):