Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Технология ведения лога (диагностика) 
:(
    Опции темы
_Tanatos_
Дата 20.11.2007, 17:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



День добрый!

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

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

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

Состав данных в одной записи:
время
файл-строка
класс-функция
тип записи (информация, предупреждение, ошибка)
код ошибки (число)
текст ошибки (строка)
дополнительный текст (пока неочень понятно нужно или нет, для всякого мусора в общем)
данные (строка в формате имя переменной=значение\n)
+ ссылка на родительскую запись, т.е. получается дерево вызовов

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

И еще, в коде будут использоваться прерывания ... как бы все это совместить ??? пока предполагаю внутри функции всегда ставить try{}__finally{} для фиксации входа и выхода, иначе дерево в случае ошибки просто умирает (пока фиксирую родительскую запись через push в начале функции и pop в конце (в блоке __finally) - родитель всегда последний).

У кого какие мысли есть по этому поводу? Буду благодарен за любую помощь и подсказку!
PM MAIL   Вверх
bsa
Дата 20.11.2007, 17:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Пишешь класс, допустим Log. Он ведет лог.
Пишешь дружественный класс Logger, у которого в конструктор передается ссылка на экземляр Log (можно и без этого - если экземляр Log глобальный или статический). После этого в начале каждой функции создаешь экземпляр Logger. Если произойдет исключение у Logger'а автоматически вызовется деструктор, который запишет выход из функции.
Более того, это все можно реализовать макросами, тогда есть возможность использовать __PRETTY_FUNCTION__ и прочие определения препроцессора для уменьшения кода.
PM   Вверх
JackYF
Дата 20.11.2007, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 18
Всего: 162



Цитата(bsa @  20.11.2007,  17:34 Найти цитируемый пост)
__PRETTY_FUNCTION__

вот  smile !

а я, значит, велосипеды пишу... только вот стандарт по поводу этого, равно как и __FUNCTION__ молчит.
Молчит и ман по g++. В гугле нашёл кое-какие сообщения в мэйл-листах, которые говорят о том, что такое таки есть и его используют. Где найти документацию?


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
bsa
Дата 20.11.2007, 18:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(JackYF @ 20.11.2007,  18:15)
Цитата(bsa @  20.11.2007,  17:34 Найти цитируемый пост)
__PRETTY_FUNCTION__

вот  smile !

а я, значит, велосипеды пишу... только вот стандарт по поводу этого, равно как и __FUNCTION__ молчит.
Молчит и ман по g++. В гугле нашёл кое-какие сообщения в мэйл-листах, которые говорят о том, что такое таки есть и его используют. Где найти документацию?

http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
PM   Вверх
JackYF
Дата 20.11.2007, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 18
Всего: 162



Цитата(bsa @  20.11.2007,  18:25 Найти цитируемый пост)

http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html 

о! я уже эту страничку видел, но на другом сайте и с чуть другим оформлением, поэтому усомнился в официальности.

Респект, ну и + ты точно заслужил, спасибо smile


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
archimed7592
Дата 20.11.2007, 20:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



bsa, лови и от меня smile.
А что думают другие компиляторы относительно __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
PM Jabber   Вверх
bsa
Дата 20.11.2007, 21:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(archimed7592 @ 20.11.2007,  20:45)
bsa, лови и от меня smile.
А что думают другие компиляторы относительно __PRETTY_FUNCTION__ ? Другими словами - насколько это переносимо в пределах современных популярных компиляторов?

Про __PRETTY_FUNCTION__ не уверен, а вот, судя по той ссылке, __func__ юзать можно на любых компиляторах, совместимых с C99.
PM   Вверх
jonie
Дата 20.11.2007, 22:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 15
Всего: 118



они не думают... ибо не стандарт. да и про __func__ они тоже не должны бы думать (по крайней мере в ansi iso iec 14882(2003) я такого не видел)... может пнете посмотрю )


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
archimed7592
Дата 20.11.2007, 22:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(bsa @  20.11.2007,  21:16 Найти цитируемый пост)
Про __PRETTY_FUNCTION__ не уверен, а вот, судя по той ссылке, __func__ юзать можно на любых компиляторах, совместимых с C99.

Ммм, фишка в том, что программирую я исключительно на С++, а в С++ __func__ официально пока нету. Появится только в 2009 :(.

Добавлено через 2 минуты и 53 секунды
Цитата(jonie @  20.11.2007,  22:10 Найти цитируемый пост)
может пнете посмотрю ) 

Нет, нет, это точно, что оффициально появится только в С++09. Я об этом даже где-то писал...



Цитата(jonie @  20.11.2007,  22:10 Найти цитируемый пост)
ansi iso iec 14882(2003)

smile.


--------------------
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
PM Jabber   Вверх
SaDFromSpb
Дата 21.11.2007, 01:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 5.4.2006
Где: Санкт-Петербург

Репутация: 3
Всего: 3



_Tanatos_, если сделаешь, кинь, плиз, исходники на всеобщее обозрение. Может, доделаем до состояния конфетки, если оно еще ей не будет являться  =). Было бы здорово иметь что-нибудь продуманно-универсальное для этого дела...

Добавлено через 3 минуты и 17 секунд
Ну или если кто-то знает уже готовые решения для этого, то сообщите.
По поводу боязни неуниверсальности __PRETTY_FUNCTION__ - можно использовать reflex, правда есть мысль, что здесь она через чур.... Дополнительную библиотеку все-таки подключать...




--------------------
"За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001)
PM   Вверх
archimed7592
Дата 21.11.2007, 02:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(SaDFromSpb @  21.11.2007,  01:40 Найти цитируемый пост)
Дополнительную библиотеку все-таки подключать...

Если бы только библиотеку подключать...


--------------------
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
PM Jabber   Вверх
Mayk
Дата 21.11.2007, 04:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



в бусте есть boost/current_function.hpp 


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
chipset
Дата 21.11.2007, 04:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4071
Регистрация: 11.1.2003
Где: Seattle, US

Репутация: 27
Всего: 164



А у меня такой вот макрос: 

Код

#define CURRENT_FUNCTION __PRETTY_FUNCTION__
#ifdef __FUNCTION__
#define CURRENT_FUNCTION __FUNCTION__
#endif



Вроде работает на gcc и VCях.


--------------------
Цитата(Jimi Hendrix)
Well, I stand up next to a mountain
And I chop it down with the edge of my hand
PM MAIL WWW   Вверх
Lazin
Дата 21.11.2007, 09:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



_Tanatos_, я так понимаю, что ты хочешь строить дерево, в оперативной памяти, для определения причины сбоя в программе. Дерево придется периодически сохранять на диск, в идеале при добавлении нового элемента...
По хорошему лог нужно записывать так:
    - должен быть простой формат лога, никаких xml и прочего, для того что-бы можно было не загружать весь файл полностью (представь что там несколько гигов), тот же xml придется парсить целиком.
    - в формате лога должна быть следующая информация: код процесса, код потока, время записи, уровень(степень подробности) сообщения, дальше на твое усмотрение, можно еще номер строки и имя файла добавить..
    - класс отвечающий за запись в лог(еще лучше просто функция) не должен буферизировать сообщения, иначе, если программа упадет, последние сообщения не запишутся в файл, если программа под windows, то мало просто записывать на диск данные, нужно принудительно очищать буфер файла, если этого не делать в случае вызова Terminate данные могут быть не записаны на диск
    - опять-же в случае если пишешь под windows смотри MSDN на тему Event Log, ReportEvent и тд.
самое главное, это должна быть самая простая и надежная часть программы, если хочешь сделать надежную библиотеку для ведения логов, оставь ООП изыски тем кому это надо, к примеру деструкторы не вызываются в случае если процесс аварийно завершается, но можно к примеру установить фильтр необработанных исключений(SetUnhandledExceptionFilter) и он отработает по любому.

Добавлено через 1 минуту и 23 секунды
ps
недавно открыл для себя программу grep, которая очень подходит для анализа огромных лог файлов

Добавлено через 13 минут и 4 секунды
Кстати Event Log в Windows работает очень быстро, надежно, и лучше не изобретать велосипед, а использовать то, что предоставляет платформа для этих целей.
PM MAIL Skype GTalk   Вверх
SergeCpp
Дата 21.11.2007, 10:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


 
**


Профиль
Группа: Участник
Сообщений: 955
Регистрация: 8.8.2005
Где: At Home

Репутация: 15
Всего: 124



Цитата(Lazin @  21.11.2007,  11:01 Найти цитируемый пост)
недавно открыл для себя программу grep, которая очень подходит для анализа огромных лог файлов


AWK подходит ещё более очень user posted image
(на авторов, на авторов там посмотрите! user posted image)

Также посмотрите SED
PM MAIL WWW ICQ   Вверх
_Tanatos_
Дата 21.11.2007, 11:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Благодарю всех за активное участие и ценные советы.

В силу обстоятельств на начальной стадии проекта буду писать на C++ Builder а он не знает не __func__, __FUNCTION__ и __PRETTY_FUNCTION__. Ну да ладно, меня не ломает скопировать название функции и класса, и потом можно написать простую тулзу которая перебирая перед компиляцией все исходники будет править имя класса и функции - так что это решаемо.

Отказаться от использования ООП думаю не оправданно. Конечно остаются некоторые вероятности, но отказать может почти все. Например от отключения питания не спасут никакие технологии программирования (кстати ИБП тоже подыхают периодически).

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

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

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

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

Это сообщение отредактировал(а) _Tanatos_ - 21.11.2007, 11:20
PM MAIL   Вверх
Lazin
Дата 21.11.2007, 11:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



О чем то похожем говорил bsa, можно вынести часть функционала в отдельный класс и использовать raii

Код

//класс - лог-файл - записывает данные в файл в каком-то формате
class Log
{
 static long node;
 void write(long n, LPCSTR msg);
};

//класс - непосредственно для записи сообщений пользовательским кодом
class Logger
{
friend class Log;
static Log s_log;
long current_node;
public:
 Logger() : current_node(Log::node)
 {
    ++Log::node;//здесь нужна синхронизация
 }

 ~Logger()
 {
   s_log.write(current_node, "return");
 }

 void Write(LPCSTR str)
 {
    s_log.write(current_node, str);
 }
};


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


Это сообщение отредактировал(а) Lazin - 21.11.2007, 11:55
PM MAIL Skype GTalk   Вверх
archimed7592
Дата 21.11.2007, 12:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(Lazin @  21.11.2007,  11:54 Найти цитируемый пост)
 ~Logger()
 {
   s_log.write(current_node, "return");
 }

Я бы ещё добавил
Код
s_log.write(current_node, std::uncaught_exception() ? "return(stack unwinding)" : "return");



--------------------
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
PM Jabber   Вверх
SaDFromSpb
Дата 21.11.2007, 12:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 5.4.2006
Где: Санкт-Петербург

Репутация: 3
Всего: 3



А вот, кстати, содержимое boost/current_function.hpp:
Код

namespace boost
{

namespace detail
{

inline void current_function_helper()
{

#if defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600))

# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__

#elif defined(__FUNCSIG__)

# define BOOST_CURRENT_FUNCTION __FUNCSIG__

#elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500))

# define BOOST_CURRENT_FUNCTION __FUNCTION__

#elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550)

# define BOOST_CURRENT_FUNCTION __FUNC__

#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)

# define BOOST_CURRENT_FUNCTION __func__

#else

# define BOOST_CURRENT_FUNCTION "(unknown)"

#endif

}

} // namespace detail

} // namespace boost


Все весьма банально =)



--------------------
"За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001)
PM   Вверх
dumb
Дата 21.11.2007, 14:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


sceloglauxalbifacies
****


Профиль
Группа: Экс. модератор
Сообщений: 2929
Регистрация: 16.6.2006

Репутация: 8
Всего: 158



Цитата(_Tanatos_ @  21.11.2007,  11:13 Найти цитируемый пост)
буду писать на C++ Builder а он не знает не __func__, __FUNCTION__ и __PRETTY_FUNCTION__
он знает __FUNC__smile
PM MAIL   Вверх
JackYF
Дата 21.11.2007, 17:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 18
Всего: 162



Цитата(Mayk @  21.11.2007,  04:37 Найти цитируемый пост)
в бусте есть boost/current_function.hpp  

* ушёл смотреть *

ну вот, уже выложили smile
ну так, в принципе, и предполагалось.

Буст подруливает.
Maykу однозначно плюс.


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
_Tanatos_
Дата 22.11.2007, 13:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Подскажите какой тип лучше использовать для хранения времени события и заодно какие функции для работы с ним.
PM MAIL   Вверх
Lazin
Дата 22.11.2007, 13:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



структура FILETIME, функции GetLocalTyme SystemTimeToFileTime
PM MAIL Skype GTalk   Вверх
SaDFromSpb
Дата 22.11.2007, 15:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 5.4.2006
Где: Санкт-Петербург

Репутация: 3
Всего: 3



Цитата(_Tanatos_ @  22.11.2007,  13:41 Найти цитируемый пост)
Подскажите какой тип лучше использовать для хранения времени события и заодно какие функции для работы с ним. 

А что, в <ctime> большой выбор для этого?  smile  Или что-то нестандартное хочешь использовать? Время события системное хочешь использовать, или считая от запуска программы?



--------------------
"За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001)
PM   Вверх
StarikanMan
Дата 22.11.2007, 16:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 1
Регистрация: 22.11.2007

Репутация: нет
Всего: нет



bsa, он написал очень хороший пример
PM MAIL   Вверх
dumb
Дата 22.11.2007, 16:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


sceloglauxalbifacies
****


Профиль
Группа: Экс. модератор
Сообщений: 2929
Регистрация: 16.6.2006

Репутация: 8
Всего: 158



всем, кто пытается осмыслить посты StarikansBack'а - этот убогий является каким-то обгашенным куском недоразума, генерирующим случайные наборы слов.
PM MAIL   Вверх
Lazin
Дата 22.11.2007, 16:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(dumb @  22.11.2007,  16:32 Найти цитируемый пост)
всем, кто пытается осмыслить посты StarikansBack'а - этот убогий является каким-то обгашенным куском недоразума, генерирующим случайные наборы слов. 

Кстати пример кода взят из одного из недавних постов на форуме))
PM MAIL Skype GTalk   Вверх
Lazin
Дата 22.11.2007, 17:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(Alexeis @  22.11.2007,  16:55 Найти цитируемый пост)
[offtop]Это по ходу обиженный на Гуеду RinOSPro, который мстит за то что его банили.[/offtop] 

точнее множество его клонов))
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 23.11.2007, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Вот что у меня получилось на данный момент 
Привожу фрагмент кода с описанием структуры данных одной записи:

Код

typedef struct
{
    unsigned long   m_RowID;    // Порядковый номер строки
    int             m_Level;    // Уровень вложенности
    int             m_ProcID;   // Идентификатор процесса
    int             m_UnitID;   // Идентификатор модуля
    char            m_Type;     // Тип сообщенияы
    int             m_Error;    // Код ошибки
    char*           m_Msg;      // Текст сообщения
    char*           m_Data;     // Дополнительные данные
    char*           m_Function; // Имя функции
    char*           m_File;     // Имя файла
    int             m_Line;     // Номер строки
    SYSTEMTIME      m_Time;     // Время события
}
TtLibLogRecord;


Какие есть мысли и соображения?
Изначально буду реализовывать функцию регистрации записи с полным перечнем 
Поля m_Level и m_Time обрабатываются автоматически внутри класса.
Предполагаю, что использование полей m_Type, m_File и m_Line будет внутри макроса, тем самым сократиться общая длина записи
Макрос __FUNC__ пока не буду использовать, так как нет гарантий, что при смене компилятора работоспособность кода сохранится.
Для времени пока остановился на SYSTEMTIME, дабы не вызывать лишние функции преобразования, да и при записи в текстовый файл не надо будетзаниматься обратной конвертацией для форматирования.

далее приведено определение обработчика для регистрации новой записи
Код

typedef void (__closure *TtLibOnLogAdd)(CtLibLogEngine& LogEngine);
typedef std::vector<TtLibOnLogAdd> TtLibLogSubscribers;


Вот и сам класс журналирования
Код

class CtLibLogEngine
{
private:
    TtLibLogRecord      m_Record;
    unsigned long       m_RowID;
    TtLibLogSubscribers m_Subscribers;
protected:
public:
   ~CtLibLogEngine();
    CtLibLogEngine();

    inline const TtLibLogRecord& getRecord() const;

    inline void SubscriberAdd(TtLibOnLogAdd OnLogAdd);
    inline void SubscriberRemove(TtLibOnLogAdd OnLogAdd);

    inline void RegisterMsg(int ProcID, int UnitID, char Type, int Error, const char* Msg, const char* Data, const char* Function, const char* File, int Line);
};


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

примерно так.
Вспомогательный класс опишу чуть позже.

Поддержка многопоточности на последний этап, так как это на перспективу и мне пока не нужно.

Из актуальных вопросов это регистрация условий обработки - ограничение по минимальному и максимальному уровню для m_Type для каждого отдельного m_UnitID, что позволит сократить объем сохраняемых записей.

Пока планирую сделать так:
Код

typedef struct
{
    int     m_UnitID;   // Идентификатор модуля
    char    m_Min;      // Минимальное значение для m_Type
    char    m_Max;      // Максимальное значение для m_Type
}
TtLibLogCondition;


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

При необходимости задавать условия для каждого обработчика отдельно, это уже реализуется внутри конкретного обработчика.

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

Это сообщение отредактировал(а) _Tanatos_ - 23.11.2007, 14:27
PM MAIL   Вверх
Lazin
Дата 23.11.2007, 14:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(_Tanatos_ @  23.11.2007,  14:17 Найти цитируемый пост)

Код

typedef struct
{
    unsigned long   m_RowID;    // Порядковый номер строки
    int             m_Level;    // Уровень вложенности
    int             m_ProcID;   // Идентификатор процесса
    int             m_UnitID;   // Идентификатор модуля
    char            m_Type;     // Тип сообщенияы
    int             m_Error;    // Код ошибки
    char*           m_Msg;      // Текст сообщения
    char*           m_Data;     // Дополнительные данные
    char*           m_Function; // Имя функции
    char*           m_File;     // Имя файла
    int             m_Line;     // Номер строки
    SYSTEMTIME      m_Time;     // Время события
}
TtLibLogRecord;

Мне кажется эта структура сильно избыточна, если знаешь имя ф-ии, то имя файла и номер строки знать не обязательно. И имя ф-ии можно указывать только при входе в ф-ию, с другой стороны нет идентификатора потока, в многопоточном приложении записи из разных потоков смешаются, и получится хаос. Уровень вложенности нужно расчитывать для каждого потока отдельно. Еще можно расчитывать m_Level в зависимости от степени вложенности.
PM MAIL Skype GTalk   Вверх
Mayk
Дата 23.11.2007, 15:52 (ссылка) |   (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(Lazin @  23.11.2007,  18:53 Найти цитируемый пост)

Мне кажется эта структура сильно избыточна, если знаешь имя ф-ии, то имя файла и номер строки знать не обязательно. 

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

Цитата(_Tanatos_ @  23.11.2007,  18:17 Найти цитируемый пост)
typedef void (__closure *TtLibOnLogAdd)(CtLibLogEngine& LogEngine);

если хочешь портируемый код, то надо избавиться от не портируемого __closure. 
Цитата(_Tanatos_ @  23.11.2007,  18:17 Найти цитируемый пост)
    char*           m_Msg;      // Текст сообщения
    char*           m_Data;     // Дополнительные данные

Кто выделяет и освобождает память для этих полей? 

Цитата(_Tanatos_ @  23.11.2007,  18:17 Найти цитируемый пост)
    char            m_Type;     // Тип сообщенияы

только 256 типов сообщений? а не мало?


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Lazin
Дата 23.11.2007, 16:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(Mayk @  23.11.2007,  15:52 Найти цитируемый пост)
не согласен. имя файла и номер строки очень удобны. в любом уважающим себя редакторе есть ф-ция перехода на заданную строку. Перейти на определение ф-ции значительно сложнее. К тому же иногда хотелось бы посмортреть до каких строк работает прога, а до каких она не доходит. 

Ну номер строки еще ладно, но имя файла и имя ф-ии нужно указывать только при их изменении.
Цитата(Mayk @  23.11.2007,  15:52 Найти цитируемый пост)
если хочешь портируемый код, то надо избавиться от не портируемого __closure. 

так он же написал, что использует Borland? а есть ли какая-нибудь портируемая вльтернатива __closure ?
Цитата(Mayk @  23.11.2007,  15:52 Найти цитируемый пост)
Кто выделяет и освобождает память для этих полей? 

присоединяюсь

можно еще добавить поле - "категория", например все сообщения имеющие отношение к работе с базой данных должны иметь одну категорию, все, что имеют отношение к взаимодействию между процессами, другую, плюс должна быть возможность комбинировать категории. Это поможет в итоге отфильтровать лог, при поиске конкретной проблемы.
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 23.11.2007, 16:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата

только 256 типов сообщений? а не мало?

А надо больше? Сколько не прикидывал, пока больше 10 не набиралось smile. Тип означает: Info, Warning, Error и аналогичное деление.
Думаю использовать более 10 уровней детализации не имеет смысла ибо ни один программист не сможет в них свободно ориентироваться.

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

Цитата

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

А чем заменить? Если просто убрать, то указатель будет на отдельную функцию, а мне надо на функцию класса.

Цитата

Мне кажется эта структура сильно избыточна, если знаешь имя ф-ии, то имя файла и номер строки знать не обязательно. 

В данном случае больше не меньше. Реальный вызов гораздо более компактный. И потом я планирую сделать браузер для логов, который позволит просматривать исходники ... если будет время добавлю поддержку компиляторов, чтобы файлы открывались именно в них (но это не скоро).
PM MAIL   Вверх
Mayk
Дата 23.11.2007, 18:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(Lazin @  23.11.2007,  20:33 Найти цитируемый пост)

так он же написал, что использует Borland?

Цитата(_Tanatos_ @  23.11.2007,  18:17 Найти цитируемый пост)
Макрос __FUNC__ пока не буду использовать, так как нет гарантий, что при смене компилятора работоспособность кода сохранится.

При использование __closure есть  работоспосбность кода при смене компилятора не сохранится вообще.

Цитата(_Tanatos_ @  23.11.2007,  20:38 Найти цитируемый пост)

А чем заменить? Если просто убрать, то указатель будет на отдельную функцию, а мне надо на функцию класса.

Цитата(Lazin @  23.11.2007,  20:33 Найти цитируемый пост)
а есть ли какая-нибудь портируемая вльтернатива __closure ?

boost::bind + boost::function, например. 

Цитата(_Tanatos_ @  23.11.2007,  20:38 Найти цитируемый пост)

Думаю использовать более 10 уровней детализации не имеет смысла ибо ни один программист не сможет в них свободно ориентироваться.

А если мне хочется нумеровать сообщения, начиная с 0x10000000[например сообщения из .exe], а в другом месте с 0x200000[например, сообщения из .dll]? или использование юзеровских типов не предусмотрено? а почему тогда не использовать enum? 

Цитата(Lazin @  23.11.2007,  20:33 Найти цитируемый пост)

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

ОпределенноХорошаяМысль.


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
_Tanatos_
Дата 23.11.2007, 19:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата

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


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

Цитата

А чем заменить? Если просто убрать, то указатель будет на отдельную функцию, а мне надо на функцию класса.
а есть ли какая-нибудь портируемая вльтернатива __closure ?
boost::bind + boost::function, например.


Посмотрю - как разберусь исправлю. Спасибо за информацию!

Цитата

А если мне хочется нумеровать сообщения, начиная с 0x10000000[например сообщения из .exe], а в другом месте с 0x200000[например, сообщения из .dll]? или использование юзеровских типов не предусмотрено? а почему тогда не использовать enum?


Нет проблем, пользуйте m_Proc или m_Unit.
Кстати спрашивали насчет потоков ... под них так же планируется использовать m_Proc - ИМХО предостаточно.
Смысла писать туда реальный идентификатор потока не вижу, так как он от раза к разу меняется, можно его скинуть как данные при старте потока или процесса. А с логе использовать внутренние идентификаторы.

Цитата

а почему тогда не использовать enum? 

А зачем? Предполагаю использовать отрицательные значения для нескольких предопределенных значений, остальное на усмотрение программиста, так как никакой нагрузки смысловой (в пределах кода журнала) эти данные не несут - а значит и ограничивать нет смысла.
Пусть каждый сам разобьет на то количество уровней которое ему нужно, а для варианта по умолчанию уже есть enum но его можно или использовать или нет.
PM MAIL   Вверх
SaDFromSpb
Дата 23.11.2007, 22:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 5.4.2006
Где: Санкт-Петербург

Репутация: 3
Всего: 3



Цитата(_Tanatos_ @  23.11.2007,  19:14 Найти цитируемый пост)
Предполагаю использовать отрицательные значения для нескольких предопределенных значений

Тогда, на всякий случай определи его как signed char, так как стандарт этого не гарантирует по умолчанию.

И давай скорее пример использования, а то так разбираться лениво  smile ...



--------------------
"За исключением части, касающейся потоков, библиотека Loki написана на стандартном языке С++. Увы, это означает, что многие современные компиляторы не смогут работать с ней в полном объеме." (А. Александреску. Modern C++ design. 2001)
PM   Вверх
_Tanatos_
Дата 26.11.2007, 13:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



На суд уважаемых форумчан выдвигается несколько вариантов решения с вложенностью, а именно

Вариант №1: (мне нравится больше всего) вложенность управляется при помощи параметра m_Level. Его изменение производится исключительно классом TtLibLogEngine. Командой для изменения служат зарезервированные значения типа сообщения m_Type:
-1 - Увеличиваем вложенность
-2 - Уменьшаем вложенность
Плюс данного решения в том, что нет необходимости вводить дополнительные переменные, функции и т.д., минус нельзя одновременно зафиксировать запись пользовательского типа с вложенностью.
Пример использования:
Код

...
    // Фрагмент из библтотеки
    lltBegin        = -1,
    lltComplete     = -2,
...
    // Фрагмент из кода программы, использующую библиотеку
void SomeFunction()
{
    // Первая строчка функции (при использовании макроса будет еще короче, но макросы на потом)
    g_LogEngine.RegisterMsg(0, 0, lltBegin, 0, "Начинаем важное дело ...", "None", "::SomeFunction", __FILE__, __LINE__);
    ...
    // Последняя строчка функции.
    g_LogEngine.RegisterMsg(0, 0, lltComplete, 0, "... Выполнено!", "None", "::SomeFunction", __FILE__, __LINE__);
}


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

void SomeFunction()
{
    // Первая строчка функции (при использовании макроса будет еще короче, но макросы на потом)
    g_LogEngine.RegisterMsgBegin(0, 0, UserType, 0, "Начинаем важное дело ...", "None", "::SomeFunction", __FILE__, __LINE__);
    ...
    // Последняя строчка функции.
    g_LogEngine.RegisterMsgEnd(0, 0, UserType, 0, "... Выполнено!", "None", "::SomeFunction", __FILE__, __LINE__);
}


Вариант №3: Вынести управление в отдельную функцию, которая будет вызываться перед регистрацией сообщения и будет модифицировать одну последующую запись. ИМХО хуже из-за лишнего вызова, хотя можно сделать с модификатором inline.
Пример использования:
Код

void SomeFunction()
{
    // Первая строчка функции (при использовании макроса будет еще короче, но макросы на потом)
    g_LogEngine.Begin()
    g_LogEngine.RegisterMsg(0, 0, UserType, 0, "Начинаем важное дело ...", "None", "::SomeFunction", __FILE__, __LINE__);
    ...
    // Последняя строчка функции.
    g_LogEngine.End()
    g_LogEngine.RegisterMsg(0, 0, UserType, 0, "... Выполнено!", "None", "::SomeFunction", __FILE__, __LINE__);
}


--------------------

Предложение, насчет передачи имени класса и функции только один раз, отклюняю, так как:
1) Никто не мешает сделать это собственными силами. Объявляйте константы, ссылки или просто записывайте в лог один раз, а остальные записи оставляйте пустыми.
2) Как Вы себе представляете восстановление данных значений после выхода из вложенной функции???

--------------------

Цитата

Тогда, на всякий случай определи его как signed char

Принято.

Цитата

И давай скорее пример использования

На этой неделе будет выложен исходник с использованием.
Пока пишу библиотеку в перерывах (не многочисленных) между основной работой :(, так что сделано достаточно мало.
PM MAIL   Вверх
Lazin
Дата 26.11.2007, 14:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(_Tanatos_ @  26.11.2007,  13:52 Найти цитируемый пост)
TtLibLogEngine. Командой для изменения служат зарезервированные значения типа сообщения m_Type:
-1 - Увеличиваем вложенность
-2 - Уменьшаем вложенность

Правильнее использовать rtti, создаешь в стеке объект, в конструкторе вызывается нужная функция(увеличивает вложенность), в деструкторе уменьшаем вложенность, а как передавать - детали. Нужно различать поток, из которого вызывается ф-я, и вычислять вложенность отдельно для каждого потока. Это можно сделать внутри TtLibLogEngine, при каждом вызове ф-ии получать ThreadId потока, и дальше вычислять для него степень вложенности.
Я зделал что то похожее, но у меня - просто надстройка над библиотекой ведения логов, которая немного форматирует текст.


Код

#ifndef LoggerH
#define LoggerH


#    include <windows.h>
#    include <map>
#    include "mydeb.h"

#    define myBASE_LOG_INDENT 1
#    define myMAX_DBG_LEVEL 7

///RAII класс для записи иерархии сообщений в лог
class Logger
{
protected:
    int        m_level;
    long    m_indent;
public:

    static int GetCurrentDbgLevel();

    Logger(const char* func_name, int lvl);

    ~Logger();

    void Write(const char* msg) const;
};

///Класс для записи заголовка лог-файла
class LogHeader
{
public:
    LogHeader(const char* msg);

    const LogHeader& operator () (const char* msg) const;

    const LogHeader& ModuleVersion (HINSTANCE inst) const;

    const LogHeader& ModuleVersion(const char* filename) const;
};

///    myMACRO
#    define LOG_FUNCTION_AT_LEVEL(l) Logger __my_log ( __FUNCSIG__, l);
#    define LOG_FUNCTION() Logger __my_log ( __FUNCSIG__, Logger::GetCurrentDbgLevel() );
#    define LOG_MESSAGE(s) __my_log.Write( (s) );

#endif




Код

#include "Logger.h"
#include <string>
#include <exception>

///mylog----------------------------------------------------------------------------------
class myLog
{
    friend class Logger;
    friend class LogHeader;
private:
    std::map<DWORD, long> indent_level;
    CRITICAL_SECTION lock;
public:
    myLog();

    ~myLog();
private:
    DWORD InitializeCurrentThreadContext();

    long GetIndentLevel();

    void Enter();

    void Leave();
    
    void Write(int level, const char* msg) const;

    void AddHeaderStr( const char* msg ) const;

    void AddVersionForInst(HINSTANCE inst) const;

    void AddVersionForName(const char* fname) const;
};


static oikLog s_log;


oikLog :: myLog() : indent_level()
{
    InitializeCriticalSection(&lock);
    InitializeCurrentThreadContext();
}

myLog :: ~myLog()
{
    DeleteCriticalSection(&lock);
}

DWORD myLog :: InitializeCurrentThreadContext()
{
    EnterCriticalSection(&lock);
    DWORD id = GetCurrentThreadId();
    if ( indent_level.find(id) == indent_level.end() )
    {
        indent_level[id] = myBASE_LOG_INDENT;
    }
    LeaveCriticalSection(&lock);
    return id;
}

long oikLog :: GetIndentLevel()
{
    DWORD id = InitializeCurrentThreadContext();
    EnterCriticalSection(&lock);
    long indent = indent_level[id];
    LeaveCriticalSection(&lock);
    return indent;
}

void oikLog :: Enter()
{
    DWORD id = InitializeCurrentThreadContext();
    EnterCriticalSection(&lock);
    indent_level[id]++;
    LeaveCriticalSection(&lock);
}

void oikLog :: Leave()
{        
    DWORD id = InitializeCurrentThreadContext();
    EnterCriticalSection(&lock);
    indent_level[id]--;
    LeaveCriticalSection(&lock);
}

void oikLog :: Write(int level, const char* msg) const
{
    DebugMsg( level, (LPSTR)msg );//запись в лог
}

void oikLog :: AddHeaderStr( const char* msg ) const
{
    AddHdrString( (LPSTR)msg );//запись в лог
}

void oikLog :: AddVersionForInst(HINSTANCE inst) const
{
    AddVerInfoHdrFoInstance(inst);//запись в лог
}

void oikLog :: AddVersionForName(const char* fname) const
{
    AddVerInfoHdrFoFileName((LPSTR)fname);//запись в лог
}


///logger----------------------------------------------------------------------------------
int Logger :: GetCurrentDbgLevel()
{
    long indent = s_log.GetIndentLevel();
    return (indent > myMAX_DBG_LEVEL) ? (int)myMAX_DBG_LEVEL : (int)indent;
}

Logger :: Logger(const char* func_name, int lvl) : m_level( lvl ), m_indent( s_log.GetIndentLevel() )
{
    Write(func_name);
    Write("{");
    s_log.Enter();
    m_indent = s_log.GetIndentLevel();
}

Logger :: ~Logger()
{
    if (std::uncaught_exception())
        Write("#return: stack unwinding");
    else
        Write("#return");
    s_log.Leave();
    m_indent--;
    Write("}");
}

void Logger :: Write(const char* msg) const
{
    std::string m;
    m.assign(m_indent*4, ' ');
    m.append(msg);
    s_log.Write(m_level, m.c_str());
}



///logheader-------------------------------------------------------------------------------
LogHeader :: LogHeader(const char* msg)
{
    s_log.AddHeaderStr(msg);
}

const LogHeader& LogHeader :: operator () (const char* msg) const
{
    s_log.AddHeaderStr(msg);
    return *this;
}

const LogHeader& LogHeader :: ModuleVersion (HINSTANCE inst) const
{
    s_log.AddVersionForInst(inst);
    return *this;
}

const LogHeader& LogHeader :: ModuleVersion(const char* filename) const
{
    s_log.AddVersionForName(filename);
    return *this;
}



Это сообщение отредактировал(а) Lazin - 26.11.2007, 15:23
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 26.11.2007, 14:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата

Цитата
TtLibLogEngine. Командой для изменения служат зарезервированные значения типа сообщения m_Type:
-1 - Увеличиваем вложенность
-2 - Уменьшаем вложенность

Правильнее использовать rtti, создаешь в стеке объект, в конструкторе вызывается нужная функция(увеличивает вложенность), в деструкторе уменьшаем вложенность, а как передавать - детали. Нужно различать поток, из которого вызывается ф-я, и вычислять вложенность отдельно для каждого потока. Это можно сделать внутри TtLibLogEngine, при каждом вызове ф-ии получать ThreadId потока, и дальше вычислять для него степень вложенности.


Так оно и будет. Будет дополнительный класс CtLibLogHelper, который будет в конструкторе регистрировать сообщение с увеличением вложенности, а в деструкторе собщение с уменьшением вложенности.

За пример спасибо. Изучаю.
PM MAIL   Вверх
_Tanatos_
Дата 26.11.2007, 15:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(Lazin @  26.11.2007,  14:23 Найти цитируемый пост)
static int GetCurrentDbgLevel();


Выложи здесь реализацию данной функции?
Заранее благодарю!
PM MAIL   Вверх
Lazin
Дата 26.11.2007, 15:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(Lazin @  26.11.2007,  14:23 Найти цитируемый пост)
Код

///logger----------------------------------------------------------------------------------
int Logger :: GetCurrentDbgLevel()
{
    long indent = s_log.GetIndentLevel();
    return (indent > myMAX_DBG_LEVEL) ? (int)myMAX_DBG_LEVEL : (int)indent;
}

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

Это сообщение отредактировал(а) Lazin - 26.11.2007, 15:26
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 26.11.2007, 15:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



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

Код

void CDocument::Save(const char* FileName)
{
      CtLibLogHelper LogHelper(ProcID, ThreadID, "CDocument::Save");
      LogHelper.Begin("Сохраняем документ...", "Имя файла: "+FileName, __FILE__, __LINE__);

      // Открываем файл
      LogHelper.Message(lltInfo, "Открываем файл", "", __FILE__, __LINE__);
      ...
      if(File == INVALID)
          LogHelper.Error(lltError, 134, "невозможно создать файл.", "", __FILE__, __LINE__);
      // Что-то пишем
      ...

      // Закрываем файл
      CloseHandle(File);
      LogHelper.Complete("... Готово!", "Записанно:"+Count+" строк", __FILE__, __LINE__);
}


В конструкторе класса CtLibLogHelper указываем идентификатор Процесса, потока и имя класса и функции - данные указываются один раз для одной функции. Соответственно все записи производятся через вспомогательный класс. Функция Error производит запись в журнал и сама генерирует исключение с указанным кодом (в данном случае 134).
PM MAIL   Вверх
Lazin
Дата 26.11.2007, 16:05 (ссылка) |   (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(_Tanatos_ @  26.11.2007,  15:55 Найти цитируемый пост)
Функция Error производит запись в журнал и сама генерирует исключение с указанным кодом (в данном случае 134). 

Мне кажется это противоестественным. Пользователь класса вряд-ли будет расчитывать на то что писалка в лог кидает исключения. Не стоит смешивать подсистему обработки ошибок и подсистему записи в лог. Если к примеру ты захочешь убрать запись в лог, это приведет к изменению логики работы программы, а запись логов не должна на логику влиять.
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 26.11.2007, 16:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(Lazin @ 26.11.2007,  16:05)
Цитата(_Tanatos_ @  26.11.2007,  15:55 Найти цитируемый пост)
Функция Error производит запись в журнал и сама генерирует исключение с указанным кодом (в данном случае 134). 

Мне кажется это противоестественным. Пользователь класса вряд-ли будет расчитывать на то что писалка в лог кидает исключения. Не стоит смешивать подсистему обработки ошибок и подсистему записи в лог. Если к примеру ты захочешь убрать запись в лог, это приведет к изменению логики работы программы, а запись логов не должна на логику влиять.

Согласен. Принято к изменению. 
PM MAIL   Вверх
_Tanatos_
Дата 26.11.2007, 17:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(archimed7592 @ 21.11.2007,  12:01)
Цитата(Lazin @  21.11.2007,  11:54 Найти цитируемый пост)
 ~Logger()
 {
   s_log.write(current_node, "return");
 }

Я бы ещё добавил
Код
s_log.write(current_node, std::uncaught_exception() ? "return(stack unwinding)" : "return");

Попробовал - неработает!
после неудачи порылся в инете на предмет использования, вот такой пример не работает
Что делаю не так?


ИМХО лучше по старинке через флаг внутри класса и дополнительную функцию для его сброса.
Код

        if(m_Complete)
            g_LogEngine->RegisterMsg(...);
        else
            g_LogEngine->RegisterMsg(...);



PM MAIL   Вверх
archimed7592
Дата 26.11.2007, 18:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(_Tanatos_ @  26.11.2007,  17:44 Найти цитируемый пост)
Попробовал - неработает!

Наверное это причуды твоего BCB smile.


--------------------
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
PM Jabber   Вверх
Lazin
Дата 27.11.2007, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(_Tanatos_ @  26.11.2007,  17:44 Найти цитируемый пост)
Попробовал - неработает!
после неудачи порылся в инете на предмет использования, вот такой пример не работает
Что делаю не так?

Обрати внимание, на то, что std::uncaught_exception( ) срабатывает только если объект был создан внутри блока try, иначе он не вызывается. С Builderoм STL нормально работает...
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 27.11.2007, 20:15 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата

Обрати внимание, на то, что std::uncaught_exception( ) срабатывает только если объект был создан внутри блока try, иначе он не вызывается. С Builderoм STL нормально работает...

Не знаю как нормально, но у меня не получилось добиться работы.

Добавлено через 13 минут и 38 секунд
Выкладываю на ваш суд черновой вариант классов

Начну с вспомогательных структур:
Код

//---------------------------------------------------------------------------
// Набор констант для использования с полем Type
typedef enum
{
    // Отрицательные значения зарезервированы, не использовать!
    lltBegin        = -1,
    lltComplete     = -2,

    lltDebug        = 0,
    lltInfo         = 1,
    lltNotice       = 2,
    lltError        = 3,
    lltCritical     = 4,
    lltAlert        = 5,
    lltEmergency    = 6,
}
TtLibLogType;
//---------------------------------------------------------------------------
class CtLibLogEngine;
class CtLibLogHelper;
//---------------------------------------------------------------------------
// Глобальный экземпляр класса
// TODO: в принципе надо заменить на указатель дабы
// для DLL можно было подключать логгер из основного процесса
extern CtLibLogEngine g_LogEngine;
//---------------------------------------------------------------------------
// Указатель на функцию-подписчик на событие добавления записи в журнал
typedef void (__closure *TtLibOnLogAdd)(CtLibLogEngine* LogEngine);
//---------------------------------------------------------------------------
// Массив подписчиков
typedef std::vector<TtLibOnLogAdd> TtLibLogSubscribers;
//---------------------------------------------------------------------------
// Эта структура описывает все данные одной записи
typedef struct
{
    unsigned long   m_RowID;    // Порядковый номер строки
    int             m_Level;    // Уровень вложенности
    signed char     m_LevelCh;  // Изменение уровня (пока не задействовано)
    int             m_ProcID;   // Идентификатор процесса
    int             m_UnitID;   // Идентификатор модуля
    signed char     m_Type;     // Тип сообщенияы
    int             m_Error;    // Код ошибки
    char*           m_Msg;      // Текст сообщения
    char*           m_Data;     // Дополнительные данные
    char*           m_Function; // Имя функции
    char*           m_File;     // Имя файла
    int             m_Line;     // Номер строки
    SYSTEMTIME      m_Time;     // Время события
}
TtLibLogRecord;
//---------------------------------------------------------------------------
// Структура для хранения условий обработки по модулям
typedef struct
{
    int     m_UnitID;   // Идентификатор модуля
    char    m_Min;      // Минимальное значение для m_Type
    char    m_Max;      // Максимальное значение для m_Type
}
TtLibLogCondition;
//---------------------------------------------------------------------------
// Массив условий
typedef std::vector<TtLibLogCondition> TtLibLogConditions;
//---------------------------------------------------------------------------


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

class CtLibLogEngine
{
friend class CtLibLogHelper;
private:
    unsigned long       m_RowID;
    int                 m_Level;

    const char*         m_MsgBreakError;
    const char*         m_MsgBreakOk;
protected:
    TtLibLogRecord      m_Record;           // Данные сообщения
    TtLibLogConditions  m_Conditions;       // Условия фильтрации сообщений
    TtLibLogSubscribers m_Subscribers;      // Массив подписчиков на сообщения
public:
   ~CtLibLogEngine()
        {};
    CtLibLogEngine()
        : m_MsgBreakError(""),
          m_MsgBreakOk(""),
          m_RowID(0),
          m_Level(0),
          m_Record(TtLibLogRecord())
        {};

    void Initialize(const char* MsgBreakError, const char* MsgBreakOk)
    {
        m_MsgBreakError = MsgBreakError;
        m_MsgBreakOk = MsgBreakOk;
    }

    inline const TtLibLogRecord& getRecord() const
        { return m_Record; }

    void ConditionAdd(int UnitID, char Min, char Max)
        {
            m_Conditions.push_back();
            m_Conditions.back().m_UnitID = UnitID;
            m_Conditions.back().m_Min = Min;
            m_Conditions.back().m_Max = Max;
        }

    inline void SubscriberAdd(TtLibOnLogAdd OnLogAdd)
        {
            for(std::size_t i=0; i<m_Subscribers.size(); i++)
                if(m_Subscribers[i] == OnLogAdd)
                    return;
            m_Subscribers.push_back(OnLogAdd);
        }
    inline void SubscriberRemove(TtLibOnLogAdd OnLogAdd)
        {
            for(std::size_t i=0; i<m_Subscribers.size(); i++)
            {
                if(m_Subscribers[i] == OnLogAdd)
                {
                    for(std::size_t j=i; j<m_Subscribers.size()-1; j++)
                        m_Subscribers[j] = m_Subscribers[j+1];
                    m_Subscribers[m_Subscribers.size()-1] = NULL;
                    m_Subscribers.resize(m_Subscribers.size()-1);
                    return;
                }
            }
        };

    inline void RegisterMsg(int ProcID, int UnitID, char Type, int Error, const char* Msg, const char* Data, const char* Function, const char* File, int Line)
        {
            bool Ok = true;

            for(std::size_t i=0; i<m_Conditions.size(); i++)
                if(m_Conditions[i].m_UnitID)
                {
                    if(m_Conditions[i].m_Min<=Type && m_Conditions[i].m_Max>=Type)
                        { Ok = true; break; }
                    else
                        Ok = false;
                }

            m_RowID++;

            if(Ok)
            {
                m_Record.m_Level    = m_Level;
                m_Record.m_LevelCh  = 0;
                m_Record.m_RowID    = m_RowID;
                m_Record.m_ProcID   = ProcID;
                m_Record.m_UnitID   = UnitID;
                m_Record.m_Type     = Type;
                m_Record.m_Error    = Error;
                m_Record.m_Msg      = (char*) Msg;
                m_Record.m_Data     = (char*) Data;
                m_Record.m_Function = (char*) Function;
                m_Record.m_File     = (char*) File;
                m_Record.m_Line     = Line;
                GetLocalTime(&m_Record.m_Time);

                for(std::size_t i=0; i<m_Subscribers.size(); i++)
                    m_Subscribers[i](this);
            }
        }
};


Смотрим вспомогательный класс CtLibLogHelper
Код

class CtLibLogHelper
{
private:
    int             m_ProcID;       // Идентификатор процесса
    int             m_UnitID;       // Идентификатор модуля
    const char*     m_Function;     // Имя класса и функции
    const char*     m_File;         // Имя файла
    bool            m_Complete;     // Признак успешного завершения работы
protected:
public:
   ~CtLibLogHelper()
    {
        // Завершающая запись (номер строки неизвестен)
        if(m_Complete)
            g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, lltComplete, 0, g_LogEngine.m_MsgBreakOk, "", m_Function, m_File, 0);
        else
            g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, lltComplete, 0, g_LogEngine.m_MsgBreakError, "", m_Function, m_File, 0);
        g_LogEngine.m_Level--;
    }

    CtLibLogHelper(int ProcID, int UnitID, char Type, const char* Msg, const char* Data, const char* Function, const char* File, int Line)
        : m_Complete(false),
          m_ProcID(ProcID),
          m_UnitID(UnitID),
          m_Function(Function),
          m_File(File)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
        g_LogEngine.m_Level++;
    }
    CtLibLogHelper(char Type, const char* Msg, const char* Data, const char* Function, const char* File, int Line)
        : m_Complete(false),
          m_ProcID(0),
          m_UnitID(0),
          m_Function(Function),
          m_File(File)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
        g_LogEngine.m_Level++;
    }

    void Message(char Type, const char* Msg, const char* Data, int Line)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
    }

    void Error(char Type, int Error, const char* Msg, const char* Data, int Line)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, Error, Msg, Data, m_Function, m_File, Line);
    }

    void Complete(char Type, const char* Msg, const char* Data, int Line)
    {
        m_Complete = true;
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
    }
};


А теперь пример использования:
Код

// Инициализация
    g_LogEngine.Initialize("...return (stack unwinding)", "...return (complete)");
// У меня эта функция отображает на экране лог в виде дерева
    g_LogEngine.SubscriberAdd(OnLogAdd);

// пример использования для записи в журнал
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    CtLibLogHelper LogHelper(lltBegin, "Процедура проверки вложенности...", ("Sender = " + Sender->ClassName()).c_str(), __FUNC__, __FILE__, __LINE__);
    LogHelper.Message(lltInfo, "Запускаем процедуру проверки", "", __LINE__);
    LogHelper.Message(lltInfo, "  Этап №1", "", __LINE__);
    LogHelper.Message(lltInfo, "  Этап №2", "", __LINE__);
    LogHelper.Message(lltInfo, "  Этап №3", "", __LINE__);
    LogHelper.Message(lltError, 12, "  Принудительный сбой в работе", "Здесь добавляем отладочную информацию.", __LINE__);
    LogHelper.Complete(lltInfo, "Процедура проверки выполнена!", "", __LINE__);
}

Тут даже без макросов все довольно компактно получается.
Имя функции и файла указываются только один раз.
PM MAIL   Вверх
_Tanatos_
Дата 27.11.2007, 20:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Из текущих задач:
- сделать код безопасным для многопоточных приложений
- сделать глобальный объект указателем для поддержки DLL (при инициализации DLL надо соответственно инициировать указатель, а в основной программе соответственно создавать сам объект)
- Разработать класс (образец) подписчик для записи журнала в файл
- добавить раздельную нумерацию строк для потоков

Высказывайте свои пожелания, предложения и критику
PM MAIL   Вверх
_Tanatos_
Дата 27.11.2007, 21:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Примерно так выглядит класс записывающий данные в файл:

Код

class CtLibLogToFile
{
private:
    bool    m_Initialize;
    HANDLE  m_hFile;
protected:
public:
   ~CtLibLogToFile()
        {
            if(m_hFile)
                CloseHandle(m_hFile);
            m_hFile = NULL;
        }
    CtLibLogToFile()
        : m_Initialize(false),
          m_hFile(NULL)
        {};
    CtLibLogToFile(const char* FileName)
    {
        Initialize(FileName);
    };

    void Initialize(const char* FileName)
    {
        if(m_hFile==NULL && FileName!=NULL && m_Initialize==false)
        {
            m_hFile = CreateFile(FileName, GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if(m_hFile != INVALID_HANDLE_VALUE)
                m_Initialize = true;
        }
        else
            // Повторная инициализация невозможна
            throw;
    };

    void OnLogAdd(CtLibLogEngine* LogEngine)
    {
        unsigned long   R;
        static char     Buffer[256];
        static char     Level[50] = "                                                         ";

        const TtLibLogRecord& Rec = LogEngine->getRecord();

        // Level
        WriteFile(m_hFile, Level, Rec.m_Level, &R, NULL);

        // №№ | Дата | Время
        sprintf(Buffer, "%010d  %04d//%02d//%02d %02d:%02d:%02d[%d]", Rec.m_RowID, Rec.m_Time.wYear, Rec.m_Time.wMonth, Rec.m_Time.wDay, Rec.m_Time.wHour, Rec.m_Time.wMinute, Rec.m_Time.wSecond, Rec.m_Time.wMilliseconds);
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);
        // Код модуля | Код процесса/потока | Тип сообщения | Код ошибки |
        sprintf(Buffer, "  %04d %04d %03d %06d", Rec.m_UnitID, Rec.m_ProcID, Rec.m_Type, Rec.m_Error);
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);

        // Текст сообщения | Данные | Файл | Строка |
        sprintf(Buffer, "  %s || %s || %s [%0d]", Rec.m_Msg, Rec.m_Data, Rec.m_File, Rec.m_Line);
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);

        sprintf(Buffer, "\n");
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);
    };
};


Пример использования
Код

// Глобально
CtLibLogToFile LogToFile;
...
// В рамках инициализации
    LogToFile.Initialize("D:\\Temp\\Log.txt"); // <===

    g_LogEngine = g_LogEngine->Initialize("...return (stack unwinding)", "...return (complete)");
    g_LogEngine->SubscriberAdd(OnLogAdd);
    g_LogEngine->SubscriberAdd(LogToFile.OnLogAdd); // <===


PM MAIL   Вверх
UnrealMan
Дата 28.11.2007, 00:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  27.11.2007,  20:15 Найти цитируемый пост)
Тут даже без макросов все довольно компактно получается.

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

Этот логгер неудобно будет использовать для вывода составных данных - когда строка формируется из подстрок, чисел и прочих данных с нужным форматированием. Придётся самому вычислять строку и передавать её логгеру - это как минимум громоздко.
PM MAIL   Вверх
Lazin
Дата 28.11.2007, 09:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Можно сделать так что-бы ф-я SubscriberAdd получала не указатель на ф-ю с модификатором __closure, а указатель на класс, а все "подписчики" имели-бы одинаковый интерфейс, это было-бы переносимо на другие компиляторы.
Цитата(_Tanatos_ @  27.11.2007,  21:07 Найти цитируемый пост)
Примерно так выглядит класс записывающий данные в файл:

Этот класс не учитывает одного простого факта - что возможен запуск нескольких копий одного приложения, в этом случае в файле будет мусор. Нужно синхронизировать доступ к файлу, например создать именованный мьютекс и захватывать его пере записью, и выводить в лог код процесса, что-бы можно было различать вывод разных процессов.
Цитата(UnrealMan @  28.11.2007,  00:59 Найти цитируемый пост)
Указание типа лога через константу - IMHO, не самая лучшая идея.


Цитата(UnrealMan @  28.11.2007,  00:59 Найти цитируемый пост)
Этот логгер неудобно будет использовать для вывода составных данных - когда строка формируется из подстрок, чисел и прочих данных с нужным форматированием. Придётся самому вычислять строку и передавать её логгеру - это как минимум громоздко. 

Возможное решение этой проблеммы - использовать полиморфизм, передавать в объект logEngine объекты, содержащие нужную информацию. CtLibLogHelper может выступать в роли фабрики классов.

Добавлено через 2 минуты и 29 секунд
выглядеть это может так
Код

LogHelper.Message("blablabla");
LogHelper.AddValue("blablabla", val);
e.t.c.

PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 28.11.2007, 18:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Можно сделать так что-бы ф-я SubscriberAdd получала не указатель на ф-ю с модификатором __closure, а указатель на класс, а все "подписчики" имели-бы одинаковый интерфейс, это было-бы переносимо на другие компиляторы.


Принято:
Код

// Класс-подписчик на событие добавления записи в журнал
class CtLibLogSubscribe
{
public:
//    virtual ~CtLibLogSubscribe()=0;
    virtual void OnLogAdd(CtLibLogEngine* LogEngine)=0;
};

//---------------------------------------------------------------------------
typedef std::vector<CtLibLogSubscribe*> TtLibLogSubscribers;
//---------------------------------------------------------------------------

...

    inline void SubscriberAdd(CtLibLogSubscribe* Subscriber)
    inline void SubscriberRemove(CtLibLogSubscribe* Subscriber)

...

class CtLibLogToFile :
    public CtLibLogSubscribe
{
private:
...
}

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

Добавлено через 4 минуты и 24 секунды
Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Цитата(_Tanatos_ @  27.11.2007,  21:07 )Примерно так выглядит класс записывающий данные в файл:Этот класс не учитывает одного простого факта - что возможен запуск нескольких копий одного приложения, в этом случае в файле будет мусор. Нужно синхронизировать доступ к файлу, например создать именованный мьютекс и захватывать его пере записью, и выводить в лог код процесса, что-бы можно было различать вывод разных процессов.


Не хочу чересчур усложнять класс, который должен работать максимально быстро.
ИМХО это обходится крайне просто, при инициализации лога добавьте к имени файла идентификатор процесса.
А если нужна паралельная запись в файл, то надо сделать отдельный класс для этой задачи
Таких классов-обработчиков может быть много. Например для накопления сообщений и отсылки их по почте и т.д.
В данном случае мой класс является лишь примером. Сначала закончу с ядром, а уж потом займусь переферией.

Добавлено через 8 минут и 54 секунды
Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Цитата(UnrealMan @  28.11.2007,  00:59 )Указание типа лога через константу - IMHO, не самая лучшая идея.

Совершенно непонятное заявление! Поясни, что тебе не нравится? Не нравятся константы, пишы числа, переменные, выражения, да что угодно - главное чтобы результат был signed char. А лучше всего напиши чем именно не нравится и для какой задачи тебе это не подходит.

P.S.
Не ленитесь подробней изложить свою мысль, ведь от этого напрямую зависит результат
PM MAIL   Вверх
_Tanatos_
Дата 28.11.2007, 19:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(UnrealMan @  28.11.2007,  00:59 Найти цитируемый пост)
Этот логгер неудобно будет использовать для вывода составных данных - когда строка формируется из подстрок, чисел и прочих данных с нужным форматированием. Придётся самому вычислять строку и передавать её логгеру - это как минимум громоздко. 

А как ты себе представляешь класс логгера должен форматировать твои данные? Мне кажется Вы на этот класс возлагаете не его задачи. И потом громоздкое и длинное сообщение будет неудобоваримым. А если речь идет о данных, то для них есть отдельное поле (см ниже). Как вспомогательный инструмент можно написать функцию аналогичную sprintf, но возвращающую указатель на готовую строку и вставлять эту конструкцию вместо сообщения:

LogHelper.Message(lltInfo, Format("  Этап №%d", Number), "", __LINE__);

Годится такой вариант?

Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Возможное решение этой проблеммы - использовать полиморфизм, передавать в объект logEngine объекты, содержащие нужную информацию. CtLibLogHelper может выступать в роли фабрики классов.

Опять же непонятно зачем полиморфизм и причем тут фабрика классов? Приведенный тобой пример вполне подходит для формирования поля Data, если вызов функции AddValue будет приводить к добавлению строчки с именем переменной и ее значением. Если так, то согласен и постараюсь добавить несколько функций для работы с различными типами данных. Только проблема заключается в том, что надо будет так же добавлять функцию Post() в конце для определения момента когда сообщение полностью сформированно и его надо передать в класс Engine.

PM MAIL   Вверх
UnrealMan
Дата 28.11.2007, 21:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  28.11.2007,  19:15 Найти цитируемый пост)
А как ты себе представляешь класс логгера должен форматировать твои данные?

Так же, как это делают потоки типа fstream или хотя бы как fprintf.

Код
LOG_INFO("pass: ", std::setw(4), pass);
LOG_INFO_F("pass: %4.d", pass);


Цитата(_Tanatos_ @  28.11.2007,  19:15 Найти цитируемый пост)
Мне кажется Вы на этот класс возлагаете не его задачи. 

Это не первостепенная задача логгера, но тут можно провести аналогию с файловыми потоками и простым сишным выводом через fprintf. Для того, чтобы записать текст в файл, можно воспользоваться строковым потоком или sprintf и для результата вызвать write/fwrite. Но вряд ли кто-то захочет так делать, потому что проще не вводить лишние звенья, а сразу воспользоваться оператором << файлового потока или fprintf.

Цитата(_Tanatos_ @  28.11.2007,  19:15 Найти цитируемый пост)
Как вспомогательный инструмент можно написать функцию аналогичную sprintf, но возвращающую указатель на готовую строку и вставлять эту конструкцию вместо сообщения:

LogHelper.Message(lltInfo, Format("  Этап №%d", Number), "", __LINE__);

Годится такой вариант?

Это неудобный вариант.
PM MAIL   Вверх
_Tanatos_
Дата 29.11.2007, 12:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



В вашем примере указано только одно поле, а их как минимум два Сообщение и данные.
Такой вариант приемлим?
Код

LOG_INFO_MSG("Connect to %s", ServerName);
LOG_INFO_Data("Log: %4.d", login);
LOG_INFO_Data("pass: %4.d", pass);
LOG_WRITE;


P.S.
To All:
Спасибо всем, кто принимает участие в обсуждении!
Стараюсь учесть пожелания и предложения каждого
Сорри что изменения не так быстро воплощаются в код.

P.S.S.
Повторюсь, макросы пока не пишу так как они не сказываются на функциональности.
Понятное дело, что с ними короче и удобней, понятно что они будут и это даже не обсуждается.
Будут сделаны когда утоится состав и формат всех функций, так что не сердчайте на их отсутствие.
Но! Если здесь писать все в виде макросов, то совершенно непонятно получается что должно быть внутри.


Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 12:45
PM MAIL   Вверх
_Tanatos_
Дата 29.11.2007, 13:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Не хочется изобретать велосипед и раз уж речь зашла о форматировании, то хотелось бы использовать sprintf.
Только как передать переменное кол-во  аргументов дальше?

Код

void ExMsg(const char* Message, ...)
{
    ......
    sprintf(Temp, Message, /*Что здесь писать???*/);
    ......
}
    
Уважаемые гуру, нужен Ваш совет!
Писать множество отдельных функций для различных типов данных ИМХО нелогично (что делать если надо сразу два аргумента передать при форматировании одной строки?). 
PM MAIL   Вверх
UnrealMan
Дата 29.11.2007, 13:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  29.11.2007,  12:42 Найти цитируемый пост)
а их как минимум два Сообщение и данные

А какой резон отделять данные от сообщения?
PM MAIL   Вверх
UnrealMan
Дата 29.11.2007, 14:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  29.11.2007,  13:48 Найти цитируемый пост)
Только как передать переменное кол-во  аргументов дальше?

Код

#include <cstdarg>
#include <cstdio>

void Test(const char* format, ...)
{
    va_list ap;
    va_start(ap, format);
    
    char tmp[0x1000];
    
    vsprintf(tmp, format, ap);
    va_end(ap);

    printf(tmp);
}

int main()
{
    Test("Text: %s%d%s", "str1 ", 123, " str2 ");
}


Добавлено через 3 минуты и 32 секунды
Есть одно маленькое "но": теперь тебе придётся помнить о возможности переполнения буфера, используемого для sprintf.

Это сообщение отредактировал(а) UnrealMan - 29.11.2007, 14:05
PM MAIL   Вверх
_Tanatos_
Дата 29.11.2007, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Спасибо!

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

Вот пример использования:
Код

    LogHelper.ExMsg("Connect...");
    LogHelper.ExData("Address: %s\n", "address");
    LogHelper.ExData("Login: %s\n", "log");
    LogHelper.ExData("Password: %s\n", "pass123");
    LogHelper.ExPost(lltInfo, __LINE__);


У меня получилось немного иначе, так как при многократном вызове нужно сохранять предыдущую часть. Ну и требования по длинне сообщения смегчаются smile ограничена лишь максимальная длина сообщения для ОДНОЙ операции, но не общая
Так выглядит содержимое функции:
Код

    void ExMsg(const char* Message, ...)
    {
        int     Len = m_ExMessage ? strlen(m_ExMessage) : 0;
        char*   Temp = new char[Len+1024];
        va_list ap;

        if(m_ExMessage)
            strcpy(Temp, m_ExMessage);

        va_start(ap, Message);
        vsprintf(Temp+Len, Message, ap);
        va_end(ap);

        if(m_ExMessage)
        {
            delete m_ExMessage;
            m_ExMessage = NULL;
        }
        m_ExMessage = Temp;
    }


И парочку сервисных функций:
Код

    void ExPost(char Type, int Line)
    {
        g_LogEngine->RegisterMsg(m_ProcID, m_UnitID, Type, 0, m_ExMessage, m_ExData, m_Function, m_File, Line);
        ExReset();
    }
    void ExReset()
    {
        if(m_ExMessage)
        {
            delete m_ExMessage;
            m_ExMessage = NULL;
        }
        if(m_ExData)
        {
            delete m_ExData;
            m_ExData = NULL;
        }
    }

Естественно добавился вызов ExReset() в деструктор smile

Еще добавилось два конструктора:
Код

    CtLibLogHelper(int ProcID, int UnitID, char Type, const char* Function, const char* File, int Line);
    CtLibLogHelper(char Type, const char* Function, const char* File);

для инициализации класса без создания сообщения в конструкторе.

Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 15:01
PM MAIL   Вверх
Lazin
Дата 29.11.2007, 15:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Я не очень понимаю что нужно получить в итоге, наверное лучше сначала составить какой нибудь список требований к библиотеке т.е. что нам от нее надо....
использование элипсисов я считаю тупиковым, так как в данном случае теряется гибкость и расширяемость, если я сделаю свой класс, как мне расширить класс, что-бы он выводил в лог объекты моего класса. Для этого придется наследовать, от класса - логгера, и переопределять ф-ю. Лучше сделать это так, как это сделано в библиотеках iostream или boost::serializaion etc. 
должно выглядеть примерно так:
Код

int a = 10;
g_logger << "value a = " << a << my_logger::end;//выводим данные, потом выводим специальное значение, которое информирует объект о том что мы закончили формировать сообщение
//в лог должно после этого добавиться "**** value a = 10"
------
my_logger& operator << (my_logger& l, TPoint& p)//оператор вывода в лог объектов класса TPoint
{
 l << p.x << p.y;
 return *this;
}
------
TPoint p(10,20);
g_logger << "Point p = " << p << my_logger::end;

Можно использовать интерфейс как в iostream для изменения параметров (там это называется манипуляторами)
Код

g_logger << my_logger::step_into;//говорим логгеру, что нужно увеличить вложенность(новая функция)
g_logger << my_logger::return;//говорим логгеру, что нужно уменьшить степень вложенности

с таким интерфейсом проще написать макросы
Код

#define TRACE_MESSAGE(p1,p2,p3) \
            g_logger << (p1) << (p2) << (p3) << my_logger::end;

и так можно написать для любого кол. параметров

Это сообщение отредактировал(а) Lazin - 29.11.2007, 15:53
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 29.11.2007, 15:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Переработал класс CtLibLogHelper
Код

class CtLibLogHelper
{
private:
    int             m_ProcID;       // Идентификатор процесса
    int             m_UnitID;       // Идентификатор модуля
    const char*     m_Function;     // Имя класса и функции
    const char*     m_File;         // Имя файла
    bool            m_Complete;     // Признак успешного завершения работы
    bool            m_LevelUp;      // Признак инициализации с увеличением уровня

    char*           m_ExMessage;    // Сооьщение
    char*           m_ExData;       // Данные
protected:
public:
   ~CtLibLogHelper();

    // Инициализация класса
    // Если LevelUp=true то первая запись будет заголовком, остальные вложенными
    // Если LevelUp=false то все записи будут в пределах одного уровня
    CtLibLogHelper(const char* File, const char* Function, bool LevelUp=true, int UnitID=0, int ProcID=0);

    // Сохраняет фрагмент сообщения
    void AddMessage(const char* Message, ...);
    void AddData(const char* Data, ...);
    // Записывает подготовленное сообщение
    // Если Complete=true, то выставляется соответствующий флаг, на false не реагирует никак
    void Post(signed char Type, int Line, bool Complete = false);
    // Принудительное освобождение памяти занятой при формировании сообщения (всегда вызывается из Post)
    void Reset();
};


Примеры использования:
Код


void SubFunc()
{
// Инициируем класс, сохраняем имя файла, функции, признак вложенности отключаем!!!, код модуля и процесса/потока
    CtLibLogHelper LogHelper(__FILE__, __FUNC__, false);

// Формируем сообщение
    LogHelper.AddMessage("...Test...");
// Записываем подготовленное сообщение (никаких вложенностей, см. конструктор)
    LogHelper.Post(lltInfo, __LINE__);
}

// Функция которая ведет вложженную запись
void Simmple()
{
// Инициируем класс, сохраняем имя файла, функции, признак вложенности, код модуля и процесса/потока
    CtLibLogHelper LogHelper(__FILE__, __FUNC__);

// Формируем заголовок
    LogHelper.AddMessage("Begin");
// Записываем подготовленное сообщение (последующие сообщения будут вложенными)
    LogHelper.Post(lltBegin, __LINE__);

// Формируем новое сообщение
    LogHelper.AddMessage("Connect...");
    LogHelper.AddData("Address: %s\n", "address");
    LogHelper.AddData("Login: %s\n", "log");
    LogHelper.AddData("Password: %s\n", "pass123");
// Записываем подготовленное сообщение
    LogHelper.Post(lltInfo, __LINE__);

// Вызываем функцию
    SubFunc();

// Формируем завершающее сообщение
    LogHelper.AddMessage("...Ok!");
// Записываем подготовленное сообщение, устанавливаем флаг успешного завершения!!!!
    LogHelper.Post(lltInfo, __LINE__, true);
}

В результате получаем следующий лог:
Код

Begin
  Connect...                                Address: address|Login: log|Password: pass123
  ...Test...
  ...Ok!


Если последний вызов
    LogHelper.Post(lltInfo, __LINE__, true);
заменить на обычный
    LogHelper.Post(lltInfo, __LINE__);
получим:
[/code]
В результате получаем следующий лог:
Код

Begin
  Connect...                                Address: address|Login: log|Password: pass123
  ...Test...
  ...return (stack unwinding)

Последняя строка сформируется автоматически.

При использовании макросов можно будет формировать как однострочные записи в журнал, так и многострочные с форматированием.
Единственное неудобство будет состоять в том, что надо будет отслеживать использование различных макросов для первой строки (где нужно объявление) и для остальных. Пока не знаю как это обойти. Есть идеи?
PM MAIL   Вверх
_Tanatos_
Дата 29.11.2007, 16:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(Lazin @  29.11.2007,  15:48 Найти цитируемый пост)
Я не очень понимаю что нужно получить в итоге, наверное лучше сначала составить какой нибудь список требований к библиотеке т.е. что нам от нее надо.... 


Эта ветка для этого и создана smile.
Конечно в идеале сначала составить что-то вроде ТЗ, а потом уже писать.
Но! я давненько не программировал, так что пользуясь случаем восстанавливаю навыки.

Цитата(Lazin @  29.11.2007,  15:48 Найти цитируемый пост)
использование элипсисов я считаю тупиковым, так как в данном случае теряется гибкость и расширяемость, если я сделаю свой класс, как мне расширить класс, что-бы он выводил в лог объекты моего класса. 

С таким аргументом не поспоришь smile
Да и возможность записать все в одну строку прильщает.
Непонятно только как добавлять вспомогательную информацию и  как разделять сообщение и данные?

Склоняюсь к использованию дополнительного класса CtLibLogHelper для управления вложенностью и запретить прямой доступ извне. Это позволит гарантировать, что иерархия будет корректной всегда. Иначе можно сделать 10 увеличений вложенности а потом по исключению (или недосмотру) словить неправильную иерархию. А сам класс CtLibLogEngine тогда будет использоваться для записи сообщений в текущий уровень.

Мои требования к логгеру:
- построение древовидного лога (с гарантией корректности структуры)
- создание структурированных записей
- наличие вспомогательной информации  (тип, файл, строка, модуль, процесс, поток ...)
- возможность управлять фильтрованием записей в режиме исполнения (например включать/отключать трассировку по горячей клавише)
- возможность регистрировать несколько подписчиков на сообщения (один сохраняет в файл, другой выводи на консоль и т.д.
- желательно возможность формирования простых записей в одну строчку
- еще высказывали пожелание отключать ведение лога при чистовой компиляции (мне бы хотелось ограничивать ведение лога определенным уровнем и физически вычищать из конечного кода только сообщения уровнем ниже заданного)
- поддержка многопоточных приложений

Какие еще есть пожелания?

Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 16:46
PM MAIL   Вверх
JackYF
Дата 29.11.2007, 16:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 18
Всего: 162



Не насилуйте va_args, юзайте boost::format...


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
Lazin
Дата 29.11.2007, 16:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



как вариант, определить оператор << в классе LogHelper, будет более логично...
тем более LogHelper отвечает за контроль иерархии записей
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 29.11.2007, 16:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



А как такой вариант?
Код

LogHelper.Log(Type, Error, __LINE__) << "Message with error" << loData << "Data" << loPost;
LogHelper.Log(Type, __LINE__) << "Message with complete" << loData << "Data" << loComplete;

Опробовал вполне работоспособный вариант.

С макросом вообще сказка получается:
Код

LOG_Info << "Message" << loData << "Data" << loPost;
LOG_Error(12)  << "Message with error" << loData << "Data" << loPost;
LOG_Complete << "Message with complete" << loData << "Data" << loComplete;


Остается правда вопрос! Как с помощью макроса при финальной компиляции исключить из кода запись в журнал?

Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 21:07
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.2670 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.