Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Реализуем пригодный лог на основе потоков

Anna | 25.06.2014 | нет комментариев

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

Да, дозволено сослаться на огромное число низкоквалифицированного персонала, заработная плата которого зависит от числа написанных строк кода либо от знания длинно глядеть на монитор, не моргая. Но такие работники есть фактически во всех отраслях. Строители имеют больше низкую квалификацию, чем архитекторы, но это не мешает зданиям в большинстве своем быть пригодными для полновесного применения без дополнительных «заплаток».

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

Вторая повод значительно больше прозаична. Невнимание к мелочам. Исключительно в начале плана. И чем моложе команда, тем результат катастрофичнее. Безусловно, значительно увлекательнее обговаривать перспективы применения мультиметодов [1], чем следить за тем, Дабы операторы отделялись пробелами. Да и к финальной функциональности сходственные мелочи специального отношения не имеют. Не отменнее ли вначале сконцентрироваться на первоочередных требованиях, чай время плана и бюджет ограничены…

Оказывается, еще как не отменнее. Это как дом без основы. Клиент просил просторные помещения, лифт, санузел на всяком этаже – про основа он, допустимо, не знает. Но добротное основание, способное вынести все сооружение, нужно. И о нем в первую очередь обязаны позаботиться исполнители – от зодчего до строителя.

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

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

  • Полный интерфейс
  • Кроссплатформенность
  • Итог сообщений в консоль IDE
  • Удобство и простота применения
  • Средства привлечения внимания разработчика

Полный интерфейс. Модуль ведения журналов должен представлять полный комплект операций с самого начала разработки. Интерфейс может быть не всецело реализован, впрочем он должен быть. Так дюже комфортно иметь вероятность разделять диагностические сообщения по степени критичности (оплошность, предупреждение, информация). Невозможно сделать вначале один вид сообщения, а потом ввести распределение – часть системы теснее будет разработана с этим обобщенным итогом, если даже принудить людей перекопать все сделанное, едва ли разработчики исполнят эту работу так правильно, как сделали бы это сразу.

Зачастую теснее через неделю глядишь на свой код, как на Сторонний. ?сно, что по прошествии нескольких месяцев ряд ньюансов реализации (в частности, что является скептической задачей, а что легко незначительной внештатной обстановкой) будут утрачены навечно.

Кроссплатформенность. Подсистема журналирования должна первоначально поддерживать все используемые платформы. Это исключительно значимо, когда нет вероятности полновесно и непрерывно тестировать работу приложения на всех операционных системах. То, что восхитительно работало на iOS, может необычно себя вести на Android-устройстве и напротив. Безусловно, система итога сообщений не заменяет механические тесты. Но они довольно трудны, внедрение и помощь требует существенных трудозатрат и, морально это либо нет, Зачастую о тестах успешно забывают. Следственно я считаю, что отменнее начинать с полновесно работающего минимума, чем ничего не делать, мечтая об идеале. Правда в любом случае в идеале обязаны быть и тесты и комфортная подсистема ведения лога.

Итог сообщений в консоль IDE. Обратите внимание, в список базовых требований не вынесено “Вывод сообщений в непрерывное хранилище (файл либо базу данных)”. Правда обыкновенно это как раз то, чего многие ожидают от лога. В файле журнала возникает надобность, когда хоть как-то работающая система отдается на тестирования грядущим пользователям либо особому отделу. Но на исходных этапах значительно пригоднее ставить в популярность о загвоздках программистов, чем тестировщиков.

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

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

  1. Привести четыре числа к строковому типу
  2. Из полученных частей составить строку
  3. Передать строку в лог

Даже если язык предоставляет средства итога в форматированную строку (скажем, sprintf [2] и stringstream [3]) понадобится создавать разные промежуточные переменные, терять время, размывать основную логику… В всеобщем не стоит изумляться, если в итоге в лог будет выведено что-то типа “недопустимый размер изображения”. Толк слова “недопустимый”, такой внятный теперь, через пару недель будет утерян фактически всецело. В итоге дизайнер, взамен того, Дабы поправить проблемную картинку, вынужден будет обращаться к программисту за разъяснениями.

Следственно подсистема ведения журнала должна предоставлять средства комфортного форматирования выводимых строк, не принуждая разработчика вручную приводить к строковым правда бы базовые типы. Тут в C допустимы два направления.

Первое – достояние из C. Форматирование строки по образцу. Т.е. функция итога в лог должна владеть интерфейсом и функциональностью, аналогичной sprintf. Это дело вкуса, лично мне данный вариант велико несимпатичен. В первую очередь отталкивает надобность работы с переменным числом разнотипных параметров функции. Т.е. вероятность проверки входных данных фактически никакая. Помимо того, хоть и упрощенная, но надобность ручного приведения типов присутствует. Скажем, необходимо помнить, что для итога беззнакового числа следует использовать код форматирования %u взамен %i – вовсе не хочется припоминать сходственное, когда голова занята решением иной задачи. Плюс допустимы неуловимые на этапе компиляции задачи, когда не совпадает число (порядок, тип) кодов форматирования и реально переданных параметров.

2-й вариант – потоки итога. Всех недостатков предыдущего решения он лишен: нет зависимости параметров, если передан объект, не поддерживающий оператора итога – будет оплошность компиляции, а не всевозможные сюрпризы, не необходимо вручную выделять буферы, приводить типы и т.п. И основное – итог строки сообщения с включением переменных различных типов дозволено реально записать в одну строку. Да и с реализацией специальных трудностей не предвидится – стандартная библиотека теснее содержит поток stringstream [3], что наводит на мысль о наличии готовых алгорифмов потокового итога в строку.

Средства привлечения внимания разработчика. Программист занят. Он может не подметить ошибку даже в консоли итога IDE. Задача может легко потеряться среди тучи информационных сообщений. Следственно, во-первых, лог должен поддерживать настройку яруса итога. Скажем, “выводить только ошибки и предупреждения”. Это необходимо, но этого немного – настройку требуется время от времени исполнять. А программист занят.

Тут приходит на выручку особый макрос assert [4]. Он разрешает прекрастить выполнение программы на проблемном месте. Итог получается различный – Visual Studio дает возrmark!#endif exit(EXIT_FAILURE); }
Выходит, первым делом проверяется, что ярус подробности допускает итог ошибок, после этого вызывается рабочий способ writeMessage, которому передается сообщение и тип сообщения. В итоге будет выведено что-то сходственное
error: [message]

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

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

В свою очередь writeMessage выглядит дальнейшим образом

void Log::writeMessage(const std::string& message, MessageType type)
{
    std::string text(message);
    std::replace(text.begin(), text.end(), 'n', ' ');
    appendToFile(text);
#ifdef _DEBUG
    writeIDEDebugString(text, type);
#endif
}

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

Отладочный режим определяется по макросу _DEBUG, тот, что обыкновенно механически определен в Visual Studio. В других средах разработки, скорее каждого, его необходимо будет добавлять вручную. В любом случае специальных трудностей это не вызывает.


Рисунок 2. Определения макроса-знака отладки для XCode

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

Windows (Visual Studio)
void Log_Windows::writeIDEDebugString(const std::string& message, MessageType type)
{
	OutputDebugStringA(message.c_str());
	OutputDebugStringA("n");
}
Android (Eclipse)

void Log_Android::writeIDEDebugString(const String& message, MessageType type)

{
	switch(type){
		case MESSAGE_INFO:
			__android_log_print(ANDROID_LOG_INFO, "", message.c_str());
			break;
		case MESSAGE_WARNING:
			__android_log_print(ANDROID_LOG_WARN, "", message.c_str());
			break;
		case MESSAGE_ERROR:
			__android_log_print(ANDROID_LOG_ERROR, "", message.c_str());
			break;
	}
}

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


Рисунок 3. Выделение цветом сообщений в зависимости от типа в Eclipse

MacOS и iOS (XCode)
void Log_Mac::writeIDEDebugString(const std::string& message, MessageType type)
{
    NSLog(@"%s", message.c_str());
}

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

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

std::cout << std::uppercase << "test" << 'n';

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

Log::error<<"text"<<std::endl;
Log::warning<<"text"<<std::endl;
Log::message<<"text"<<std::endl;

Т.е. информацию о типе сообщения будет содержать сам объект потока. Безусловно, стандартными параметрами потоков типа std::uppercase все еще дозволено пользоваться. Каждая функциональность потоков унаследована от классов стандартной библиотеки.

class Streamer : public std::ostream {
    public:

        Streamer(MessageType messageType);
        ~Streamer();

    private:
        class StringBuffer : public std::stringbuf {
        public:
            Buffer(MessageType messageType);
            ~Buffer();
            virtual int sync();

        private:
            MessageType mMessageType;
        };
    };

Для всякого вида потока будет свой объект, следственно конструкторы принимают параметр MessageType. Сам класс итога мы наследуем от std::ostream [5], за образование строки будет отвечать вложенный класс StringBuffer, тот, что в свою очередь унаследован от std::stringbuf [6]. Всякий раз, когда пользователь будет информировать о заключении образовании сообщения, будет механически вызван способ sync, в котором мы и исполним непринужденный итог. Осведомить о заключении итога дозволено стандартными для буферизируемых потоков методами: с поддержкой вызова способа flush

Log::message<<"text";
Log::message.flush();

либо легко добавив в поток std::endl

Log::message<<"text"<<std::endl;

Также необходимо связать поток итога со строковым буфером. Это выполняется в конструкторе

Log::Streamer::Streamer(Log::MessageType messageType)
: std::ostream(new StringBuffer(messageType))
    {
    }

Соответственно при уничтожении, буфер необходимо самосильно истребить

Log::Streamer::~Streamer()
{
    delete rdbuf();
}

В конструкторе же буфера довольно запомнить тип выводимого сообщения

Log::Streamer::StringBuffer:: StringBuffer(Log::MessageType messageType)
: mMessageType(messageType)
{
}

При уничтожении на каждый случай вызываем его синхронизацию – это недопустит «пропадание» сообщения, если программист позабудет вызвать flush либо endl.

Log::Streamer::Buffer::~Buffer()
{
    pubsync();
}

Дабы поток имел доступ к закрытому для внешнего мира основному интерфейсу класса Log, разместим его вовнутрь

class Log
{
public:

...

	class Streamer : public std::ostream {
...
     };

    static Streamer message;
    static Streamer warning;
    static Streamer error;
...

message warning и error – экземпляры потоков для сообщений всякого типа. Тип сообщения передается им в конструктор

Log::Streamer Log::message(Log::MESSAGE_INFO);
Log::Streamer Log::warning(Log::MESSAGE_WARNING);
Log::Streamer Log::error(Log::MESSAGE_ERROR);

Ну и, наконец, разглядим реализацию функции синхронизации строкового буфера потока.

int Log::Streamer::StringBuffer::sync()
{
    if (Log::sInstance == NULL) {
        return 0;
    }
    std::string text(str());
    if (text.empty()) {
        return 0;
    }
    str("");
    switch (mMessageType) {
        case MESSAGE_INFO:
            Log::sInstance->writeMessage(text);
            break;

		case MESSAGE_WARNING:
            Log::sInstance->writeWarning(text);
            break;

		case MESSAGE_ERROR:
            Log::sInstance->writeError(text);
            break;
    }
    return 0;
}

Как вложенный класс Log::Streamer::Buffer имеет доступ к закрытой (private) области Log. В уточнении нуждается разве что функция str(). Это достаточно необычный способ класса std::stringbuf, тот, что единовременно разрешает получать и устанавливать значение буфера. В обеих своих ипостасях он и применяется – вначале с поддержкой этой функции мы получаем строку из буфера, а после этого вызовомstr(“”) очищаем буфер.
Все, сейчас класс Streamer становится «официальным» интерфейсом Log. Для того, Дабы вывести комбинированное сообщение «i (6) should be in range [1..5]», программисту довольно написать

Log::warning << "i (" << i << ") should be in range [" << I_MIN << ".." << I_MAX << "]" << std::endl;

Это теснее примерно так же легко, как вывести значительно больше туманное «wrong i»
Таким образом, был приведен пример довольно простого класса ведения журнала событий, тот, что может быть легко реализован в самом начале даже самого сжатого по срокам (когда некогда ни писать тесты, ни уговаривать программистов, ни результативно контролировать их, ни дышать, ни спать) плана, значительно повысив его качество.

[1] en.wikipedia.org/wiki/Multiple_dispatch
[2] www.cplusplus.com/reference/cstdio/sprintf/
[3] www.cplusplus.com/reference/sstream/stringstream/
[4] www.cplusplus.com/reference/cassert/assert
[5] www.cplusplus.com/reference/ostream/ostream/
[6] www.cplusplus.com/reference/sstream/stringbuf/

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB