C++简易日志记录代码

257 阅读3分钟

这里是自己总结简易的日志代码,当只需要一个简易的日志库记录日志时,调试程序时只需要包含此Logger.hpp 并没考虑性能和太复杂运行情况

  • 支持只写文件也支持只打印,同时也支持,功能可自行通过构造参数设置
  • 默自动创建Log目录,同时按时间名自动创建日志文件
  • 支持不同级别日志记录,同时打印窗口支持不同级别不同颜色显示Log信息
  • 支持日志记录代码文件名和行号和行号

Windows平台测试通过,Linux暂未测试 纯C++版本


#ifndef LOGGER_HPP
#define LOGGER_HPP

// 这里是你的Logger.hpp文件的内容
#include <iostream>
#include <fstream>
#include <ctime>
#include <string>
#include <iomanip>
#include <direct.h> // Windows 下创建目录所需的头文件
// 在 Linux 下创建目录需要使用 <sys/stat.h> 头文件

class LogLevel {
public:
    enum Level {
        Debug,    // 调试级别
        Info,     // 信息级别
        Warning,  // 警告级别
        Error     // 错误级别
    };
};

class Logger {
public:
    // 构造函数,用于初始化日志对象
    Logger(bool enableConsoleOutput = true, bool enableFileOutput = true)
        : m_enableConsoleOutput(enableConsoleOutput), m_enableFileOutput(enableFileOutput)
    {
        // 检查 log 文件夹是否存在,如果不存在则创建它
        std::string logDir = "log";
#ifdef _WIN32
        _mkdir(logDir.c_str()); // Windows 下创建目录
#else
        mkdir(logDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); // Linux 下创建目录
#endif

        // 创建日志文件名,格式为 "log_年月日_时分秒.txt"
        std::time_t t = std::time(nullptr);
        char timestamp[20]; // 用于存储格式化后的时间字符串

        std::tm tm;
        localtime_s(&tm, &t); // 将时间戳转换为本地时间
        std::strftime(timestamp, sizeof(timestamp), "%Y-%m-%d_%H-%M-%S", &tm); // 将时间格式化为字符串
        m_filePath = logDir + "/log_" + timestamp + ".txt";

        if (m_enableFileOutput)
        {
            m_file.open(m_filePath.c_str(), std::ios_base::out | std::ios_base::app); // 打开日志文件
        }
    }

    // 析构函数,用于关闭日志文件
    ~Logger()
    {
        if (m_enableFileOutput)
        {
            m_file.close(); // 关闭日志文件
        }
    }

    // 设置日志级别
    void setLogLevel(LogLevel::Level level)
    {
        m_logLevel = level;
    }

    // 输出调试信息
    void debug(const std::string& message, const char* file = nullptr, int line = -1)
    {
        log(LogLevel::Debug, message, file, line);
    }

    // 输出普通信息
    void info(const std::string& message, const char* file = nullptr, int line = -1)
    {
        log(LogLevel::Info, message, file, line);
    }

    // 输出警告信息
    void warning(const std::string& message, const char* file = nullptr, int line = -1)
    {
        log(LogLevel::Warning, message, file, line);
    }

    // 输出错误信息
    void error(const std::string& message, const char* file = nullptr, int line = -1)
    {
        log(LogLevel::Error, message, file, line);
    }

private:
    bool m_enableConsoleOutput; // 是否输出到控制台
    bool m_enableFileOutput;    // 是否输出到文件
    std::string m_filePath;     // 日志文件路径
    std::ofstream m_file;       // 日志文件对象
    LogLevel::Level m_logLevel = LogLevel::Info; // 日志级别,默认为 Info

    // 内部函数,用于输出日志信息
    void log(LogLevel::Level level, const std::string& message, const char* file = nullptr, int line = -1)
    {
        if (level < m_logLevel) // 如果日志级别低于设定的级别,则不输出
        {
            return;
        }

        std::string logLevelStr; // 日志级别字符串
        std::string colorCode;   // 控制台输出颜色代码
        switch (level)
        {
        case LogLevel::Debug:
            logLevelStr = "DEBUG";
            colorCode = "\033[36m"; // 青色
            break;
        case LogLevel::Info:
            logLevelStr = "INFO";
            colorCode = "\033[32m"; // 绿色
            break;
        case LogLevel::Warning:
            logLevelStr = "WARNING";
            colorCode = "\033[33m"; // 黄色
            break;
        case LogLevel::Error:
            logLevelStr = "ERROR";
            colorCode = "\033[31m"; // 红色
            break;
        default:
            logLevelStr = "UNKNOWN";
            break;
        }

        std::string output;
        if (file == nullptr || line == -1) // 如果没有文件名和行号,则只输出时间、级别和消息
        {
            output = "[" + getCurrentTime() + "]" + " [" + logLevelStr + "]" + " " + message + "\n";
        }
        else // 如果有文件名和行号,则输出时间、级别、文件名、行号和消息
        {
            std::string fileName(file);
            fileName = fileName.substr(fileName.find_last_of("/\\") + 1); // 获取文件名
            output = "[" + getCurrentTime() + "]" + " [" + logLevelStr + "]" + " [" + fileName + ":" + std::to_string(line) + "]" + " " + message + "\n";
        }

        // 输出到控制台
        if (m_enableConsoleOutput)
        {
            std::cout << colorCode << output << "\033[0m"; // 输出带颜色的日志信息
        }

        // 输出到文件
        if (m_enableFileOutput && m_file)
        {
            m_file << output; // 将日志信息写入文件
            m_file.flush();   // 将缓冲区的数据立即写入文件
        }
    }

    // 获取当前时间
    std::string getCurrentTime()
    {
        char timeStr[30]; // 用于存储格式化后的时间字符串
        std::time_t t = std::time(nullptr); // 获取当前时间戳
        std::tm tm;
        localtime_s(&tm, &t); // 将时间戳转换为本地时间
        std::strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm); // 将时间格式化为字符串
        return std::string(timeStr); // 将字符数组转换为 std::string 对象并返回
    }
};
#endif

测试例子

int main()
{
    //1.开启控制台打印 2.开启文件写入
    Logger m_log(true,true);
    //记录代码文件名和代码行
    m_log.info("hell world", __FILE__, __LINE__);
    m_log.warning("hell world", __FILE__, __LINE__);
    m_log.error("hell world");
    m_log.debug("hell world");
    std::cout << "Hello World!\n";
}

运行截图

image.png

image.png Qt版本

#ifndef LOGGER_HPP
#define LOGGER_HPP
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QDebug>

class Logger
{
public:
    enum LogLevel
    {
        Debug,
        Info,
        Warning,
        Error
    };

    Logger(bool enableConsoleOutput = true, bool enableFileOutput = true)
        : m_enableConsoleOutput(enableConsoleOutput), m_enableFileOutput(enableFileOutput)
    {
        // 检查 log 文件夹是否存在,如果不存在则创建它
        QString logDir = QDir::currentPath() + "/log";
        if (!QDir(logDir).exists())
        {
            QDir().mkpath(logDir);
        }

        // 创建日志文件名,格式为 "log_年月日_时分秒.txt"
        QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss");
        m_filePath = QString("%1/log_%2.txt").arg(logDir).arg(timestamp);

        if (m_enableFileOutput)
        {
            m_file = new QFile(m_filePath);
            m_file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
        }
    }

    ~Logger()
    {
        if (m_enableFileOutput)
        {
            m_file->close();
            delete m_file;
        }
    }

    void setLogLevel(LogLevel level)
    {
        m_logLevel = level;
    }

    void debug(const QString &message, const char *file = Q_NULLPTR, int line = -1)
    {
        log(Debug, message, file, line);
    }

    void info(const QString &message, const char *file = Q_NULLPTR, int line = -1)
    {
        log(Info, message, file, line);
    }

    void warning(const QString &message, const char *file = Q_NULLPTR, int line = -1)
    {
        log(Warning, message, file, line);
    }

    void error(const QString &message, const char *file = Q_NULLPTR, int line = -1)
    {
        log(Error, message, file, line);
    }

private:
    bool m_enableConsoleOutput; // 是否输出到控制台
    bool m_enableFileOutput;    // 是否输出到文件
    QString m_filePath;         // 日志文件路径
    QFile *m_file;              // 日志文件对象
    LogLevel m_logLevel = Info; // 日志级别,默认为 Info

    void log(LogLevel level, const QString &message, const char *file, int line)
    {
        if (level < m_logLevel)
        {
            return;
        }

        QString logLevelStr;
        QString colorCode;
        switch (level)
        {
        case Debug:
            logLevelStr = "DEBUG";
            colorCode = "\033[36m"; // 青色
            break;
        case Info:
            logLevelStr = "INFO";
            colorCode = "\033[32m"; // 绿色
            break;
        case Warning:
            logLevelStr = "WARNING";
            colorCode = "\033[33m"; // 黄色
            break;
        case Error:
            logLevelStr = "ERROR";
            colorCode = "\033[31m"; // 红色
            break;
        default:
            logLevelStr = "UNKNOWN";
            break;
        }

        QString output;
        if (file == Q_NULLPTR || line == -1)
        {
            output = QString("%1[%2]%3 %4\033[0m\n")
                         .arg(colorCode)
                         .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"))
                         .arg(logLevelStr)
                         .arg(message);
        }
        else
        {
            output = QString("%1[%2][%3:%4]%5 %6\033[0m\n")
                         .arg(colorCode)
                         .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"))
                         .arg(QFileInfo(file).fileName())
                         .arg(line)
                         .arg(logLevelStr)
                         .arg(message);
        }
        if (m_enableConsoleOutput)
        {
            qInfo().noquote() << output;
        }

        if (m_enableFileOutput && m_file)
        {
            QTextStream stream(m_file);
            stream << QString("[%1][%2] %3\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")).arg(logLevelStr).arg(message);
            m_file->flush();
        }
    }
};
#endif