qt 程序中,有简单的调试信息,又有重要的日志信息,如何把qDebug()、qWarning()等调试信息输出到文件?又如何即输出在终端又同时输出在文件呢?
log4qt给你灵活自由配置!
完整代码在下面。
log4qt封装
1 简单日志系统qInstallMessageHandler
实现自定义的qInstallMessageHandler
实现不同等级的日志打印,每天产生一个新的日志文件,当然也可以都打印在一个文件里面,简单改下即可
#include <QApplication>
#include <iostream>
#include <QDebug>
#include <QtMessageHandler>
#include <QFile>
#include <QDir>
using namespace std;
//写入信息到文件
void WriteLog(QString str, QString LogType)
{
QString fileFolder= qApp->applicationDirPath()+"/log/"+QDateTime::currentDateTime().toString("yyyy-MM-dd");
QDir dir(fileFolder);
if(!dir.exists())
{
dir.mkpath(fileFolder);
}
QString filePath=QString("%1/%2.log").arg(fileFolder).arg(LogType);
QString strToWrite=QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
strToWrite.append(str);
QFile file(filePath);
file.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream text_stream(&file);
text_stream <<strToWrite;
file.flush();
file.close();
//打印到控制台
std::cout << strToWrite.toLocal8Bit().constData() << std::endl;
}
//注册函数
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QString txtMessage = "";
QString messageType = "";
switch (type) {
case QtDebugMsg: //调试信息提示
messageType = "Debug";
txtMessage = QString("Debug: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);
break;
case QtInfoMsg:
messageType = "Info";
txtMessage = QString("Warning: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);
break;
case QtWarningMsg: //一般的warning提示
messageType = "Waring";
txtMessage = QString("Warning: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);
break;
case QtCriticalMsg: //严重错误提示
messageType = "Critical";
txtMessage = QString("Critical: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);
//PostErrorMessage(txtMessage,messageType);
break;
case QtFatalMsg: //致命错误提示
messageType = "Fatal";
txtMessage = QString("Fatal: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);
abort();
}
WriteLog(txtMessage, messageType);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//注册监听级别函数
qInstallMessageHandler(myMessageOutput);
qDebug() << "debug1";
qInfo() <<"info1";
qWarning() << "warni";
qFatal("error");
return a.exec();
}
qtcreater 输出如下:
日志文件如下:
2 log4qt简单使用
- 下载
Log4Qt官网下载(版本很老)
GitHub(推荐) - 加载到项目
可以用源码自行编译成库使用,或直接在项目内添加源码。
1 解压log4qt到目标文件夹,如D:\Qt\下;
2 qt项目的pro文件中加入一行:include(D:\Qt\log4qt\src\log4qt\log4qt.pri),
pri文件会将需要的h文件和cpp文件加入到qt项目中; - 简单使用
#include "logger.h"
#include "basicconfigurator.h"
Log4Qt::BasicConfigurator::configure();
Log4Qt::Logger * log = Log4Qt::Logger::rootLogger();
log->debug("debug!");
log->info("information!");
log->warn("warn");
log->log(logLevel, data);
//logLevel可以是上面的warn,info,debug等,data是需要输出的数据
- 配置Log4Qt
#include <QApplication>
#include "logger.h"
#include "propertyconfigurator.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//配置文件路径
Log4Qt::PropertyConfigurator::configure(a.applicationDirPath()
+ "/log4qt.conf");
Log4Qt::Logger *a1 = Log4Qt::Logger::logger("A1");
a1->debug("the message of log");
return a.exec();
}
log4qt.conf是配置文件,里面存放Log4Qt的配置信息
指定扩展器
log4j.logger.A1=DEBUG,CONSOLE,A1
####每天生产一个文件
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
日志文件路径
log4j.appender.A1.File=logs/a1.txt
输出DEBUG级别以上的日志
log4j.appender.D.Threshold = DEBUG
日志布局类型(自由模式)
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
日志格式
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
2 log4qt定制用法
配置了终端和日志文件同时输出
main.cpp
#include <QApplication>
#include "qblog4qt.h"
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
int main(int argc, char* argv[])
{
QApplication a(argc,argv);
QString str = "String";
char* p = "char*";
int i = 1;
Log4Info(str);
Log4Info(str,p);
Log4Info(str,p,i);
QVariantMap dataMap;
QVariantMap binaryMap;
binaryMap.insert("completeBinarysId","m_curUUid");
dataMap.insert("binarysState",binaryMap);
QJsonObject dataobject = QJsonObject::fromVariantMap(dataMap);
QByteArray t("zzzzzzz");
qDebug() << "test";
Log4qt << str;
Log4qt << QString(QJsonDocument(dataobject).toJson());
Log4qt << QString(t);
return 0;
}
log4qt类封装:
qblog4qt.h
#ifndef QBLOG4QT_H
#define QBLOG4QT_H
#include <QObject>
#include "log4qt/logger.h"
#include "log4qt_global.h"
#include "log4qt/logmanager.h"
#include "log4qt/propertyconfigurator.h"
#define Log4Debug QBLog4Helper(__FILE__, __LINE__, Q_FUNC_INFO).debug
#define Log4Info QBLog4Helper(__FILE__, __LINE__, Q_FUNC_INFO).info
#define Log4Warn QBLog4Helper(__FILE__, __LINE__, Q_FUNC_INFO).warn
#define Log4Error QBLog4Helper(__FILE__, __LINE__, Q_FUNC_INFO).error
#define Log4Fatal QBLog4Helper(__FILE__, __LINE__, Q_FUNC_INFO).fatal
#define Log4qt QBLog4Helper(__FILE__, __LINE__, Q_FUNC_INFO)
class QBLog4Qt : public QObject
{
Q_OBJECT
LOG4QT_DECLARE_QCLASS_LOGGER
public:
explicit QBLog4Qt(QObject *parent = 0);
~QBLog4Qt();
static QBLog4Qt *instance();
virtual void debug(const QString& log){logger()->debug(log);}
virtual void info (const QString& log){logger()->info(log);}
virtual void warn (const QString& log){logger()->warn(log);}
virtual void error(const QString& log){logger()->error(log);}
virtual void fatal(const QString& log){logger()->fatal(log);}
};
class QBLog4Helper{
Q_DISABLE_COPY(QBLog4Helper)
public:
Q_DECL_CONSTEXPR QBLog4Helper() : version(1), line(0), file(0), function(0) {}
QBLog4Helper(const char *fileName, int lineNumber, const char *functionName);
enum LogType{LGDebugMsg, LGInfoMsg, LGWarningMsg, LGErrorMsg, LGFatalMsg};
QString TemplateParameter (){ return ""; }
template <typename T, typename ... Args>
QString TemplateParameter(T head, Args ... args) { return QString("%1 ").arg(head) + TemplateParameter(args...); }
template <typename T, typename ... Args>
void debug(T head, Args ... args){
QString logmsg = QString("%1 ").arg(head) + TemplateParameter(args...);
writelogToLocal(LGDebugMsg,logmsg);
}
template <typename T, typename ... Args>
void info(T head, Args ... args){
QString logmsg = QString("%1 ").arg(head) + TemplateParameter(args...);
writelogToLocal(LGInfoMsg,logmsg);
}
template <typename T, typename ... Args>
void warn(T head, Args ... args){
QString logmsg = QString("%1 ").arg(head) + TemplateParameter(args...);
writelogToLocal(LGWarningMsg,logmsg);
}
template <typename T, typename ... Args>
void error(T head, Args ... args){
QString logmsg = QString("%1 ").arg(head) + TemplateParameter(args...);
writelogToLocal(LGErrorMsg,logmsg);
}
template <typename T, typename ... Args>
void fatal(T head, Args ... args){
QString logmsg = QString("%1 ").arg(head) + TemplateParameter(args...);
writelogToLocal(LGFatalMsg,logmsg);
}
virtual void writelogToLocal(LogType logtype, const QString& logmsg);
virtual void copy(const QBLog4Helper &logContext){}
template <typename T>
inline QBLog4Helper &operator<<(T logmsg) {
writelogToLocal(LGInfoMsg,QString("%1").arg(logmsg));
return *this;
}
int version;
int line;
const char *file;
const char *function;
};
#endif // QBLOG4QT_H
qblog4qt.cpp
#include "qblog4qt.h"
#include <QApplication>
#include <QStandardPaths>
#include <QSettings>
#include <QDir>
#include <QThread>
#include <QFileInfo>
QBLog4Qt::QBLog4Qt(QObject *parent) :
QObject(parent)
{
QString outpath = QCoreApplication::applicationDirPath() + "/z021.log";
QString configPath = QApplication::applicationDirPath() + "/log4qt.conf";
if(!QFile::exists(configPath)){//不存在配置文件 创建默认配配置文件
QSettings configSet(configPath,QSettings::IniFormat);
configSet.setIniCodec("UTF-8");
//console
configSet.setValue("log4j.rootLogger",QStringList()<<"INFO"<<"A1"<<"A2");
configSet.setValue("log4j.appender.A1","org.apache.log4j.ConsoleAppender");
configSet.setValue("log4j.appender.A1.layout","org.apache.log4j.PatternLayout");
configSet.setValue("log4j.appender.A1.layout.ConversionPattern","%d{yyyy-MM-dd HH:mm:ss} %m%n");
//file
configSet.setValue("log4j.appender.A2","org.apache.log4j.FileAppender");
configSet.setValue("log4j.appender.A2","org.apache.log4j.RollingFileAppender");
configSet.setValue("log4j.appender.A2.File",outpath);
configSet.setValue("log4j.appender.A2.AppendFile","true");
configSet.setValue("log4j.appender.A2.MaxFileSize","4096KB");
configSet.setValue("log4j.appender.A2.MaxBackupIndex","3");
configSet.setValue("log4j.appender.A2.layout","org.apache.log4j.PatternLayout");
configSet.setValue("log4j.appender.A2.layout.ConversionPattern","%d{yyyy-MM-dd HH:mm:ss} %m%n");
configSet.sync();
}else{// 存在修改日志保存路径
QSettings configSet(configPath,QSettings::IniFormat);
configSet.setIniCodec("UTF-8");
configSet.setValue("log4j.appender.A2.File",outpath);
configSet.sync();
}
Log4Qt::PropertyConfigurator::configure(configPath);
Log4Qt::LogManager::setHandleQtMessages(true);
logger()->info("start used log4qt!");
}
QBLog4Qt::~QBLog4Qt()
{
logger()->info("stop used log4qt!");
}
QBLog4Qt *QBLog4Qt::instance()
{
static QBLog4Qt obj;
return &obj;
}
QBLog4Helper::QBLog4Helper(const char *fileName, int lineNumber, const char *functionName)
: version(1)
, line(lineNumber)
, file(fileName)
, function(functionName)
{
}
void QBLog4Helper::writelogToLocal(QBLog4Helper::LogType logtype, const QString &log)
{
QString threadText = QStringLiteral("0x%1").arg(quintptr(QThread::currentThreadId()));
QString sfile(file);
#ifdef Q_OS_WIN
sfile = sfile.right(sfile.length() - (sfile.lastIndexOf("\\")+1));
#endif
#ifdef Q_OS_LINUX
sfile = sfile.right(sfile.length() - (sfile.lastIndexOf("/")+1));
#endif
QString sFun(function);
sFun = sFun.mid(sFun.indexOf(" ")+1,sFun.indexOf("(")-sFun.indexOf(" ")-1);
QString filter = QString("[%1] [%2 line(%3) pid(%4)] ").arg(sfile).arg(sFun).arg(line).arg(threadText);
switch (logtype) {
case LGDebugMsg: QBLog4Qt::instance()->debug(filter+log);
break;
case LGInfoMsg: QBLog4Qt::instance()->info(filter+log);
break;
case LGWarningMsg: QBLog4Qt::instance()->warn(filter+log);
break;
case LGErrorMsg: QBLog4Qt::instance()->error(filter+log);
break;
case LGFatalMsg: QBLog4Qt::instance()->fatal(filter+log);
break;
default: QBLog4Qt::instance()->info(filter+log);
break;
}
}
3 log4qt类说明
- Layout简介
Log4Qt提供了多种Layout对象,用于格式化日志输出,指定日志级别、线程名称、Logger名称、日期时间等信息。
Layout类是Log4Qt API中的抽象类。 PatternLayout:根据一个模式字符串输出日志事件;
SimpleLayout:输出日志事件的级别和消息;
TTCCLayout:输出日志事件的时间、线程名称、Logger名称和嵌套的诊断上下文信息。
PatternLayout和TTCCLayout通过PatternFormatter来实现格式化。当PatternFormatter解析模式字符串时,会根据发现的信息创建一个PatternConverter链,每个PatternConverter会处理LoggingEvent的某个成员。
转换字符:用于指定数据的类型,例如:类别、级别、日期、线程名称。Log4Qt中的转换字符有: c:Logger 名称。
d{format_string}:日期。参数 format_string 可选,用于格式化日期。 m:消息内容 p:消息级别
r:启动程序的相对时间 t:线程名称 x:NDC(嵌套的诊断上下文)名称 X:MDC(映射的诊断上下文)名称 F:文件名称 M:方法名称
L:行号 l:位置信息 n:平台相关的行分隔符,Windows:\r\n,Linux:\n
- NDC简介
NDC(Nested Diagnostic Context)即嵌套诊断上下文,是log4J用于存储上下文信息(context
information)的类,NDC采用栈的机制push和pop上下文,每个线程有独立的上下文,如果要存储的上下文信息是堆栈式的在选择NDC。
- MDC简介
MDC(Mapped Diagnositc Context)即映射诊断上下文,是log4J用于存储上下文信息(context
information)的类,MDC内部采用Hash容器实现,是线程独立的,但一个子线程会自动获得一个父线程MDC的copy,如果要存储的上下文信息是key/value式的选择MDC。
- PatternLayout
PatternLayout是Layout的一个派生类,如果想生成基于模式匹配的特定格式的日志信息,可以使用PatternLayout来进行格式化。
PatternLayout的枚举ConversionPattern定义了两个常用的模式
enum ConversionPattern
{
DEFAULT_CONVERSION_PATTERN,// "%m,%n"
TTCC_CONVERSION_PATTERN,//"%r [%t] %p %c %x - %m%n"
};
- SimpleLayout
SimpleLayout 是Layout的一个派生类,对日志消息的格式化只包含日志的级别和消息内容。
SimpleLayout示例
#include <QCoreApplication>
#include <log4qt/logger.h>
#include <log4qt/simplelayout.h>
#include <log4qt/consoleappender.h>
#include <log4qt/loggerrepository.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 创建SimpleLayout
Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger();
Log4Qt::SimpleLayout *layout = new Log4Qt::SimpleLayout();
layout->setFooter("end");
layout->setHeader("start");
layout->activateOptions();
// 创建ConsoleAppender
Log4Qt::ConsoleAppender *appender = new Log4Qt::ConsoleAppender(layout, Log4Qt::ConsoleAppender::STDOUT_TARGET);
appender->activateOptions();
logger->addAppender(appender);
logger->setLevel(Log4Qt::Level::DEBUG_INT);
logger->debug("Debug, Log4Qt!");
logger->info("Info, Log4Qt!");
// 关闭 logger
logger->removeAllAppenders();
logger->loggerRepository()->shutdown();
return a.exec();
}
// output:
// start
// DEBUG - Debug, Log4Qt!
// INFO - Info, Log4Qt!
// end
- TTCCLayout