若該文為原創(chuàng)文章,轉載請注明原文出處
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/130762721文章來源地址http://www.zghlxwxcb.cn/news/detail-450570.html
紅胖子網絡科技博文大全:開發(fā)技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續(xù)更新中…
Qt開發(fā)專欄:三方庫開發(fā)技術
上一篇:《Qt+QtWebApp開發(fā)筆記(一):QtWebApp介紹、下載和搭建基礎封裝http輕量級服務器Demo》
下一篇:《Qt+QtWebApp開發(fā)筆記(三):http服務器動態(tài)html連接跳轉基礎交互》文章來源:http://www.zghlxwxcb.cn/news/detail-450570.html
前言
??上一篇使用QtWebApp的基于Qt的輕量級http服務器實現(xiàn)了一個靜態(tài)網頁返回的Demo,網頁服務器很重要的就是日志,因為在服務器類上并沒有直接返回,所以,本篇先把日志加上。
Demo
??
下載地址
??鏈接:https://pan.baidu.com/s/1BPVRLS07qk-WPi-txERKbg?pwd=1234
日志系統(tǒng)
生產環(huán)境需要查看舊的日志消息,例如兩天前的日志消息。
??可以簡單地將輸出重定向到一個文件(MyFirstWebApp>logfile.txt),但這有兩個問題:
- 在許多系統(tǒng)上,輸出重定向有些慢。
- 日志文件將變得無限大,如果不短時間停止web服務器,就無法防止這種情況發(fā)生。
??因此,最好讓web服務器自己將所有消息寫入文件。這就是記錄器模塊的作用。
??要將日志模塊的源代碼包括到項目中,請在項目文件中添加一行:
include(../QtWebApp/QtWebApp/logging/logging.pri)
??這個而模塊也是QtWebApp的logging模塊,如下:
??
??然后在程序的*.ini文件中添加另一個部分:
[logging]
minLevel=WARNING
bufferSize=100
fileName=../logs/webapp1.log
maxSize=1000000
maxBackups=2
timestampFormat=dd.MM.yyyy hh:mm:ss.zzz
msgFormat={timestamp} {typeNr} {type} {thread} {msg}
??日志級別有:DEBUG(別名ALL)、INFO、WARN或WARNING、CRITICAL(別名ERROR)、FATAL。信息級別由Qt 5.5引入。
??上面的示例配置啟用線程本地緩沖區(qū),**這些緩沖區(qū)將不太重要的消息保留在內存中,直到出現(xiàn)警告或嚴重錯誤。**然后,錯誤消息與收集到的低級消息一起寫入日志文件。只要一切正常,使用緩沖區(qū)可以大大減少日志文件的大小。像這樣的系統(tǒng)操作員。
??但是,緩沖區(qū)的內存和性能成本都很高。收益通常大于成本。要禁用緩沖區(qū),請將bufferSize設置為0。在這種情況下,只有配置了minLevel及以上級別的消息才會寫入日志文件。
??如果沒有指定文件名,則記錄器會寫入控制臺。日志文件的路徑可以是絕對路徑,也可以是相對于配置文件的文件夾的路徑。maxSize參數(shù)限制日志文件的大小(以字節(jié)為單位)。當超過此限制時,記錄器將啟動一個新文件。設置maxBackups指定磁盤上應保留多少舊日志文件。
??時間戳格式設置的作用。QDateTime::toString()的文檔以獲得對字符的解釋,還有更多可用的內容。msgFormat設置指定每條消息的格式。以下字段可用:
- {timestamp}:創(chuàng)建日期和時間
- {typeNr}:數(shù)字格式的消息類型或級別(0=DEBUG, 4=INFO, 1=WARNING, 2=CRITICAL, 3=FATAL)
- {type}:字符串格式的消息類型或級別(DEBUG, INFO, WARNING, CRITICAL, FATAL)
- {thread}:線程的ID號
- {msg}:消息文本
- {xxx}:可以自己定義的任何記錄器變量QT 5.0及更新版本在調試模式下有一些附加變量:
{file}:Filename of source code where the message was generated
{function}:Function where the message was generated
{line}:Line number where the message was generated
??Qt開發(fā)人員將這三個字段添加到他們的框架中。也可以使用\n在消息格式中插入換行符和插入??制表符。上述所有變量也可以在日志消息中使用,例如:
qCritical("An error occured in {file}: out of disk space");
??需要一個指向FileLogger實例的全局指針,以便整個程序都可以訪問它。首先添加到global.h:
#include "httpsessionstore.h"
#include "staticfilecontroller.h"
#include "templatecache.h"
#include "filelogger.h"
using namespace stefanfrings;
/** Storage for session cookies */
extern HttpSessionStore* sessionStore;
/** Controller for static files */
extern StaticFileController* staticFileController;
/** Cache for template files */
extern TemplateCache* templateCache;
/** Redirects log messages to a file */
extern FileLogger* logger;
#endif // GLOBAL_H
??global.cpp:
#include "global.h"
HttpSessionStore* sessionStore;
StaticFileController* staticFileController;
TemplateCache* templateCache;
FileLogger* logger;
??在main.cpp中,配置FileLogger的實例:
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QString configFileName=searchConfigFile();
// Configure logging
QSettings* logSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
logSettings->beginGroup("logging");
logger=new FileLogger(logSettings,10000,&app);
logger->installMsgHandler();
// Log the library version
qDebug("QtWebApp has version %s",getQtWebAppLibVersion());
// Session store
QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
sessionSettings->beginGroup("sessions");
sessionStore=new HttpSessionStore(sessionSettings,&app);
// Static file controller
QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
fileSettings->beginGroup("files");
staticFileController=new StaticFileController(fileSettings,&app);
// Configure template cache
QSettings* templateSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
templateSettings->beginGroup("templates");
templateCache=new TemplateCache(templateSettings,&app);
// HTTP server
QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
listenerSettings->beginGroup("listener");
new HttpListener(listenerSettings,new RequestMapper(&app),&app);
return app.exec();
}
??數(shù)字10000是以毫秒為單位的刷新間隔,記錄器使用它來重新加載配置文件。因此,可以在程序運行時編輯任何記錄器設置,并且更改在幾秒鐘后生效,而無需重新啟動服務器。如果不希望自動重新加載,請使用值0。
??給了一個示例代碼,用于查詢和記錄庫的版本號。一些人要求添加該功能。
不要忘記創(chuàng)建一個空文件夾MyFirstWebApp/logs。記錄器本身不會創(chuàng)建文件夾。
??現(xiàn)在可以啟動應用程序并查看會發(fā)生什么。因為程序沒有錯誤,所以日志文件保持為空。但??可以看到控制臺窗口中的輸出已降至最低:
??
??讓在logincontroller.cpp中插入一條qCritical()消息,然后可以看到日志緩沖區(qū)工作:
??然后打開URLhttp://localhost:8080/login?username=test&password=wrong.
??再次查看日志文件,它就在那里:
??
??現(xiàn)在通過將min Level降低到DEBUG來進行另一個測試。保存ini文件,等待10秒,然后打開URLhttp://localhost:8080/hello.再次檢查日志文件??梢钥吹剑M管沒有發(fā)生錯誤,但現(xiàn)在所有的調試消息都已寫入。因此,在不重新啟動程序的情況下更改日志級別可以很好地工作。
??
??其實這個很容易看出來,是直接對qt的幾個日志等級進行了(PS:這個日志庫還不錯,installMsgHandler可以截斷qDebug等相關的錯誤信息,可以直接無縫使用到每一個qt項目中,有這個興趣可以試一試)。
??
記錄器變量
??寫到記錄器支持用戶定義的變量。這些變量是線程本地的,在清除它們之前一直保留在內存中。對于web應用程序,在每條消息中記錄當前用戶的名稱可能很有用。向requestmapper.cpp添加代碼以設置記錄器變量:
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data());
HttpSession session=sessionStore->getSession(request,response,false);
QString username=session.get("username").toString();
logger->set("currentUser",username);
...
}
??通過這種方式,請求映射器在將請求傳遞給控制器類之前,為所有傳入的HTTP請求查詢調用用戶的名稱。
??現(xiàn)在可以修改ini文件以使用該變量:
msgFormat={timestamp} {typeNr} {type} {thread} User:{currentUser} {msg}
??運行程序并打開URLhttp://localhost:8080/login?username=test&password=hello兩次。然后再次檢查日志文件:
??
??在用戶登錄之前,可以看到變量{currentUser}為空。然后,所有以下請求都會以該用戶的名稱記錄。
??注意:在RequestMapper類中放置了許多靜態(tài)資源(logger、sessionStore、staticFileController、templateCache)。在實際應用程序中,建議創(chuàng)建一個單獨的類,例如名稱為“Globals”的類,這樣每個人都知道在哪里可以找到這樣的資源?;蛘甙凑赵贒emo1項目中的例子,將它們放在任何類之外的cpp源文件中。
日志緩沖區(qū)和線程池
??由于線程被重新用于后續(xù)的HTTP請求,記錄器可能會輸出更多的細節(jié)。例如,假設第一個成功的HTTP請求會產生一些隱藏的調試消息,然后由同一線程處理的第二個請求會產生錯誤。然后,日志文件將包含錯誤消息以及所有緩沖的調試消息。但其中一些來自以前的HTTP請求,并不需要它。
??要清除兩個HTTP請求之間的緩沖區(qū),請?zhí)砑拥絩equestmapper.cpp:
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
...
else {
response.setStatus(404,"Not found");
response.write("The URL is wrong, no such document.");
}
qDebug("RequestMapper: finished request");
logger->clear(true,true);
}
??因此,每當HTTP請求的處理完成時,都要清理記錄器的內存。當同一個線程處理下一個請求時,它將以空緩沖區(qū)開始。(碰到錯誤則會輸出到文件,所以一個http請求完成了,就是其前面的日志都是無錯誤,所以可以清空了)。
雙文件記錄器
??該項目還包含一個DualFileLogger類,可用于并行生成兩個日志文件。這可能對以下設置組合有用:
- 主記錄日志文件
minLevel=INFO
bufferSize=0
- 第二日志文件
minLevel=ERROR (or WARNING)
bufferSize=100
??這樣,主日志文件就不包含調試消息。但是,當發(fā)生錯誤時,輔助日志文件會包含該錯誤以及多達100條相關的調試消息。如果錯誤消息本身無法識別問題原因,則此文件特別有用。
總結
??這個日志logging模塊起到的最大作用,是因為在QtWebApp三方源碼中的qDebug,qWarn,QFatal等相關系統(tǒng)直接輸出到控制臺的,使用該日志則截斷才可以獲取httpservice模塊以及其他模塊中的打印調試信息,而這些信息是在函數(shù)返回值中沒有體現(xiàn)的。
??為了能查看到三方模塊日志,則必須要使用logging模塊,或者自己寫一個模塊去截斷,或者直接修改三方源碼中的調試信息的代碼。
??使用httpservice肯定是最好使用logging模塊了。
Demo增量:添加logging日志模塊
步驟一:準備代碼模板
??準備之前的demo模板:
??
步驟二:拷貝logging模塊
??將QtWebApp中的logging,符合模塊化設計準則,如下圖:
??
??拷貝到的Demo
??
??添加模塊進入工程:
# logging模塊,QtWebApp自帶的三方模塊
include ($$PWD/modules/logging/logging.pri)
??
??第三方的模塊。
步驟三:添加配置logging的配置文件
??先把上一篇的Demo配置文件加了listener之后就讀不出的問題解決了,其實區(qū)別關鍵在下面:
??
??beginGroup就是進入了這一組,這一組拿到key就可以不帶前綴。
??
??然后開始添加日志配置,也在httpServerManager,因為配置文件beginGroup之后就是操作單獨一組了,這里從第三方源碼中也可以看出來:
??
??本次加入logging,也要進行配置文件分組的區(qū)分,原來的_pSettings改成_pHttpListenerSettings,然后新增_pLoggingListenerSettings用于配置logging模塊的配置實例:
步驟四:新增logging日志代碼
??
??
??
步驟五:運行結果
??
??至此,日志加入成功
步驟六:日志配置調整
??
??修改下日志時間:
??
??記錄日志則是:
??
Demo源碼
HttpServerManager.h
#ifndef HTTPSERVERMANAGER_H
#define HTTPSERVERMANAGER_H
#include <QObject>
#include <QMutex>
#include "httplistener.h"
#include "filelogger.h"
#include "HelloWorldRequestHandler.h"
class HttpServerManager : public QObject
{
Q_OBJECT
private:
explicit HttpServerManager(QObject *parent = 0);
public:
static HttpServerManager *getInstance();
public slots:
void slot_start(); // 開啟線程
void slot_stop(); // 停止線程
private:
static HttpServerManager *_pInstance;
static QMutex _mutex;
private:
bool _running; // 運行狀態(tài)
private:
HttpListener *_pHttpListener; // http服務監(jiān)聽器
QSettings *_pHttpListenerSettings; // http服務器配置文件
FileLogger *_pFileLogger; // 日志記錄
QSettings *_pFileLoggerSettings; // 日志配置文件
private:
QString _ip; // 服務器監(jiān)聽ip(若為空,則表示監(jiān)聽所有ip)
quint16 _port; // 服務器監(jiān)聽端口
int _minThreads; // 空閑最小線程數(shù)
int _maxThreads; // 負載最大線程數(shù)
int _cleanupInterval; // 空線程清空間隔(單位:毫秒)
int _readTimeout; // 保持連接空載超時時間(單位:毫秒)
int _maxRequestSize; // 最大請求數(shù)
int _maxMultiPartSize; // 上載文件最大數(shù)(單位:字節(jié))
};
#endif // HTTPSERVERMANAGER_H
HttpServerManager.cpp
#include "HttpServerManager.h"
#include <QApplication>
#include <QDir>
#include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")
HttpServerManager *HttpServerManager::_pInstance = 0;
QMutex HttpServerManager::_mutex;
HttpServerManager::HttpServerManager(QObject *parent)
: QObject(parent),
_pHttpListener(0),
_pHttpListenerSettings(0),
_pFileLogger(0),
_pFileLoggerSettings(0),
_running(false),
_port(8088),
_minThreads(2),
_maxThreads(10),
_cleanupInterval(60000),
_readTimeout(60000),
_maxRequestSize(100),
_maxMultiPartSize(1024*1024*1024)
{
}
HttpServerManager *HttpServerManager::getInstance()
{
if(!_pInstance)
{
QMutexLocker lock(&_mutex);
if(!_pInstance)
{
_pInstance = new HttpServerManager();
}
}
return _pInstance;
}
void HttpServerManager::slot_start()
{
if(_running)
{
LOG << "It's running!!!";
return;
}
_running = true;
LOG << "Succeed to run";
QString httpServerPath = QString("%1/etc/httpServer.ini").arg(qApp->applicationDirPath());
LOG << httpServerPath << "exit:" << QFile::exists(httpServerPath);
// 啟動日志幾里路
{
if(!_pFileLoggerSettings)
{
_pFileLoggerSettings = new QSettings(httpServerPath, QSettings::IniFormat);
}
_pFileLoggerSettings->beginGroup("logging");
// 日志不會主動創(chuàng)建文件夾,這里需要補全
{
QFileInfo fileInfo(httpServerPath);
QString dirPath = fileInfo.dir().absolutePath();
dirPath = QString("%1/%2")
.arg(dirPath)
.arg(_pFileLoggerSettings->value("fileName").toString());
dirPath = dirPath.mid(0, dirPath.lastIndexOf("/"));
QDir dir;
dir.mkpath(dirPath);
}
_pFileLogger = new FileLogger(_pFileLoggerSettings);
_pFileLogger->installMsgHandler();
}
// 啟動http的監(jiān)聽
{
if(!_pHttpListenerSettings)
{
_pHttpListenerSettings = new QSettings(httpServerPath, QSettings::IniFormat);
}
_pHttpListenerSettings->beginGroup("listener");
_pHttpListener = new HttpListener(_pHttpListenerSettings, new HelloWorldRequestHandler);
}
LOG;
}
void HttpServerManager::slot_stop()
{
if(!_running)
{
LOG <<"It's not running!!!";
return;
}
_running = false;
LOG << "Succeed to stop";
}
工程模板v1.1.0
??
入坑
入坑一:日志一直不出來
問題
??日志一直不出來
原因
??
??日志log文件的路徑是基于ini配置文件的相對路徑
解決
??
上一篇:《Qt+QtWebApp開發(fā)筆記(一):QtWebApp介紹、下載和搭建基礎封裝http輕量級服務器Demo》
下一篇:《Qt+QtWebApp開發(fā)筆記(三):http服務器動態(tài)html連接跳轉基礎交互》
若該文為原創(chuàng)文章,轉載請注明原文出處
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/130762721
到了這里,關于Qt+QtWebApp開發(fā)筆記(二):http服務器日志系統(tǒng)介紹、添加日志系統(tǒng)至Demo測試的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!