C++ 日志类设计

目录

一、设计思路

二、完整代码实现

三、核心知识点讲解

[1. 单例模式](#1. 单例模式)

[2. operator () 重载](#2. operator () 重载)

[3. 可变参数与格式化](#3. 可变参数与格式化)

[4. 时间戳](#4. 时间戳)

[5. 文件输出](#5. 文件输出)

四、使用示例

五、总结


在日常写项目、做实验、写服务端程序时,我们总需要一个轻量好用的日志工具。既能打印控制台,又能输出到文件,自带时间戳和级别,调用方式还要干净优雅。

今天就基于重载 operator () 的思路,实现一个极简、可直接嵌入项目的 C++ 日志类,代码不多但功能完整,非常适合学习和工程使用。


设计思路

  1. 调用方式优雅 :使用 () 运算符重载,像函数一样直接调用 log(INFO, "hello %d", 123);
  2. 功能完整:自动时间戳、日志级别、控制台输出、文件输出
  3. 全局单例:一处定义,随处使用,不用反复创建对象
  4. 轻量无依赖:仅用标准库,不引入第三方组件
  5. 格式统一[时间] [级别] 信息,美观易读

完整代码实现

cpp 复制代码
#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>

// 日志级别
enum LogLevel {
    INFO, DEBUG, WARNING, ERROR, FATAL
};

class Logger {
public:
    // 单例模式,全局唯一实例
    static Logger& instance() {
        static Logger log;
        return log;
    }

    // 核心:重载 operator()
    void operator()(LogLevel level, const char* fmt, ...) {
        // 格式化时间
        char time_buf[32];
        std::time_t now = std::time(nullptr);
        std::strftime(time_buf, sizeof(time_buf), "%F %T", std::localtime(&now));

        // 获取级别字符串
        const char* level_str = get_level_str(level);

        // 格式化用户输入日志内容
        char content[1024];
        va_list args;
        va_start(args, fmt);
        vsnprintf(content, sizeof(content), fmt, args);
        va_end(args);

        // 拼接并输出到控制台
        std::cout << "[" << time_buf << "] [" << level_str << "] " << content << "\n";

        // 如果设置了日志文件,则同时写入文件
        if (!log_file.empty()) {
            std::ofstream ofs(log_file, std::ios::app);
            if (ofs) {
                ofs << "[" << time_buf << "] [" << level_str << "] " << content << "\n";
            }
        }
    }

    // 设置日志输出文件
    void set_file(const std::string& path) {
        log_file = path;
    }

private:
    Logger() = default;  // 私有构造,禁止外部创建
    std::string log_file;

    // 日志级别转字符串
    const char* get_level_str(LogLevel level) const {
        switch (level) {
            case INFO:    return "INFO";
            case DEBUG:   return "DEBUG";
            case WARNING: return "WARNING";
            case ERROR:   return "ERROR";
            case FATAL:   return "FATAL";
            default:      return "UNKNOWN";
        }
    }
};

// 全局日志对象,直接使用
static Logger& log = Logger::instance();

核心知识点讲解

1. 单例模式

cpp 复制代码
static Logger& instance() {
    static Logger log;
    return log;
}
  • 保证全局只有一个日志实例
  • 避免多次打开文件、重复输出
  • 懒加载,用到时才初始化

2. operator () 重载

这是整个设计最优雅的地方:

cpp 复制代码
void operator()(LogLevel level, const char* fmt, ...)

通过重载括号运算符,我们可以直接像调用函数一样使用:

cpp 复制代码
log(INFO, "server started, pid = %d", getpid());

3. 可变参数与格式化

使用 C 标准库的可变参数机制:

  • va_list / va_start / va_end 处理参数列表
  • vsnprintf 格式化字符串
  • 支持 %d %s %f 等常用占位符,和 printf 用法一致

4. 时间戳

cpp 复制代码
std::strftime(time_buf, sizeof(time_buf), "%F %T", std::localtime(&now));
  • %F → 年 - 月 - 日
  • %T → 时:分: 秒
  • 日志格式整齐,便于查看和排查问题

5. 文件输出

cpp 复制代码
std::ofstream ofs(log_file, std::ios::app);
  • std::ios::app 表示追加模式,不会覆盖原有内容
  • 不设置文件时只打印控制台,设置后同时落盘

使用示例

cpp 复制代码
#include <unistd.h>
#include "Logger.h"

int main() {
    // 可选:设置日志文件
    log.set_file("service.log");

    // 直接使用
    log(INFO,  "service start successfully, pid = %d", getpid());
    log(DEBUG, "connect to client success, fd = %d", 5);
    log(WARNING, "low memory warning");
    log(ERROR, "open fifo failed");
    log(FATAL, "system error, process exit");

    return 0;
}

运行效果:

复制代码
[2026-04-14 15:30:20] [INFO] service start successfully, pid = 12345
[2026-04-14 15:30:20] [DEBUG] connect to client success, fd = 5
[2026-04-14 15:30:20] [WARNING] low memory warning
[2026-04-14 15:30:20] [ERROR] open fifo failed
[2026-04-14 15:30:20] [FATAL] system error, process exit

同时,这些内容也会被写入 service.log


总结

这个极简日志类虽然代码不长,但具备了日常开发所需的核心能力:

  • 优雅调用:operator() 重载
  • 自动时间戳 + 日志级别
  • 控制台 + 文件双输出
  • 全局单例,随处可用
  • 轻量、无依赖、易集成

无论是课程实验、管道通信、多进程服务,还是小型项目,都可以直接拿来用,非常适合作为自己的基础工具类。

相关推荐
一tiao咸鱼16 分钟前
前端转 agent # 03 - Pydantic v2 数据校验深入
前端·后端
一路向北North20 分钟前
Spring Security OAuth2.0(18):资源服务测试
java·后端·spring
用户990450177800939 分钟前
若依工作流主流方案对接
后端
汉克老师1 小时前
GESP2026年6月认证C++八级( 第二部分判断题(1-10))精讲
c++·归并排序·dijkstra·二分·互斥·gesp8级·可重复组合
触底反弹1 小时前
🚀 Node.js path 和 fs 模块,从回调地狱到 async/await 的进化之路!
javascript·后端·node.js
问简1 小时前
【共享盘】ubuntu、windows
linux·windows·ubuntu
nianniannnn2 小时前
c++复习自存--流、异常处理、多线程与C++工程规范
开发语言·c++
小生不才yz2 小时前
Shell脚本精读 · S13-01 | POSIX sh 与 Bash:语法对照表
linux
Csvn2 小时前
Python 开发技巧 · 生成器(Generator)进阶 —— 从 yield 到 yield from,写出省内存的生产者-消费者模式
后端·python
geovindu2 小时前
CSharp: Dijkstra Algorithms
开发语言·后端·算法·c#