![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
День добрый!
Помогите решить задачку или найти подход к ее решению. В программе необходимо обеспечить высокую информативность диагностических сообщений, для быстрого поиска и устранения причин сбоя. Кто что использует? Поделюсь своими мыслями ... Вести просто журнал в текстовом файле ИМХО не очень удобно, так как при большом объеме информации (код ошибки, файл, строка, имя класса и функции, текст ошибки, дополнительные данные) на одну запись эта масса текста становится просто нечитабельной. Нет никакой иерархии, и две записи в журнал из одной функции могут разбежаться в стороны из-за большого кол-ва записей из вложенной функции. Предполагаю создать свой класс для ведения лога с поддержкой иерархии, фиксацией времени. Так же планирую сделать небольшое приложение для чтения лога и естественно поиска с фильтрацией записей по признакам. Состав данных в одной записи: время файл-строка класс-функция тип записи (информация, предупреждение, ошибка) код ошибки (число) текст ошибки (строка) дополнительный текст (пока неочень понятно нужно или нет, для всякого мусора в общем) данные (строка в формате имя переменной=значение\n) + ссылка на родительскую запись, т.е. получается дерево вызовов Хотелось бы получить так же возможность при компиляции (в идеале вообще при выполнении программы, но как???) задавать уровень трасировки от фиксации действий пользователя, до подробнейшего дерева вызовов. И еще, в коде будут использоваться прерывания ... как бы все это совместить ??? пока предполагаю внутри функции всегда ставить try{}__finally{} для фиксации входа и выхода, иначе дерево в случае ошибки просто умирает (пока фиксирую родительскую запись через push в начале функции и pop в конце (в блоке __finally) - родитель всегда последний). У кого какие мысли есть по этому поводу? Буду благодарен за любую помощь и подсказку! |
|||
|
||||
bsa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
Пишешь класс, допустим Log. Он ведет лог.
Пишешь дружественный класс Logger, у которого в конструктор передается ссылка на экземляр Log (можно и без этого - если экземляр Log глобальный или статический). После этого в начале каждой функции создаешь экземпляр Logger. Если произойдет исключение у Logger'а автоматически вызовется деструктор, который запишет выход из функции. Более того, это все можно реализовать макросами, тогда есть возможность использовать __PRETTY_FUNCTION__ и прочие определения препроцессора для уменьшения кода. |
|||
|
||||
JackYF |
|
|||
![]() полуавантюрист ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 5814 Регистрация: 28.8.2004 Где: страна тысячи озё р Репутация: 18 Всего: 162 |
вот ![]() а я, значит, велосипеды пишу... только вот стандарт по поводу этого, равно как и __FUNCTION__ молчит. Молчит и ман по g++. В гугле нашёл кое-какие сообщения в мэйл-листах, которые говорят о том, что такое таки есть и его используют. Где найти документацию? |
|||
|
||||
bsa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html |
|||
|
||||
JackYF |
|
|||
![]() полуавантюрист ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 5814 Регистрация: 28.8.2004 Где: страна тысячи озё р Репутация: 18 Всего: 162 |
о! я уже эту страничку видел, но на другом сайте и с чуть другим оформлением, поэтому усомнился в официальности. Респект, ну и + ты точно заслужил, спасибо ![]() |
|||
|
||||
archimed7592 |
|
|||
![]() Архимед ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 58 Всего: 93 |
bsa, лови и от меня
![]() А что думают другие компиляторы относительно __PRETTY_FUNCTION__ ? Другими словами - насколько это переносимо в пределах современных популярных компиляторов? -------------------- If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas. © George Bernard Shaw |
|||
|
||||
bsa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
Про __PRETTY_FUNCTION__ не уверен, а вот, судя по той ссылке, __func__ юзать можно на любых компиляторах, совместимых с C99. |
|||
|
||||
jonie |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 5613 Регистрация: 21.8.2005 Где: Владимир Репутация: 15 Всего: 118 |
они не думают... ибо не стандарт. да и про __func__ они тоже не должны бы думать (по крайней мере в ansi iso iec 14882(2003) я такого не видел)... может пнете посмотрю )
-------------------- Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет... |
|||
|
||||
archimed7592 |
|
|||
![]() Архимед ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 58 Всего: 93 |
Ммм, фишка в том, что программирую я исключительно на С++, а в С++ __func__ официально пока нету. Появится только в 2009 :(. Добавлено через 2 минуты и 53 секунды Нет, нет, это точно, что оффициально появится только в С++09. Я об этом даже где-то писал... ![]() -------------------- If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas. © George Bernard Shaw |
|||
|
||||
SaDFromSpb |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 263 Регистрация: 5.4.2006 Где: Санкт-Петербург Репутация: 3 Всего: 3 |
_Tanatos_, если сделаешь, кинь, плиз, исходники на всеобщее обозрение. Может, доделаем до состояния конфетки, если оно еще ей не будет являться =). Было бы здорово иметь что-нибудь продуманно-универсальное для этого дела...
Добавлено через 3 минуты и 17 секунд Ну или если кто-то знает уже готовые решения для этого, то сообщите. По поводу боязни неуниверсальности __PRETTY_FUNCTION__ - можно использовать reflex, правда есть мысль, что здесь она через чур.... Дополнительную библиотеку все-таки подключать... -------------------- "За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001) |
|||
|
||||
archimed7592 |
|
|||
![]() Архимед ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 58 Всего: 93 |
Если бы только библиотеку подключать... -------------------- If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas. © George Bernard Shaw |
|||
|
||||
Mayk |
|
|||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 45 Всего: 134 |
в бусте есть boost/current_function.hpp
-------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
|||
|
||||
chipset |
|
||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 4071 Регистрация: 11.1.2003 Где: Seattle, US Репутация: 27 Всего: 164 |
А у меня такой вот макрос:
Вроде работает на gcc и VCях. --------------------
|
||||
|
|||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
_Tanatos_, я так понимаю, что ты хочешь строить дерево, в оперативной памяти, для определения причины сбоя в программе. Дерево придется периодически сохранять на диск, в идеале при добавлении нового элемента...
По хорошему лог нужно записывать так:
- в формате лога должна быть следующая информация: код процесса, код потока, время записи, уровень(степень подробности) сообщения, дальше на твое усмотрение, можно еще номер строки и имя файла добавить.. - класс отвечающий за запись в лог(еще лучше просто функция) не должен буферизировать сообщения, иначе, если программа упадет, последние сообщения не запишутся в файл, если программа под windows, то мало просто записывать на диск данные, нужно принудительно очищать буфер файла, если этого не делать в случае вызова Terminate данные могут быть не записаны на диск - опять-же в случае если пишешь под windows смотри MSDN на тему Event Log, ReportEvent и тд. Добавлено через 1 минуту и 23 секунды ps недавно открыл для себя программу grep, которая очень подходит для анализа огромных лог файлов Добавлено через 13 минут и 4 секунды Кстати Event Log в Windows работает очень быстро, надежно, и лучше не изобретать велосипед, а использовать то, что предоставляет платформа для этих целей. |
|||
|
||||
SergeCpp |
|
|||
![]() ![]() ![]() Профиль Группа: Участник Сообщений: 955 Регистрация: 8.8.2005 Где: At Home Репутация: 15 Всего: 124 |
||||
|
||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Благодарю всех за активное участие и ценные советы.
В силу обстоятельств на начальной стадии проекта буду писать на C++ Builder а он не знает не __func__, __FUNCTION__ и __PRETTY_FUNCTION__. Ну да ладно, меня не ломает скопировать название функции и класса, и потом можно написать простую тулзу которая перебирая перед компиляцией все исходники будет править имя класса и функции - так что это решаемо. Отказаться от использования ООП думаю не оправданно. Конечно остаются некоторые вероятности, но отказать может почти все. Например от отключения питания не спасут никакие технологии программирования (кстати ИБП тоже подыхают периодически). Однозначно простой лог меня не устроит. Нужно дерево. Кстати необязательно его при этом хранить целиком в память, достаточно точно знать номер записи родителя или как еще более простой вариант номер уровня (и не надо заводить стек для родителей) если номер меньше чем номер предыдущей записи - значит дочерний, если больше значит выхоим на уровень выше. Недостаток - парсить действительно надо будет все. Но никто не мешает этот-же лог просмотреть и как текст просто отфильтровав по типу записи и классу. По поводу сброса буффера на диск, надо постараться отловить крах системы (может даже вынести ведение лога в независимый процесс - пока не думал над этим) а в штатном режиме делать сброс буввера например, каждые 10 секунд. Будут результаты отпишусь, может и правда совместными усилиями сделаем конфетку. Еще один момент, уважаемые форумчане! Подскажите как быть с ведением лога в многопоточном приложении? Пока предполагаю вести независимые логи в разные файлы, чтобы каждый из них можно было просматривать и как дерево и в развернутом виде. Это сообщение отредактировал(а) _Tanatos_ - 21.11.2007, 11:20 |
|||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
О чем то похожем говорил bsa, можно вынести часть функционала в отдельный класс и использовать raii
принцип такой, впри входе в ф-ю создается объект Logger. Через который производится запись в лог, каждый такой объект имеет свой номер, по которому можно определить порядок вызовов, в деструкторе, в лог также записывается информация о том что ф-я завершила выполнение. Это сообщение отредактировал(а) Lazin - 21.11.2007, 11:55 |
|||
|
||||
archimed7592 |
|
|||
![]() Архимед ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 58 Всего: 93 |
Я бы ещё добавил
-------------------- If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas. © George Bernard Shaw |
|||
|
||||
SaDFromSpb |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 263 Регистрация: 5.4.2006 Где: Санкт-Петербург Репутация: 3 Всего: 3 |
А вот, кстати, содержимое boost/current_function.hpp:
Все весьма банально =) -------------------- "За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001) |
|||
|
||||
dumb |
|
|||
![]() sceloglauxalbifacies ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 2929 Регистрация: 16.6.2006 Репутация: 8 Всего: 158 |
||||
|
||||
JackYF |
|
|||
![]() полуавантюрист ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 5814 Регистрация: 28.8.2004 Где: страна тысячи озё р Репутация: 18 Всего: 162 |
* ушёл смотреть * ну вот, уже выложили ![]() ну так, в принципе, и предполагалось. Буст подруливает. Maykу однозначно плюс. |
|||
|
||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Подскажите какой тип лучше использовать для хранения времени события и заодно какие функции для работы с ним.
|
|||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
структура FILETIME, функции GetLocalTyme SystemTimeToFileTime
|
|||
|
||||
SaDFromSpb |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 263 Регистрация: 5.4.2006 Где: Санкт-Петербург Репутация: 3 Всего: 3 |
А что, в <ctime> большой выбор для этого? ![]() -------------------- "За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001) |
|||
|
||||
StarikanMan |
|
|||
Новичок Профиль Группа: Участник Сообщений: 1 Регистрация: 22.11.2007 Репутация: нет Всего: нет |
bsa, он написал очень хороший пример
|
|||
|
||||
dumb |
|
|||
![]() sceloglauxalbifacies ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 2929 Регистрация: 16.6.2006 Репутация: 8 Всего: 158 |
всем, кто пытается осмыслить посты StarikansBack'а - этот убогий является каким-то обгашенным куском недоразума, генерирующим случайные наборы слов.
|
|||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
||||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
||||
|
||||
_Tanatos_ |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Вот что у меня получилось на данный момент
Привожу фрагмент кода с описанием структуры данных одной записи:
Какие есть мысли и соображения? Изначально буду реализовывать функцию регистрации записи с полным перечнем Поля m_Level и m_Time обрабатываются автоматически внутри класса. Предполагаю, что использование полей m_Type, m_File и m_Line будет внутри макроса, тем самым сократиться общая длина записи Макрос __FUNC__ пока не буду использовать, так как нет гарантий, что при смене компилятора работоспособность кода сохранится. Для времени пока остановился на SYSTEMTIME, дабы не вызывать лишние функции преобразования, да и при записи в текстовый файл не надо будетзаниматься обратной конвертацией для форматирования. далее приведено определение обработчика для регистрации новой записи
Вот и сам класс журналирования
Предполагается следующий сценарий использования - имеется глобальная переменная g_LogEngine через нее происходит регистрация всех событий - В данному классу подключаются обработчики выполняющие одну из следующих функций: - запись в файл - отправка лога по почте (например пересылает только критические сообщения) - отображение сообщений в окне отладки (если таковое имеется в программе) - Далее при входе в журналируюмую функцию объявляем вспомогательный класс (экземпляр), через него обеспечивается запись в журнал с увеличением уровня вложенности - используем подготовленные макросы для ведения лога в пределах функции. - вызываем функцию Complete() в вспомогательном классе - при завершении функции вспомогательный класс в деструкторе фиксирует уменьшение уровня вложенности и делает запись либо об успешном выполнении либо об аварийном завершении (если небыло вызова Complete() ) примерно так. Вспомогательный класс опишу чуть позже. Поддержка многопоточности на последний этап, так как это на перспективу и мне пока не нужно. Из актуальных вопросов это регистрация условий обработки - ограничение по минимальному и максимальному уровню для m_Type для каждого отдельного m_UnitID, что позволит сократить объем сохраняемых записей. Пока планирую сделать так:
Соответственно делаем массив таких записей и при регистрации новой записи сначала пробегаемся по этому массиву и проверяем на выполнимость условий, если разрешающего условия не найдено, то завершаем обработку, иначе поочередно вызываем все обработчики. При необходимости задавать условия для каждого обработчика отдельно, это уже реализуется внутри конкретного обработчика. Какие есть мысли, пожелания, предложения? По ходу дела буду выкладывать готовые фрагменты и по результату скину все целиком. Это сообщение отредактировал(а) _Tanatos_ - 23.11.2007, 14:27 |
||||||||
|
|||||||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Мне кажется эта структура сильно избыточна, если знаешь имя ф-ии, то имя файла и номер строки знать не обязательно. И имя ф-ии можно указывать только при входе в ф-ию, с другой стороны нет идентификатора потока, в многопоточном приложении записи из разных потоков смешаются, и получится хаос. Уровень вложенности нужно расчитывать для каждого потока отдельно. Еще можно расчитывать m_Level в зависимости от степени вложенности. |
|||
|
||||
Mayk |
|
||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 45 Всего: 134 |
не согласен. имя файла и номер строки очень удобны. в любом уважающим себя редакторе есть ф-ция перехода на заданную строку. Перейти на определение ф-ции значительно сложнее. К тому же иногда хотелось бы посмортреть до каких строк работает прога, а до каких она не доходит.
если хочешь портируемый код, то надо избавиться от не портируемого __closure.
Кто выделяет и освобождает память для этих полей? только 256 типов сообщений? а не мало? -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||
|
|||||||
Lazin |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Ну номер строки еще ладно, но имя файла и имя ф-ии нужно указывать только при их изменении.
так он же написал, что использует Borland? а есть ли какая-нибудь портируемая вльтернатива __closure ? присоединяюсь можно еще добавить поле - "категория", например все сообщения имеющие отношение к работе с базой данных должны иметь одну категорию, все, что имеют отношение к взаимодействию между процессами, другую, плюс должна быть возможность комбинировать категории. Это поможет в итоге отфильтровать лог, при поиске конкретной проблемы. |
||||
|
|||||
_Tanatos_ |
|
||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
А надо больше? Сколько не прикидывал, пока больше 10 не набиралось ![]() Думаю использовать более 10 уровней детализации не имеет смысла ибо ни один программист не сможет в них свободно ориентироваться. На данный момент планирую, что память внутри обработки журнала не будет ни выделяться, ни освобождаться, а следовательно всем будет заниматься тот кто вызывает функцию. Эти данные актуальны только на этапе обработки сообщения и никоим образом не могут быть использованы после. А значит указатель будет ссылаться на достоверные данные. Позже скину пример использования.
А чем заменить? Если просто убрать, то указатель будет на отдельную функцию, а мне надо на функцию класса.
В данном случае больше не меньше. Реальный вызов гораздо более компактный. И потом я планирую сделать браузер для логов, который позволит просматривать исходники ... если будет время добавлю поддержку компиляторов, чтобы файлы открывались именно в них (но это не скоро). |
||||||
|
|||||||
Mayk |
|
||||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 45 Всего: 134 |
При использование __closure есть работоспосбность кода при смене компилятора не сохранится вообще.
boost::bind + boost::function, например.
А если мне хочется нумеровать сообщения, начиная с 0x10000000[например сообщения из .exe], а в другом месте с 0x200000[например, сообщения из .dll]? или использование юзеровских типов не предусмотрено? а почему тогда не использовать enum?
ОпределенноХорошаяМысль. -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||||
|
|||||||||
_Tanatos_ |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Принимается, добавлю. ТОлько распишите поподробней что имеется в виду под "возможность комбинировать"? Как себе представляете этот процесс. Какого типа хотелось бы видеть категорию?
Посмотрю - как разберусь исправлю. Спасибо за информацию!
Нет проблем, пользуйте m_Proc или m_Unit. Кстати спрашивали насчет потоков ... под них так же планируется использовать m_Proc - ИМХО предостаточно. Смысла писать туда реальный идентификатор потока не вижу, так как он от раза к разу меняется, можно его скинуть как данные при старте потока или процесса. А с логе использовать внутренние идентификаторы.
А зачем? Предполагаю использовать отрицательные значения для нескольких предопределенных значений, остальное на усмотрение программиста, так как никакой нагрузки смысловой (в пределах кода журнала) эти данные не несут - а значит и ограничивать нет смысла. Пусть каждый сам разобьет на то количество уровней которое ему нужно, а для варианта по умолчанию уже есть enum но его можно или использовать или нет. |
||||||||
|
|||||||||
SaDFromSpb |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 263 Регистрация: 5.4.2006 Где: Санкт-Петербург Репутация: 3 Всего: 3 |
Тогда, на всякий случай определи его как signed char, так как стандарт этого не гарантирует по умолчанию. И давай скорее пример использования, а то так разбираться лениво ![]() -------------------- "За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001) |
|||
|
||||
_Tanatos_ |
|
||||||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
На суд уважаемых форумчан выдвигается несколько вариантов решения с вложенностью, а именно
Вариант №1: (мне нравится больше всего) вложенность управляется при помощи параметра m_Level. Его изменение производится исключительно классом TtLibLogEngine. Командой для изменения служат зарезервированные значения типа сообщения m_Type: -1 - Увеличиваем вложенность -2 - Уменьшаем вложенность Плюс данного решения в том, что нет необходимости вводить дополнительные переменные, функции и т.д., минус нельзя одновременно зафиксировать запись пользовательского типа с вложенностью. Пример использования:
Вариант №2: идентичен предыдущему варианту, но для управления вложенностью выделяется отдельные функции (можно конечно сделать и отдельный параметр, но уж больно много параметров). Пример использования:
Вариант №3: Вынести управление в отдельную функцию, которая будет вызываться перед регистрацией сообщения и будет модифицировать одну последующую запись. ИМХО хуже из-за лишнего вызова, хотя можно сделать с модификатором inline. Пример использования:
-------------------- Предложение, насчет передачи имени класса и функции только один раз, отклюняю, так как: 1) Никто не мешает сделать это собственными силами. Объявляйте константы, ссылки или просто записывайте в лог один раз, а остальные записи оставляйте пустыми. 2) Как Вы себе представляете восстановление данных значений после выхода из вложенной функции??? --------------------
Принято.
На этой неделе будет выложен исходник с использованием. Пока пишу библиотеку в перерывах (не многочисленных) между основной работой :(, так что сделано достаточно мало. |
||||||||||
|
|||||||||||
Lazin |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Правильнее использовать rtti, создаешь в стеке объект, в конструкторе вызывается нужная функция(увеличивает вложенность), в деструкторе уменьшаем вложенность, а как передавать - детали. Нужно различать поток, из которого вызывается ф-я, и вычислять вложенность отдельно для каждого потока. Это можно сделать внутри TtLibLogEngine, при каждом вызове ф-ии получать ThreadId потока, и дальше вычислять для него степень вложенности. Я зделал что то похожее, но у меня - просто надстройка над библиотекой ведения логов, которая немного форматирует текст.
Это сообщение отредактировал(а) Lazin - 26.11.2007, 15:23 |
||||||
|
|||||||
_Tanatos_ |
|
||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Так оно и будет. Будет дополнительный класс CtLibLogHelper, который будет в конструкторе регистрировать сообщение с увеличением вложенности, а в деструкторе собщение с уменьшением вложенности. За пример спасибо. Изучаю. |
||||
|
|||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
||||
|
||||
Lazin |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Я тут немного названия изменял, так-что могут быть небольшие косяки. Плюс там используется для записи в лог библиотека - разработка нашей фирмы, и я ее не могу выложить по понятным причинам. Это сообщение отредактировал(а) Lazin - 26.11.2007, 15:26 |
||||
|
|||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Выкладываю предполагаемый вариант использования.
Обращаю внимание, что макросы пока не использую, дабы было понят но что есть что. Не обращайте внимание на работу со строками, написал так для сокращения
В конструкторе класса CtLibLogHelper указываем идентификатор Процесса, потока и имя класса и функции - данные указываются один раз для одной функции. Соответственно все записи производятся через вспомогательный класс. Функция Error производит запись в журнал и сама генерирует исключение с указанным кодом (в данном случае 134). |
|||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Мне кажется это противоестественным. Пользователь класса вряд-ли будет расчитывать на то что писалка в лог кидает исключения. Не стоит смешивать подсистему обработки ошибок и подсистему записи в лог. Если к примеру ты захочешь убрать запись в лог, это приведет к изменению логики работы программы, а запись логов не должна на логику влиять. |
|||
|
||||
_Tanatos_ |
|
||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Согласен. Принято к изменению. |
||||
|
|||||
_Tanatos_ |
|
||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Попробовал - неработает! после неудачи порылся в инете на предмет использования, вот такой пример не работает Что делаю не так? ИМХО лучше по старинке через флаг внутри класса и дополнительную функцию для его сброса.
|
||||||
|
|||||||
archimed7592 |
|
|||
![]() Архимед ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 58 Всего: 93 |
-------------------- If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas. © George Bernard Shaw |
|||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Обрати внимание, на то, что std::uncaught_exception( ) срабатывает только если объект был создан внутри блока try, иначе он не вызывается. С Builderoм STL нормально работает... |
|||
|
||||
_Tanatos_ |
|
||||||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Не знаю как нормально, но у меня не получилось добиться работы. Добавлено через 13 минут и 38 секунд Выкладываю на ваш суд черновой вариант классов Начну с вспомогательных структур:
Далее привожу класс CtLibLogEngine. Пока реализацию кинул в библиотечный файл, в будущем там останутся только inline функции
Смотрим вспомогательный класс CtLibLogHelper
А теперь пример использования:
Тут даже без макросов все довольно компактно получается. Имя функции и файла указываются только один раз. |
||||||||||
|
|||||||||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Из текущих задач:
- сделать код безопасным для многопоточных приложений - сделать глобальный объект указателем для поддержки DLL (при инициализации DLL надо соответственно инициировать указатель, а в основной программе соответственно создавать сам объект) - Разработать класс (образец) подписчик для записи журнала в файл - добавить раздельную нумерацию строк для потоков Высказывайте свои пожелания, предложения и критику |
|||
|
||||
_Tanatos_ |
|
||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Примерно так выглядит класс записывающий данные в файл:
Пример использования
|
||||
|
|||||
UnrealMan |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
Хреново без макросов получается. Во-первых, необходимость всюду прописывать __LINE__ - это ж... Кроме того, отладочная информация по логике вещей должна выводиться в лог только в отладочной версии программы. И желательно, чтобы в неотладочной версии параметры, представляющие собой отладочную информацию, зря не вычислялись. Указание типа лога через константу - IMHO, не самая лучшая идея. Этот логгер неудобно будет использовать для вывода составных данных - когда строка формируется из подстрок, чисел и прочих данных с нужным форматированием. Придётся самому вычислять строку и передавать её логгеру - это как минимум громоздко. |
|||
|
||||
Lazin |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Можно сделать так что-бы ф-я SubscriberAdd получала не указатель на ф-ю с модификатором __closure, а указатель на класс, а все "подписчики" имели-бы одинаковый интерфейс, это было-бы переносимо на другие компиляторы.
Этот класс не учитывает одного простого факта - что возможен запуск нескольких копий одного приложения, в этом случае в файле будет мусор. Нужно синхронизировать доступ к файлу, например создать именованный мьютекс и захватывать его пере записью, и выводить в лог код процесса, что-бы можно было различать вывод разных процессов.
Возможное решение этой проблеммы - использовать полиморфизм, передавать в объект logEngine объекты, содержащие нужную информацию. CtLibLogHelper может выступать в роли фабрики классов. Добавлено через 2 минуты и 29 секунд выглядеть это может так
|
||||||
|
|||||||
_Tanatos_ |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Принято:
В класс входит только одна функция, так как пока не вижу что еще может потребоваться. Можно добавить туда же унифицированные функции настройки фильтра, аналогично классу Engine. Надо? Добавлено через 4 минуты и 24 секунды
Не хочу чересчур усложнять класс, который должен работать максимально быстро. ИМХО это обходится крайне просто, при инициализации лога добавьте к имени файла идентификатор процесса. А если нужна паралельная запись в файл, то надо сделать отдельный класс для этой задачи Таких классов-обработчиков может быть много. Например для накопления сообщений и отсылки их по почте и т.д. В данном случае мой класс является лишь примером. Сначала закончу с ядром, а уж потом займусь переферией. Добавлено через 8 минут и 54 секунды
Совершенно непонятное заявление! Поясни, что тебе не нравится? Не нравятся константы, пишы числа, переменные, выражения, да что угодно - главное чтобы результат был signed char. А лучше всего напиши чем именно не нравится и для какой задачи тебе это не подходит. P.S. Не ленитесь подробней изложить свою мысль, ведь от этого напрямую зависит результат |
||||||||
|
|||||||||
_Tanatos_ |
|
||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
А как ты себе представляешь класс логгера должен форматировать твои данные? Мне кажется Вы на этот класс возлагаете не его задачи. И потом громоздкое и длинное сообщение будет неудобоваримым. А если речь идет о данных, то для них есть отдельное поле (см ниже). Как вспомогательный инструмент можно написать функцию аналогичную sprintf, но возвращающую указатель на готовую строку и вставлять эту конструкцию вместо сообщения: LogHelper.Message(lltInfo, Format(" Этап №%d", Number), "", __LINE__); Годится такой вариант?
Опять же непонятно зачем полиморфизм и причем тут фабрика классов? Приведенный тобой пример вполне подходит для формирования поля Data, если вызов функции AddValue будет приводить к добавлению строчки с именем переменной и ее значением. Если так, то согласен и постараюсь добавить несколько функций для работы с различными типами данных. Только проблема заключается в том, что надо будет так же добавлять функцию Post() в конце для определения момента когда сообщение полностью сформированно и его надо передать в класс Engine. |
||||
|
|||||
UnrealMan |
|
||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
Так же, как это делают потоки типа fstream или хотя бы как fprintf.
Это не первостепенная задача логгера, но тут можно провести аналогию с файловыми потоками и простым сишным выводом через fprintf. Для того, чтобы записать текст в файл, можно воспользоваться строковым потоком или sprintf и для результата вызвать write/fwrite. Но вряд ли кто-то захочет так делать, потому что проще не вводить лишние звенья, а сразу воспользоваться оператором << файлового потока или fprintf.
Это неудобный вариант. |
||||||
|
|||||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
В вашем примере указано только одно поле, а их как минимум два Сообщение и данные.
Такой вариант приемлим?
P.S. To All: Спасибо всем, кто принимает участие в обсуждении! Стараюсь учесть пожелания и предложения каждого Сорри что изменения не так быстро воплощаются в код. P.S.S. Повторюсь, макросы пока не пишу так как они не сказываются на функциональности. Понятное дело, что с ними короче и удобней, понятно что они будут и это даже не обсуждается. Будут сделаны когда утоится состав и формат всех функций, так что не сердчайте на их отсутствие. Но! Если здесь писать все в виде макросов, то совершенно непонятно получается что должно быть внутри. Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 12:45 |
|||
|
||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Не хочется изобретать велосипед и раз уж речь зашла о форматировании, то хотелось бы использовать sprintf.
Только как передать переменное кол-во аргументов дальше?
Уважаемые гуру, нужен Ваш совет! Писать множество отдельных функций для различных типов данных ИМХО нелогично (что делать если надо сразу два аргумента передать при форматировании одной строки?). |
|||
|
||||
UnrealMan |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
||||
|
||||
UnrealMan |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
Добавлено через 3 минуты и 32 секунды Есть одно маленькое "но": теперь тебе придётся помнить о возможности переполнения буфера, используемого для sprintf. Это сообщение отредактировал(а) UnrealMan - 29.11.2007, 14:05 |
|||
|
||||
_Tanatos_ |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Спасибо!
Сообщение от данных отделяю, для удобства. Данных может быть много, но при беглом просмотре лога они лишь отвлекают глаз. В сообщении содержится короткое сообщение по сути. В данных абсолютно все что угодно, что может быть полезным для отладки или даже можно написать подсказку для юзера ![]() Вот пример использования:
У меня получилось немного иначе, так как при многократном вызове нужно сохранять предыдущую часть. Ну и требования по длинне сообщения смегчаются ![]() Так выглядит содержимое функции:
И парочку сервисных функций:
Естественно добавился вызов ExReset() в деструктор ![]() Еще добавилось два конструктора:
для инициализации класса без создания сообщения в конструкторе. Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 15:01 |
||||||||
|
|||||||||
Lazin |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
Я не очень понимаю что нужно получить в итоге, наверное лучше сначала составить какой нибудь список требований к библиотеке т.е. что нам от нее надо....
использование элипсисов я считаю тупиковым, так как в данном случае теряется гибкость и расширяемость, если я сделаю свой класс, как мне расширить класс, что-бы он выводил в лог объекты моего класса. Для этого придется наследовать, от класса - логгера, и переопределять ф-ю. Лучше сделать это так, как это сделано в библиотеках iostream или boost::serializaion etc. должно выглядеть примерно так:
Можно использовать интерфейс как в iostream для изменения параметров (там это называется манипуляторами)
с таким интерфейсом проще написать макросы
и так можно написать для любого кол. параметров Это сообщение отредактировал(а) Lazin - 29.11.2007, 15:53 |
||||||
|
|||||||
_Tanatos_ |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Переработал класс CtLibLogHelper
Примеры использования:
В результате получаем следующий лог:
Если последний вызов LogHelper.Post(lltInfo, __LINE__, true); заменить на обычный LogHelper.Post(lltInfo, __LINE__); получим: [/code] В результате получаем следующий лог:
Последняя строка сформируется автоматически. При использовании макросов можно будет формировать как однострочные записи в журнал, так и многострочные с форматированием. Единственное неудобство будет состоять в том, что надо будет отслеживать использование различных макросов для первой строки (где нужно объявление) и для остальных. Пока не знаю как это обойти. Есть идеи? |
||||||||
|
|||||||||
_Tanatos_ |
|
||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Эта ветка для этого и создана ![]() Конечно в идеале сначала составить что-то вроде ТЗ, а потом уже писать. Но! я давненько не программировал, так что пользуясь случаем восстанавливаю навыки.
С таким аргументом не поспоришь ![]() Да и возможность записать все в одну строку прильщает. Непонятно только как добавлять вспомогательную информацию и как разделять сообщение и данные? Склоняюсь к использованию дополнительного класса CtLibLogHelper для управления вложенностью и запретить прямой доступ извне. Это позволит гарантировать, что иерархия будет корректной всегда. Иначе можно сделать 10 увеличений вложенности а потом по исключению (или недосмотру) словить неправильную иерархию. А сам класс CtLibLogEngine тогда будет использоваться для записи сообщений в текущий уровень. Мои требования к логгеру: - построение древовидного лога (с гарантией корректности структуры) - создание структурированных записей - наличие вспомогательной информации (тип, файл, строка, модуль, процесс, поток ...) - возможность управлять фильтрованием записей в режиме исполнения (например включать/отключать трассировку по горячей клавише) - возможность регистрировать несколько подписчиков на сообщения (один сохраняет в файл, другой выводи на консоль и т.д. - желательно возможность формирования простых записей в одну строчку - еще высказывали пожелание отключать ведение лога при чистовой компиляции (мне бы хотелось ограничивать ведение лога определенным уровнем и физически вычищать из конечного кода только сообщения уровнем ниже заданного) - поддержка многопоточных приложений Какие еще есть пожелания? Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 16:46 |
||||
|
|||||
JackYF |
|
|||
![]() полуавантюрист ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 5814 Регистрация: 28.8.2004 Где: страна тысячи озё р Репутация: 18 Всего: 162 |
Не насилуйте va_args, юзайте boost::format...
|
|||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 41 Всего: 154 |
как вариант, определить оператор << в классе LogHelper, будет более логично...
тем более LogHelper отвечает за контроль иерархии записей |
|||
|
||||
_Tanatos_ |
|
||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
А как такой вариант?
Опробовал вполне работоспособный вариант. С макросом вообще сказка получается:
Остается правда вопрос! Как с помощью макроса при финальной компиляции исключить из кода запись в журнал? Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 21:07 |
||||
|
|||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |