Введение
Функция qDebug() — самый простой способ посмотреть, что происходит внутри программы на Qt. Но часто вывод qDebug в консоль работает не так, как ожидается: сообщения не появляются вовсе, русский текст превращается в «кракозябры», а в релизной сборке всё молчит. В статье разберём, почему так происходит, как настроить консоль для приложения и как написать собственный обработчик сообщений, чтобы полностью управлять выводом.
Функции логирования в Qt
Кроме qDebug(), в Qt есть ещё несколько функций для разных уровней важности:
qDebug()— отладочные сообщения;qInfo()— информационные сообщения;qWarning()— предупреждения;qCritical()— критические ошибки;qFatal()— фатальная ошибка, после которой программа завершается.
Все они работают по одной схеме: формируют строку и передают её в обработчик сообщений. По умолчанию обработчик пишет в стандартный поток ошибок stderr, а в Windows при запуске из отладчика — в окно отладочного вывода.
Почему вывод qDebug не виден в консоли
В Windows графическое приложение по умолчанию собирается без консольного окна. Поэтому при запуске exe-файла напрямую сообщения просто некуда выводить. Чтобы консоль появилась, в файле проекта qmake нужно добавить:
CONFIG += console
Если проект собирается через CMake, консоль включена, пока для цели не указан флаг WIN32_EXECUTABLE. Для отладочной сборки его можно отключить:
set_target_properties(myapp PROPERTIES
WIN32_EXECUTABLE $<NOT:$<CONFIG:Debug>>
)
В Qt Creator также можно включить опцию «Запускать в терминале» в настройках запуска проекта. Тогда программа стартует в отдельном окне консоли.
Сообщения пропали в релизной сборке
Если в проекте определён макрос QT_NO_DEBUG_OUTPUT, все вызовы qDebug() превращаются в пустые операции. Аналогично работают QT_NO_INFO_OUTPUT и QT_NO_WARNING_OUTPUT. Проверьте, не добавлены ли они в настройках сборки для Release. Ещё одна причина — правила логирования. Переменная окружения QT_LOGGING_RULES или файл qtlogging.ini могут отключать отдельные категории сообщений, например:
QT_LOGGING_RULES="*.debug=true"
Формат сообщений
Функция qSetMessagePattern() позволяет добавить к каждому сообщению время, уровень, имя файла и номер строки. Шаблон можно задать и через переменную окружения QT_MESSAGE_PATTERN:
qSetMessagePattern("[%{time hh:mm:ss.zzz}] %{type}: %{message} (%{file}:%{line})");
Имя файла и номер строки выводятся только в отладочной сборке или если определён макрос QT_MESSAGELOGCONTEXT.
Собственный обработчик сообщений
Когда стандартного поведения мало, можно установить свой обработчик через qInstallMessageHandler(). Он получает тип сообщения, контекст (файл, строку, функцию) и сам текст. Например, так можно вывести каждое сообщение с префиксом уровня:
#include <QCoreApplication>
#include <QDebug>
#include <cstdio>
#include <cstdlib>
// Функция для обработки сообщений
void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
Q_UNUSED(context);
const QByteArray text = msg.toUtf8();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s\n", text.constData());
break;
case QtInfoMsg:
fprintf(stderr, "Info: %s\n", text.constData());
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s\n", text.constData());
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s\n", text.constData());
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s\n", text.constData());
abort();
}
fflush(stderr);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Установка обработчика сообщений
qInstallMessageHandler(myMessageHandler);
qDebug() << "This is a debug message.";
qInfo() << "This is an info message.";
qWarning() << "This is a warning message.";
qCritical() << "This is a critical message.";
return a.exec();
}
В консоли мы увидим следующий вывод:

Вызов fflush(stderr) гарантирует, что сообщение появится сразу, а не задержится в буфере. Внутри обработчика нельзя снова вызывать qDebug(): это приведёт к бесконечной рекурсии.
Запись лога в файл
Тот же обработчик удобно использовать для сохранения сообщений в файл. Это особенно полезно для программ, которые работают у пользователей без консоли:
#include <QFile>
#include <QTextStream>
#include <QDateTime>
void fileMessageHandler(QtMsgType type, const QMessageLogContext &, const QString &msg)
{
static QFile file("app.log");
if (!file.isOpen())
file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
static const char *levels[] = {"DEBUG", "WARN", "CRIT", "FATAL", "INFO"};
QTextStream out(&file);
out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
<< " [" << levels[type] << "] " << msg << "\n";
out.flush();
}
Массив уровней упорядочен по значениям перечисления QtMsgType: QtDebugMsg = 0, QtWarningMsg = 1, QtCriticalMsg = 2, QtFatalMsg = 3, QtInfoMsg = 4. Если приложение многопоточное, запись в файл стоит защитить мьютексом.
Русский текст в консоли Windows
Если вместо кириллицы в консоли появляются непонятные символы, значит, кодировка консоли не совпадает с UTF-8. Проще всего переключить её в начале программы:
#ifdef Q_OS_WIN
#include <windows.h>
#endif
int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
SetConsoleOutputCP(CP_UTF8);
#endif
QCoreApplication a(argc, argv);
qDebug() << "Привет, мир!";
return a.exec();
}
Заключение
Вывод qDebug в консоль зависит от нескольких вещей: есть ли у приложения консольное окно, не отключены ли сообщения макросами или правилами логирования и совпадает ли кодировка. Для полного контроля используйте qSetMessagePattern() и собственный обработчик через qInstallMessageHandler(). С его помощью сообщения можно форматировать, фильтровать и сохранять в файл.