Qt日志使用

cpp 复制代码
#ifndef LOGGER_H
#define LOGGER_H

#include <QObject>
#include <QFile>
#include <QMutex>
#include <QDateTime>
#include <QBasicTimer>
#include <atomic>
#include <memory>

enum class LogLevel {
    Message,
    Warning,
    Error
};

class Logger : public QObject
{
    Q_OBJECT
public:
    static Logger* instance();
    bool initialize(const QString& filePath);

    void log(LogLevel level, const QString& message);
    void message(const QString& message);
    void warning(const QString& message);
    void error(const QString& message);

private:
    explicit Logger(QObject* parent = nullptr);
    ~Logger();
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

    void writeToFile(const QByteArray& data);
    void timerEvent(QTimerEvent* event) override;

    QFile logFile;
    QMutex fileMutex;
    std::atomic<bool> isInitialized{ false };
    QBasicTimer flushTimer;
    static std::atomic<Logger*> m_instance;
    static QMutex m_instanceMutex;
};

#define LOG_MESSAGE(msg) Logger::instance()->message(msg)
#define LOG_WARNING(msg) Logger::instance()->warning(msg)
#define LOG_ERROR(msg) Logger::instance()->error(msg)

#endif // LOGGER_H
cpp 复制代码
#include "logger.h"
#include <QThread>
#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <cstring>

std::atomic<Logger*> Logger::m_instance = nullptr;
QMutex Logger::m_instanceMutex;

Logger::Logger(QObject* parent) : QObject(parent)
{
    if (QCoreApplication::instance()) {
        connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
            this, [this] {
                QMutexLocker locker(&fileMutex);
                if (logFile.isOpen()) logFile.flush();
            });
    }
}

Logger::~Logger()
{
    QMutexLocker locker(&fileMutex);
    if (logFile.isOpen()) {
        logFile.flush();
        logFile.close();
    }
}

Logger* Logger::instance()
{
    auto* instance = m_instance.load(std::memory_order_acquire);
    if (!instance) {
        QMutexLocker locker(&m_instanceMutex);
        instance = m_instance.load(std::memory_order_relaxed);
        if (!instance) {
            // 确保在主线程创建
            if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
                qFatal("Logger instance must be created in main thread");
            }
            instance = new Logger();
            m_instance.store(instance, std::memory_order_release);
        }
    }
    return instance;
}

bool Logger::initialize(const QString& filePath)
{
    if (isInitialized.load(std::memory_order_acquire))
        return true;

    QMutexLocker locker(&fileMutex);

    QFileInfo fileInfo(filePath);
    QDir logDir = fileInfo.absoluteDir();
    if (!logDir.exists() && !logDir.mkpath(".")) {
        qDebug() << "Failed to create log directory:" << logDir.path();
        return false;
    }

    logFile.setFileName(filePath);
    if (!logFile.open(QIODevice::Append | QIODevice::Text)) {
        qDebug() << "Failed to open log file:" << filePath;
        return false;
    }

    const auto header = QByteArrayLiteral("========================================\n")
        + "Log started at: "
        + QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss").toUtf8()
        + "\n========================================\n\n";

    logFile.write(header);
    logFile.flush();

    isInitialized.store(true, std::memory_order_release);
    return true;
}

void Logger::log(LogLevel level, const QString& message)
{
    if (!isInitialized.load(std::memory_order_acquire)) {
        qDebug() << "Logger not initialized, message dropped:" << message;
        return;
    }

    const QByteArray timeStamp = QDateTime::currentDateTime()
        .toString("yyyy-MM-dd HH:mm:ss.zzz").toUtf8();
    const QByteArray msgUtf8 = message.toUtf8();

    static const char* const levelStrings[] = { "MESSAGE", "WARNING", "ERROR" };
    static_assert(sizeof(levelStrings) / sizeof(levelStrings[0]) ==
        static_cast<int>(LogLevel::Error) + 1,
        "LogLevel count mismatch");

    const char* levelStr = levelStrings[static_cast<int>(level)];

    // 精确预分配缓冲区
    int totalSize = 1; // '['
    totalSize += timeStamp.size();
    totalSize += 3; // "] ["
    totalSize += std::strlen(levelStr);
    totalSize += 2; // "] "
    totalSize += msgUtf8.size();
    totalSize += 1; // '\n'

    QByteArray logEntry;
    logEntry.reserve(totalSize);

    // 高效构建日志条目
    logEntry.append('[')
        .append(timeStamp)
        .append("] [")
        .append(levelStr)
        .append("] ")
        .append(msgUtf8)
        .append('\n');

    writeToFile(logEntry);
}

void Logger::writeToFile(const QByteArray& data)
{
    QMutexLocker locker(&fileMutex);
    logFile.write(data);

    // 使用单次触发定时器
    if (!flushTimer.isActive()) {
        flushTimer.start(20, this);
    }
}

void Logger::timerEvent(QTimerEvent* event)
{
    if (event->timerId() == flushTimer.timerId()) {
        QMutexLocker locker(&fileMutex);
        logFile.flush();
        flushTimer.stop();
    }
}

void Logger::message(const QString& message) { log(LogLevel::Message, message); }
void Logger::warning(const QString& message) { log(LogLevel::Warning, message); }
void Logger::error(const QString& message) { log(LogLevel::Error, message); }

main.cpp引用头文件并初始化

cpp 复制代码
// 初始化(主线程)
Logger::instance()->initialize("app.log");

// 记录日志(任意线程)
LOG_MESSAGE("User logged in");  // 宏展开为:
// Logger::instance()->message("User logged in");

LOG_ERROR("Database connection failed");