Глава 15.4 Сообщения об ошибках (Error Reporting)

February 16th, 2010 Begemot Posted in Учебники

wxWidgets дает нам весьма развитые средства для работы с сообщениями об ошибках, ведению лога. Не знали? А надо бы знать:)

Глава 15.4  Сообщения об ошибках (Error Reporting)

Иногда вам необходимо вывести информационное сообщение в консоли или в всплывающем окне, для помощи в отладке или в ситуации когда ошибка не может быть нормально обработана самим приложением. Для этих целей wxWidgets предоставляет набор функций логирования, использующих разные способы вывода сообщений . К примеру, если во время создание большого wxBitmap обнаружился недостаток свободной памяти, программа может использовать wxLogError для вывода сообщения об ошибки с использованием всплывающего диалога (рисунок 15-2). Или другой случай, вы просто хотите вывести значения аргументов функции в окно вывода отладочной информации – просто используйте wxLogDebug. Место, где сообщение реально появится (в диалоге, в окне отладчика или в стандартном потоке ошибок) зависит от имени используемой функции и от установленной в данный момент активной цели (wxLog target).

Рисунок 15-2. Окно диалога с логом

Рисунок 15-2. Окно диалога с логом

Все функции логирования имеют одинаковый синтаксис, подобный функциям printf и vprintf. Они принимают строку формата, в качестве первого аргумента, за которым следует произвольное количество аргументов или указатель на список аргументов (argument list pointer). К примеру:

wxString name(wxT("Calculation"));

int nGoes = 3;

wxLogError(wxT("%s does not compute! You have %d more goes."), name.c_str(), nGoes);

Библиотека поддерживает следующие функции логирования:

wxLogError используется для сообщений об ошибках которые должны быть показаны пользователю. По умолчанию, функция использует всплывающее диалоговое окно для информирования пользователя об ошибке. Почему бы просто не использовать wxMessageBox? Ну во-первых, вы можете запретить вывод сообщений об ошибках используя wxLogNull. Во-вторых, все ошибки переданные этой функции добавляются в специальную очередь и показываются пользователю во время простоя, в одном диалоге. Таким образом, в случае нескольких последовательных ошибок, пользователю не придется нажимать ОК в сообщении о каждой из них.

wxLogFatalError подобна wxLogError, но дополнительно прерывает выполнение программы использую стандартную функцию abort, выдавая 3, в качестве кода выхода. В отличии от всех остальных функций, ее вывод не может перенаправлен изменением цели логирования.

wxLogWarning подобна wxLogError, но сообщение выглядит для пользователя как информация, а не как ошибка.

wxLogMessage предназначена для всех нормальных, информационных сообщений. Она также выводит сообщения по умолчанию в всплывающем окне.

wxLogVerbose используется для подробного режима вывода сообщений. Обычно, сообщения переданные этой функции подавляются, но вы можете изменить это поведение просто вызвав wxLog::SetVerbose, если пользователь хочет знать больше деталей о работе программы.

wxLogStatus предназначена для показа статусной информации, сообщения отображаются в статус баре активного или указанного фрейма, при условии что он существует.

wxLogSysError по большей части используется самой библиотекой для логирования как каких-то определенных сообщений, так и информации о коде ошибки последней операции (значение errno или результат работы GetLastError в зависимости от платформы) и соответствующего сообщения об ошибке. Вторая форма этой функции непосредственно принимает код ошибки в качестве первого аргумента.

wxLogDebug функция для вывода отладочной информации. Сообщения отображаются только в отладочной версии программы (если определен __WXDEBUG__), и ничего не происходит в финальной сборке. При работе в Windows вы должны запускать программу под управлением отладчика или использовать сторонние программы (например DebugView http://www.sysinternals.com) для того, чтобы увидеть отладочные сообщения.

wxLogTrace подобно wxLogDebug, работает только в отладочной версии. Причина, по которой эта функциональность вынесена в отдельную функцию кроется в том, что обычно существует большое количество сообщений трассировки и есть смысл отделить их от остальной отладочной информации. Более того, вторая версия этой функции принимает первым аргументом специальную маску, которая позволяет вам в дальнейшем ограничивать количество генерируемых сообщений. К примеру, wxWidget внутри использует маску mousecapture. Если вы добавите эту строку к маске трасировки используя wxLog::AddTraceMask вы увидите сообщения трассировки при захвате мыши.

void wxWindowBase::CaptureMouse()

{

wxLogTrace(wxT("mousecapture"), wxT("CaptureMouse(%p) "), this);

...

}

void MyApp::OnInit()

{

// Add mousecapture to the list of trace masks

wxLog::AddTraceMask(wxT("mousecapture"));

...

}

Вы можете удивиться – почему бы не использовать стандартные средства ввода\вывода С или потоки библиотеки С++? Если кратко, то это хорошие обобщенный механизмы, но к сожалению они не адаптированны для wxWidgets, в которой есть специальные классы для ведения лога, имеющие три основных преимущества.

Во-первых, wxLog кроссплатформенный. Распространенная практика – использование возможностей printf или cout и cerr потоков в C++ для вывода информации. И несмотря на то что это замечательно работает в Unix системах, такой трюк не прокатит в Windows, где скорее всего эти сообщения просто не будут никуда выведены, так как stdout для графических приложений ни с чем не связан.

Таким образом, вы можете рассматривать wxLogMessage просто как замену для printf.

Также вы можете перенаправить вывод логов в cout просто написав:

wxLog *logger=new wxLogStream(&cout);

wxLog::SetActiveTarget(logger);

И даже больше, вы можете перенаправить вывод, посылаемый в cout, в текстовое поле (wxTextCtrl) используя wxStreamToTextRedirector класс.

Второе, wxLog – гибче. Вывод функций wxLog может быть перенаправлен или подавлен в зависимости от важности сообщений, чего весьма трудно или даже невозможно добиться используя традиционные методы. К примеру, можно выводить в лог только ошибки или только ошибки и предупреждения, отфильтровывая все остальные информационные сообщения.

И наконец, третье: wxLog более проработанный. Обычно, сообщение об ошибки должно быть показано пользователю в случае невозможности выполнить некоторую операцию. Давайте рассмотрим простой, но часто встречающийся случай ошибки работы с файлом: представьте, что вы пишите данные в файл и оказывается что это невозможно сделать, ввиду отсутствия достаточного свободного места. Ошибка может быть обнаружена внутри wxWidgets кода(например, в wxFile::Write), как следствие, вызывающая функция реально не знает в чем причина ошибки, она знает только то, что данные не могут быть записаны на диск. Однако, так как wxWidgets использует wxLogError в данной ситуации, точный код и соответствующее сообщение об ошибки будет показано пользователю.

Теперь давайте разберемся как работать с функциями логирования, в случае если вам необходимо нечто большее, чем стандартное поведение.

В wxWidgets существует такое понятие как цель логирования (log target) – по сути это просто класс наследник wxLog, который реализует виртуальные функции базового класса, отвечающие за логирование сообщений. В каждый момент времени только одна цель может быть активной, она же и используется функциями логирования для вывода сообщений. Обычное использование объекта логирования заключается в установке его в качестве активной цели с помощью вызова wxLog::SetActiveTarget, после чего он автоматически будет использоваться во всех дальнейших вызовах функций ведения лога. Для создания нового класса цели логирования, вам достаточно унаследовать его от wxLog и реализовать одну из функций DoLog или DoLogString (или обе).

Реализации DoLogString достаточно если вас устраивает стандартное оформление сообщений в wxLog (добавление в начало сообщения строки ‘Error’ или ‘Warning’ и текущего времени) и вы просто хотите перенаправлять сообщение в другое место. Вы можете заместить DoLog если вы хотите делать нечто большее, но вам придется самостоятельно обрабатывать различные уровни сообщений. Смотрите src/common/log.cpp в качестве примера того, как wxWidgets делает это.

Существует несколько готовых классов, наследников wxLog, которые вы можете использовать без изменений. К тому же, просмотр исходных текстов этих классов может оказаться полезным примером при реализации собственной цели логирования.

wxLogStderr логирует сообщения в переданный ей, в качестве аргумента, указатель на файл (FILE*), используя stderr, в качестве цели по умолчанию.

wxLogStream обладает такой же функциональностью что и wxLogStderr, но использует ostream и cerr вместо FILE* и stderr.

wxLogGui является стандартной целью логирования для приложений написанных с помощью wxWidgets и используется по умолчанию. Она использует наиболее подходящий алгоритм обработки всех типов сообщений для данной платформы.

wxLogWindow дает нам “консоль для логов” (“log console”), которая выводит все сообщения генерируемые приложением и, дополнительно, отправляет их в предыдущую цель логирования. Окно этой консоли имеет меню, позволяющие пользователю очистить лог, закрыть его или сохранить все сообщения в файл.

wxLogNull может быть использовано для временного подавления вывода функций логирования. В качестве примера, попробуем открыть не существующий файл, что должно привести к появлению сообщения об ошибке, которое мы, по какой-то причине, в этом месте не хотим видеть. Все, что нам необходимо сделать, это создать объект класса wxLogNull на стеке. В результате, в области жизни данного объекта любой вывод логирующих функций будет подавлен. Вы можете использовать дополнительную пару скобок для создания нужной области жизни объекта. К примеру:

wxFile file;

// wxFile.Open() normally complains if file can't be opened;

// we don't want it to

{

wxLogNull logNo;

if ( !file.Open("bar") )

... process error ourselves ...

} // ~wxLogNull called, old log target restored      wxLogMessage("..."); // ok

В ваших силах объединять несколько целей в цепочку: к примеру, мы можете перенаправлять сообщения куда-нибудь (например в файл), но при этом позволить им обрабатываться так же как обычно. Для этого, вы может использовать wxLogChain и wxLogPassThrough. К примеру:

// This implicitly sets the active log target

wxLogChain *logChain = new wxLogChain(new wxLogStderr);

// all the log messages are sent to stderr and also processed

// as usual

// don't delete logChain directly as this would leave a dangling

// pointer as active log target; use SetActiveTarget instead

delete wxLog::SetActiveTarget(new wxLogGui);

wxMessageOutput и wxLog

Иногда, wxLog, ввиду его высокоуровневости, не самый подходящий для использования класс. Например, так как он поддерживает автоматическое добавление времени и отложенный показ сообщений. wxMessageOutput класс и его наследники предлагают низкоуровневую замену printf, которую вы можете использовать как к консольных приложениях, так и в программах с графическим пользовательским интерфейсом. Используйте wxMessageOutput::Printf когда вам необходим printf; К примеру, для записи ошибки:

#include "wx/msgout.h"

wxMessageOutputStderr err;

err.Printf(wxT("Error in app %s.\n"), appName.c_str());

Вы также можете использовать wxMessageOutputDebug для вывода сообщений в окно отладчика или стандартный поток ошибок, в зависимости от платформы или того запущенна ли программа под управлением отладчика. В отличии от wxLogDebug, эти вызовы не удаляются из релизной версии.

GUI программы могут использовать wxMessageOutputMessageBox для немедленного показа сообщения в диалоговом окне, без сортировки и отложенного показа как это делает wxLog. Так же доступен класс wxMessageOutputLog, который направляет сообщения в wxLogMessage.

Как и wxLog, wxMessageOutput поддерживает концепцию текущей активной цели. Используйте wxMessageOutput::Set для установки и wxMessageOutput::Get для получения текущей активной цели. Эта цель устанавливается в соответствующий объект при инициализации библиотеки: экземпляр wxMessageOutputStderr используется для консольных приложений, и wxMessageOutputMessageBox для GUI программ. wxWidgets сама использует данных механизм, к примеру в wxCmdLineParser, следующим образом:

wxMessageOutput* msgOut = wxMessageOutput::Get();

if ( msgOut )

{

wxString usage = GetUsageString();

msgOut->Printf( wxT("%s%s"), usage.c_str(), errorMsg.c_str() );

}

else

{

wxFAIL_MSG( _T("no wxMessageOutput object?") );

}
Related:

One Response to “Глава 15.4 Сообщения об ошибках (Error Reporting)”

  1. Нихрена ничего не понял. Как организовать вывод сообщений в файл, чтобы можно было на досуге посмотреть лог выполнения программы?