Doc: Improve documentation for qInstallMessageHandler()

Mention that QtMessageHandler needs to be reentrant,
as well as other caveats. Mention QLoggingCategory,
so people do know that they don't have to necessarily
implement their own handler to filter messages (and that
not all messages reach the handler). Also mention
qFormatLogMessage().

Finally, give a more useful example for a custom
message handler that logs to a file. Note that the example
leaks a file handle at exit, but that is arguably not that
bad.

Pick-to: 6.5 6.6
Change-Id: I5be44167b266c9bbdbb0e94806bb024c9b352a32
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Kai Köhne 2023-10-11 08:50:14 +02:00
parent 7a87149c25
commit cb841b449a
2 changed files with 58 additions and 42 deletions

View File

@ -210,37 +210,26 @@ const TInputType &myMin(const TInputType &value1, const TInputType &value2)
//! [23] //! [23]
#include <qapplication.h> #include <QApplication>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) QtMessageHandler originalHandler = nullptr;
void logToFile(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{ {
QByteArray localMsg = msg.toLocal8Bit(); QString message = qFormatLogMessage(type, context, msg);
const char *file = context.file ? context.file : ""; static FILE *f = fopen("log.txt", "a");
const char *function = context.function ? context.function : ""; fprintf(f, "%s\n", qPrintable(message));
switch (type) { fflush(f);
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function); if (originalHandler)
break; *originalHandler(type, context, msg);
case QtInfoMsg:
fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
}
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
qInstallMessageHandler(myMessageOutput); originalHandler = qInstallMessageHandler(logToFile);
QApplication app(argc, argv); QApplication app(argc, argv);
... ...
return app.exec(); return app.exec();

View File

@ -2086,34 +2086,61 @@ void qErrnoWarning(int code, const char *msg, ...)
\relates <QtLogging> \relates <QtLogging>
\since 5.0 \since 5.0
Installs a Qt message \a handler which has been defined Installs a Qt message \a handler.
previously. Returns a pointer to the previous message handler. Returns a pointer to the previously installed message handler.
The message handler is a function that prints out debug messages, A message handler is a function that prints out debug, info,
warnings, critical and fatal error messages. The Qt library (debug warning, critical, and fatal messages from Qt's logging infrastructure.
mode) contains hundreds of warning messages that are printed By default, Qt uses a standard message handler that formats and
when internal errors (usually invalid function arguments) prints messages to different sinks specific to the operating system
occur. Qt built in release mode also contains such warnings unless and Qt configuration. Installing your own message handler allows you
QT_NO_WARNING_OUTPUT and/or QT_NO_DEBUG_OUTPUT have been set during to assume full control, and for instance log messages to the
compilation. If you implement your own message handler, you get total file system.
control of these messages.
The default message handler prints the message to the standard output Note that Qt supports \l{QLoggingCategory}{logging categories} for
under X11 or to the debugger under Windows. If it is a fatal message, the grouping related messages in semantic categories. You can use these
application aborts immediately after handling that message. Custom to enable or disable logging per category and \l{QtMsgType}{message type}.
message handlers should not attempt to exit an application on their own. As the filtering for logging categories is done even before a message
is created, messages for disabled types and categories will not reach
the message handler.
Only one message handler can be defined, since this is usually A message handler needs to be
done on an application-wide basis to control debug output. \l{Reentrancy and Thread-Safety}{reentrant}. That is, it might be called
from different threads, in parallel. Therefore, writes to common sinks
(like a database, or a file) often need to be synchronized.
To restore the message handler, call \c qInstallMessageHandler(0). Qt allows to enrich logging messages with further meta-information
by calling \l qSetMessagePattern(), or setting the \c QT_MESSAGE_PATTERN
environment variable. To keep this formatting, a custom message handler
can use \l qFormatLogMessage().
Example: Try to keep the code in the message handler itself minimal, as expensive
operations might block the application. Also, to avoid recursion, any
logging messages generated in the message handler itself will be ignored.
The message handler should always return. For
\l{QtFatalMsg}{fatal messages}, the application aborts immediately after
handling that message.
Only one message handler can be installed at a time, for the whole application.
If there was a previous custom message handler installed,
the function will return a pointer to it. This handler can then
be later reinstalled by another call to the method. Also, calling
\c qInstallMessageHandler(nullptr) will restore the default
message handler.
Here is an example of a message handler that logs to a local file
before calling the default handler:
\snippet code/src_corelib_global_qglobal.cpp 23 \snippet code/src_corelib_global_qglobal.cpp 23
Note that the C++ standard guarantees that \c{static FILE *f} is
initialized in a thread-safe way. We can also expect \c{fprintf()}
and \c{fflush()} to be thread-safe, so no further synchronization
is necessary.
\sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(), \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(),
{Debugging Techniques} {Debugging Techniques}, qFormatLogMessage()
*/ */
/*! /*!