qt log输出:分类;定时更新文件;带文件名、函数名、行号

qt log输出的类型有多少种

在Qt框架中,日志输出是开发过程中用于调试和诊断应用程序的重要手段。Qt提供了多种日志输出类型,以满足不同的需求。这些类型通常包括:

  1. Debug:用于输出详细的调试信息,帮助开发人员定位问题所在。这些信息通常只在开发过程中需要,而在产品发布后会被关闭。
  2. Info:提供一般性的信息,如应用程序的状态变化、重要事件等。这些信息有助于了解应用程序的运行情况,但不像调试信息那么详细。
  3. Warning:用于输出可能引发问题的警告信息。这些信息表明某些操作可能不符合预期,但不一定导致应用程序崩溃。
  4. Critical:表示发生了严重错误,可能导致应用程序无法继续运行。这些信息需要立即关注,并进行相应的处理。
  5. Fatal:表示发生了致命错误,导致应用程序无法恢复。一旦输出这种类型的信息,应用程序通常会立即终止。

这些日志输出类型可以通过Qt的日志系统(如QCoreApplication::addMessageHandler)进行配置和管理,以便在开发过程中灵活地获取所需的日志信息。

请分别举例,用代码 

在Qt中,你可以使用qDebug()qInfo()qWarning()qCritical() 和 qFatal() 函数来分别输出不同级别的日志信息。以下是每种日志输出类型的C++代码示例:

Debug 日志

#include <QDebug>
int main() {
qDebug() << "This is a debug message.";
return 0;
}

Info 日志

#include <QCoreApplication>
int main() {
qInfo() << "This is an informational message.";
return 0;
}

Warning 日志

#include <QCoreApplication>
int main() {
qWarning() << "This is a warning message.";
return 0;
}

Critical 日志

#include <QCoreApplication>
int main() {
qCritical() << "This is a critical message.";
return 0;
}

Fatal 日志

#include <QCoreApplication>
int main() {
qFatal("This is a fatal message.");
return 0; // Note: This line will not be executed due to qFatal's behavior.
}

请注意,qFatal() 函数在输出致命错误消息后会终止程序,因此在其后的代码将不会被执行。在实际应用中,请根据需要选择合适的日志输出函数来记录程序运行时的信息。

 

将日志输出为文件 

在Qt中,将日志输出到文件通常涉及自定义一个消息处理器,并将其添加到Qt的消息处理系统中。以下是一个简单的示例,展示了如何将Qt日志重定向到文件中:


#include <QCoreApplication>  
#include <QFile>  
#include <QTextStream>  
  
void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {  
    // 创建一个文件用于记录日志,这里以追加的方式打开  
    QFile file("application.log");  
    if (!file.open(QIODevice::Append | QIODevice::Text)) {  
        return;  
    }  
  
    QTextStream out(&file);  
    out << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz ")  
        << msg << endl;  
    file.close();  
}  
  
int main(int argc, char *argv[]) {  
    QCoreApplication app(argc, argv);  
  
    // 安装自定义的消息处理器  
    qInstallMessageHandler(customMessageHandler);  
  
    qDebug() << "This is a debug message.";  
    qWarning() << "This is a warning message.";  
  
    return app.exec();  
}

在这个例子中,customMessageHandler 函数被定义为接收Qt的消息,并将其写入到名为 "application.log" 的文件中。qInstallMessageHandler 函数用于将自定义的消息处理器安装到Qt的消息处理系统中。此后,所有通过 qDebug()qWarning(), 等函数发出的消息都会被重定向到这个自定义处理器,并最终被写入日志文件。

请注意,频繁地打开和关闭文件可能会对性能产生影响。在实际应用中,您可能希望优化文件操作,例如保持文件在程序运行期间一直打开,或者使用更高效的日志库来处理日志记录。

此外,如果您希望在日志中包含更多上下文信息(如线程ID、文件名、行号等),您可以从 context 参数中提取这些信息,并将其一并写入日志文件。

可以依据不同的日志类别,将日志输出到不同的文件夹吗 

当然可以。在Qt中,您可以根据日志的类别(如Debug、Info、Warning、Critical、Fatal)将日志输出到不同的文件夹。这通常需要在自定义的消息处理器中根据消息类型来决定日志文件的路径。以下是一个示例代码,展示了如何实现这一功能:


#include <QCoreApplication>  
#include <QFile>  
#include <QTextStream>  
#include <QMessageLogContext>  
#include <QDateTime>  
#include <QDir>  
  
void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {  
    QString logFolder;  
    switch (type) {  
    case QtDebugMsg:  
        logFolder = "debug";  
        break;  
    case QtInfoMsg:  
        logFolder = "info";  
        break;  
    case QtWarningMsg:  
        logFolder = "warning";  
        break;  
    case QtCriticalMsg:  
        logFolder = "critical";  
        break;  
    case QtFatalMsg:  
        logFolder = "fatal";  
        break;  
    default:  
        logFolder = "unknown";  
        break;  
    }  
  
    // 确保日志文件夹存在  
    QDir dir;  
    if (!dir.exists(logFolder)) {  
        dir.mkpath(logFolder);  
    }  
  
    // 创建日志文件路径  
    QString logFilePath = QDir::cleanPath(logFolder + "/log_" + QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss") + ".txt");  
    QFile file(logFilePath);  
    if (!file.open(QIODevice::Append | QIODevice::Text)) {  
        return;  
    }  
  
    QTextStream out(&file);  
    out << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz ")  
        << msg << endl;  
    file.close();  
}  
  
int main(int argc, char *argv[]) {  
    QCoreApplication app(argc, argv);  
  
    // 安装自定义的消息处理器  
    qInstallMessageHandler(customMessageHandler);  
  
    // 测试日志输出  
    qDebug() << "This is a debug message.";  
    qInfo() << "This is an info message.";  
    qWarning() << "This is a warning message.";  
    qCritical() << "This is a critical message.";  
    // qFatal() 会终止程序,因此在此示例中省略  
  
    return app.exec();  
}

在这个示例中,customMessageHandler 函数根据传入的消息类型 type 来决定日志应该被写入哪个文件夹。然后,它创建一个以当前日期和时间命名的日志文件,在该文件中追加日志消息。如果指定的日志文件夹不存在,代码会尝试创建它。

请注意,这个示例是为了展示概念而简化的。在生产环境中,您可能需要考虑日志文件的滚动、大小限制、线程安全以及性能优化等问题。

可以让日志以分钟为单位生成文件吗 

当然可以。如果您希望日志以分钟为单位生成文件,您可以在自定义日志处理器中根据当前时间来构造文件名,并确保每分钟生成一个新的日志文件。以下是一个示例代码,展示了如何实现这一功能:


#include <QCoreApplication>  
#include <QFile>  
#include <QTextStream>  
#include <QMessageLogContext>  
#include <QDateTime>  
#include <QDir>  
#include <QMap>  
  
// 用于存储文件指针,确保每分钟的文件只被打开和关闭一次  
QMap<QString, QFile*> openFiles;  
  
void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {  
    // 根据当前分钟构造日志文件名  
    QString currentTime = QDateTime::currentDateTime().toString("yyyyMMdd_HHmm");  
    QString logFilePath = QDir::cleanPath("logs/log_" + currentTime + ".txt");  
      
    QFile *file;  
    if (openFiles.contains(logFilePath)) {  
        // 如果文件已经打开,则直接使用  
        file = openFiles[logFilePath];  
    } else {  
        // 确保日志文件夹存在  
        QDir dir;  
        if (!dir.exists("logs")) {  
            dir.mkpath("logs");  
        }  
  
        // 打开新的日志文件  
        file = new QFile(logFilePath);  
        if (!file->open(QIODevice::Append | QIODevice::Text)) {  
            return;  
        }  
  
        // 将文件指针存储到map中  
        openFiles[logFilePath] = file;  
    }  
  
    QTextStream out(file);  
    out << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz ")  
        << msg << endl;  
  
    // 如果当前分钟已过,则关闭文件并从map中移除  
    QString nextMinute = QDateTime::currentDateTime().addSecs(60).toString("yyyyMMdd_HHmm");  
    if (currentTime != nextMinute) {  
        file->close();  
        delete file;  
        openFiles.remove(logFilePath);  
    }  
}  
  
int main(int argc, char *argv[]) {  
    QCoreApplication app(argc, argv);  
  
    // 安装自定义的消息处理器  
    qInstallMessageHandler(customMessageHandler);  
  
    // 用于测试日志输出的循环  
    for (int i = 0; i < 10; ++i) {  
        qDebug() << "Debug message" << i;  
        QThread::sleep(10); // 休眠10秒以模拟日志记录过程  
    }  
  
    // 关闭所有仍然打开的文件  
    for (auto it = openFiles.constBegin(); it != openFiles.constEnd(); ++it) {  
        it.value()->close();  
        delete it.value();  
    }  
    openFiles.clear();  
  
    return app.exec();  
}

这个示例代码中,我们使用了QMap来跟踪当前打开的文件。每当需要写入日志时,我们检查是否已经为当前分钟打开了文件。如果没有,则创建一个新文件并将其添加到openFiles映射中。如果当前时间已经进入下一分钟,我们就关闭并删除上一分钟的文件,并从openFiles映射中移除它。

请注意,这个示例中使用了QThread::sleep来模拟日志记录过程,实际应用中您可能不需要这样做。此外,在多线程应用程序中,您需要确保对openFiles的访问是线程安全的。

这个简单的日志系统没有处理日志文件滚动或大小限制等高级功能。如果您需要这些功能,可能需要考虑使用更成熟的日志库,如log4cpp、spdlog或Qt自带的QCoreApplication::addMessageHandler配合适当的文件滚动策略。

 还保持之前的日志分类

如果您希望保持之前的日志分类,并且同时希望日志文件以分钟为单位进行分割,您可以在自定义的消息处理器中结合这两种策略。以下是一个示例代码,它根据日志类型和当前分钟来创建和管理日志文件:


#include <QCoreApplication>  
#include <QFile>  
#include <QTextStream>  
#include <QMessageLogContext>  
#include <QDateTime>  
#include <QDir>  
#include <QMap>  
  
// 日志文件映射,key为(类型, 时间),value为文件指针  
QMap<QPair<QtMsgType, QString>, QFile*> openFiles;  
  
void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {  
    // 根据日志类型和当前分钟构造日志文件名  
    QString logType;  
    switch (type) {  
    case QtDebugMsg:  
        logType = "debug";  
        break;  
    case QtInfoMsg:  
        logType = "info";  
        break;  
    case QtWarningMsg:  
        logType = "warning";  
        break;  
    case QtCriticalMsg:  
        logType = "critical";  
        break;  
    case QtFatalMsg:  
        logType = "fatal";  
        break;  
    default:  
        logType = "unknown";  
        break;  
    }  
  
    QString currentTime = QDateTime::currentDateTime().toString("yyyyMMdd_HHmm");  
    QString logDir = QDir::cleanPath("logs/" + logType);  
    QString logFilePath = QDir::cleanPath(logDir + "/log_" + currentTime + ".txt");  
  
    // 确保日志文件夹存在  
    QDir dir;  
    if (!dir.exists(logDir)) {  
        dir.mkpath(logDir);  
    }  
  
    QFile *file = nullptr;  
    QPair<QtMsgType, QString> key(type, currentTime);  
    if (openFiles.contains(key)) {  
        // 如果文件已经打开,则直接使用  
        file = openFiles[key];  
    } else {  
        // 打开新的日志文件  
        file = new QFile(logFilePath);  
        if (!file->open(QIODevice::Append | QIODevice::Text)) {  
            return;  
        }  
  
        // 将文件指针存储到map中  
        openFiles[key] = file;  
    }  
  
    QTextStream out(file);  
    out << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz ")  
        << msg << endl;  
  
    // 检查是否需要关闭当前分钟的文件  
    QString nextMinute = QDateTime::currentDateTime().addSecs(60).toString("yyyyMMdd_HHmm");  
    if (currentTime != nextMinute) {  
        // 关闭并删除当前分钟的文件指针  
        if (openFiles.contains(key)) {  
            file->close();  
            delete file;  
            openFiles.remove(key);  
        }  
    }  
}  
  
int main(int argc, char *argv[]) {  
    QCoreApplication app(argc, argv);  
  
    // 安装自定义的消息处理器  
    qInstallMessageHandler(customMessageHandler);  
  
    // 测试日志输出  
    qDebug() << "This is a debug message.";  
    qInfo() << "This is an info message.";  
    qWarning() << "This is a warning message.";  
    qCritical() << "This is a critical message.";  
    // 注意:qFatal() 会终止程序,因此可能不适合此示例  
  
    // 模拟日志记录过程  
    QThread::sleep(70); // 休眠70秒以跨越分钟边界,观察文件分割效果  
  
    qDebug() << "This is another debug message after a minute.";  
  
    // 关闭所有仍然打开的文件  
    for (auto it = openFiles.constBegin(); it != openFiles.constEnd(); ++it) {  
        it.value()->close();  
        delete it.value();  
    }  
    openFiles.clear();  
  
    return app.exec();  
}

在这个示例中,我们创建了一个openFiles映射,它的键是一个包含日志类型和当前分钟时间戳的对。这允许我们为每个日志类型和每分钟时间维护一个单独的文件。当写入日志时,我们检查是否已经为该类型和分钟打开了文件,如果没有,则创建一个新文件。当分钟改变时,我们关闭并删除旧的文件指针。

请注意,这个示例是为了演示目的而简化的。在生产环境中,您可能需要添加额外的错误处理、文件锁定、日志滚动和大小限制等特性。此外,在多线程环境中,您需要确保对openFiles的访问是线程安全的。

可以在日志中自动携带类名函数名和行号吗 

在Qt中,要在日志中自动携带类名、函数名和行号,你可以使用 QMessageLogContext 的信息,这是 Qt 5 引入的一个特性,用于提供日志消息的上下文。QMessageLogContext 可以捕获发出日志消息的代码位置,包括文件名、函数名和行号。

为了在日志中包含这些信息,你需要稍微修改你的日志处理函数,以从 QMessageLogContext 中提取所需的信息,并将其添加到日志消息中。以下是一个修改后的 customMessageHandler 函数示例,该函数在日志消息中包含类名、函数名和行号:

void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {  
    // 构造日志文件名和路径(这部分与之前的示例相同)  
    // ...  
  
    // 打开或获取已打开的日志文件(这部分与之前的示例相同)  
    // ...  
  
    // 从上下文中提取文件名、函数名和行号  
    QString fileName = context.file ? context.file : "unknown file";  
    QString functionName = context.function ? context.function : "unknown function";  
    int lineNumber = context.line ? *context.line : -1;  
  
    QTextStream out(file);  
    out << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz ")  
        << "[" << fileName << ":" << functionName << ":" << lineNumber << "] "  
        << msg << endl;  
  
    // 关闭或保持文件打开(这部分与之前的示例相同)  
    // ...  
}

请注意,为了捕获这些上下文信息,你需要确保在编译时启用了 Qt 的日志上下文跟踪。这通常是通过定义 QT_MESSAGELOGCONTEXT 宏来完成的。你可以在你的项目文件(.pro)或构建系统中添加这个定义,或者在源代码文件的开始部分定义它。

例如,在你的 .pro 文件中添加:

DEFINES += QT_MESSAGELOGCONTEXT

或者,在你的源代码文件的顶部添加:

#define QT_MESSAGELOGCONTEXT

启用 QT_MESSAGELOGCONTEXT 后,每次调用 qDebug()qInfo()qWarning()qCritical(), 或 qFatal() 时,Qt 都会自动捕获调用这些函数的上下文,并将其传递给自定义的消息处理函数。然后,你的消息处理函数就可以从 QMessageLogContext 中提取出所需的信息,并将其记录在日志中。

 qmake 定义QT_MESSAGELOGCONTEXT宏: DEFINES += QT_MESSAGELOGCONTEXT,cmake 用 怎么弄

在 CMake 中定义宏,比如 QT_MESSAGELOGCONTEXT,你可以使用 add_definitions() 命令或者直接在 target_compile_definitions() 中设置。下面是如何在 CMakeLists.txt 文件中定义 QT_MESSAGELOGCONTEXT 宏的示例:


# 方法一:使用 add_definitions(全局定义)  
add_definitions(-DQT_MESSAGELOGCONTEXT)  
  
# 方法二:使用 target_compile_definitions(针对特定目标定义)  
add_executable(your_target_name your_source_files.cpp)  
target_compile_definitions(your_target_name PRIVATE QT_MESSAGELOGCONTEXT)

在上面的示例中,your_target_name 是你的项目目标名称(比如可执行文件或库),your_source_files.cpp 是你的源代码文件。

  • 方法一使用 add_definitions() 会在全局范围内定义宏,这意味着它会影响到所有后续添加的目标。
  • 方法二使用 target_compile_definitions() 允许你为特定的目标定义宏,这样更加灵活,并且符合现代 CMake 的使用习惯。

推荐使用方法二,因为它更加明确且易于维护。如果你只想为某个特定的目标定义 QT_MESSAGELOGCONTEXT,那么使用 target_compile_definitions() 是最佳选择。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值