Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Протоколирование работы приложения


Домашняя страница www.devdoc.ru

DevDoc - это новые статьи по программированию каждую неделю.

Заходи и читай!

Домашняя страница Письмо автору Архив рассылки Публикация статьи

Выпуск №15

Здравствуйте уважаемые подписчики, сегодня в номере:

Ответ на задачу

В предыдущем выпуске я предложил решить такую задачу:

Пусть у нас есть массив размером N. Нам надо написать процедуру, которая бы выполняла циклический сдвиг вправо на K элементов. K может быть любым, в т.ч. больше N. При этом запрещено:

  • выделять буфер соизмеримый по размеру с N и K
  • использовать рекурсию
  • Сложность алгоритма должна быть o(N).

Решения прислали Mr. Wanderer и Yuri V. Vasiyarov. Их можно посмотреть тут.

Хочу поздравить ребят. Они отлично справились с нетривиальной задачей.

Новое на сайте

Доработана статья про виртуальные функции. Обновленный вариант можно посмотреть здесь.


Постоянная ссылка на статью (с картинками): http://www.devdoc.ru/index.php/content/view/logging.htm

Автор: Кудинов Александр
Последняя модификация: 2007-04-02 20:41:04

Протоколирование работы приложения

Введение

В 2003 году я работал с заказчиком, который находился на расстоянии нескольких тысяч километров от меня. Сначала работа над проектом шла по накатанной схеме, которая давала хорошие результаты в прошлом. Постепенно дело приближалось к выпуску релиза. После того как заказчик просмотрел очередную промежуточную версию, я получил от него отчет об ошибке: «Ваша программа у меня не работает». Как всегда емко и понятно…. Не все заказчики владеют навыками тестирования.

Потом была длительная переписка, чтобы выяснить подробности. Затем мы обнаружили, что ошибка не воспроизводится на нашем железе. Удаленного доступа к компьютеру нет. Ехать ради этого на другой конец земного шара – глупо.

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

Требования к системе

При разработке механизма идентификации проблем на стороне заказчика я ставил перед собой следующие требования:

  1. Обнаружение причин краха программы.
  2. Сбор информации о среде выполнения. На первый взгляд это не очень важно, т.к. хорошая программа сама должна проверять доступность всех компонентов. С другой стороны, разные версии ОС или вспомогательных компонентов могут иметь немного разное поведение. Поэтому при воспроизведении ошибки очень помогает знание конфигурации на стороне пользователя.
  3. Сбор информации о действиях пользователя. Это необходимый шаг, т.к. пользователь часто не может внятно описать, что же он делал с программой.
  4. Протоколирование работы не должно влиять на работу приложения.

Решение

Для обнаружения причин краха программы очень удобно использовать мини-дампы. Об этом можно почитать в статье «Отладка приложений на C++. Часть 5 (мини-дампы)». Дополнительная функциональность неактивна до поры до времени и не видна пользователю. В случае же краха – вы всегда сможете получить от клиента снимок памяти и состояния процессора для анализа.

Для воплощения оставшихся требований замечательно подходит протоколирование работы программы. Идея была взята из Linux – там есть замечательная API функция, которая пишет сообщения в системный журнал.

Мы разработали свой улучшенный аналог для Windows. Функция имеет примерно такой прототип:

void WriteLog(int iDebugLevel, int  iLineNum, const char * strFileName, const char * strFuncName, const char *strDebugStr, ...);

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

Второй, третий и четвертый параметры определяют место формирования сообщения.

Последние два параметра определяют содержимое отладочного сообщения. Их формат полностью соответствует функции printf.

Для того, чтобы увеличить информативность сообщений, автоматизировать их создание и уменьшить объем кода мы сделали несколько макросов.

#define LOG_BEGIN_FUNCTION(fname) const char *___debug__func__name = #fname; 
 
#define WLOG_INFO(dbgstring) g_pLogFile->WriteLog(LOG_INFO, __LINE__, __FILE__, ___debug__func__name, dbgstring)
#define WLOG_WARN(dbgstring) g_pLogFile->WriteLog(LOG_WARN, __LINE__, __FILE__, ___debug__func__name, dbgstring)
#define WLOG_ERR(dbgstring) g_pLogFile->WriteLog(LOG_ERR, __LINE__, __FILE__, ___debug__func__name, dbgstring)
#define WLOG_FATAL_ERR(dbgstring) g_pLogFile->WriteLog(LOG_FATAL_ERR, __LINE__, __FILE__, ___debug__func__name, dbgstring)

К сообщениям программиста добавляется дополнительная информация: время, имя функции, файл и номер строки. Это очень сильно помогает найти место, которое сгенерировало сообщение. Все эти манипуляции делаются очень просто средствами препроцессора.

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

Модуль для записи протокола работы представляет определенный интерес. В простейшем случае вся информация пишется в текстовый файл «как есть». Недостаток метода в том, что вся информация открытая. Это облегчает анализ алгоритмов работы программы даже при отсутствии исходников.

Если совсем немного доработать код – можно записывать файл в сжатом виде. Мы использовали замечательную библиотеку Zlib. Она позволяет буквально парой строчек кода записать архивный файл. Также можно применять простейшие алгоритмы шифрования, чтобы скрыть содержимое файла от пользователя. Недостаток метода очевиден – при сжатии сильно снижается скорость работы. Потери производительности могут быть огромными, т.к. объем отладочной информации значительный.

После разных экспериментов и апробации системы протоколирования, мы остановились на обычном текстовом файле без всяких излишеств.

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

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

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

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

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

Обратите еще внимание на важность ошибки (int iDebugLevel). Утилита, которая отвечает за включение протоколирования, может запросить вывод только ошибок нужного типа.

Очень важно, чтобы библиотека для протоколирования работы программы была многопоточной.

Заключение

Протоколирование работы приложения – полезный инструмент разработчика. Даже в примитивном варианте он позволяет экономить массу времени на поиск ошибок на стороне клиента.

Эту идею можно развить и написать Фреймворк. Например, по файлу протокола можно реконструировать стек вызовов, переключение потоков и т.п. Хотя я не представляю, в каких случаях могут понадобиться такие функции.

Полезные материалы



Если вам нравиться эта рассылка рекомендуйте ее своим друзьям. Подписаться можно по адресу http://subscribe.ru/catalog/comp.soft.prog.devdoc

Copyright (C) Kudinov Alexander, 2006-2007

Перепечатка и использование материалов запрещена без писменного разрешения автора.


В избранное