LINUX.ORG.RU

Обработка ошибок и логирование


0

1

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

Что можно посмотреть как пример правильной реализации?

★★★★★

Ответ на: комментарий от pathfinder

Я хочу реализацию найти хорошую, а не лучшую.

Zodd ★★★★★
() автор топика

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

напиши более предметно вопрос.

Deleted
()

Для крестов есть немного монструозное, но не плохое решение.
http://boost-log.sourceforge.net/libs/log/doc/html/index.html
Если обернуть его самостоятельно и уменьшить общность, то можно использовать.

А обработка ошибок в плюсах частенько осуществляется в два присеста:
1) Выброс эксепшена при помощи макроса
2) запись в лог при помощи лога

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

Больше нигде обработки ошибок не видел.

Remington
()

Я у себя в программах делаю глобальный try/cath который перехватывает сообщения об ошибках, которые не должны быть отработаны. Сделано это через макросы и выглядит как-то так:

int main()
{
try_begin()

my_entry_point();

try_end()
}
Есть макрос-заглушка try_call, которая используются при вызове функций, возвращающих -1, если произошла ошибка. Этот макрос в случае ошибки кидает исключение с текстовым сообщением, расшифровывающим errno. Пример заглушки для вызова open():

int fd=0;
try_call( fd=open("path_to_file",O_RDWR) ,"my_func()/open()");

В случае ошибки try_call выкинет исключение, обработчик исключения выведет сообщение на экран (и/или в лог) и завершит приложение. Сообщение выглядит как-то так:

./src/my_func.cpp:144:my_func()/open() - File not found
И такими заглушками покрывается вызов всех функций, коды возврата которых не проверяются в явном виде.

Лог сделан в виде велосипеда внутри try_end(). Не уверен, что это правильный путь. Исключения нигде больше не применяются, кроме как для сообщения о необработанной ошибке.

pathfinder ★★★★
()
Ответ на: комментарий от val-amart

ну хоть бы язык указал.

Плюсы

Zodd ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Например, на уровне одной процедуры:

int f(int n,double &a) {
  if (n==0)return -1;
  a/=n;
  return 0;
}

А вот когда в целой программе (как не запутаться)?
В моей программе может произойти неправильные расчеты в очень многих процедурах из-за неверных входных данных или чего-нибудь другого.

Zodd ★★★★★
() автор топика
Ответ на: комментарий от pathfinder

try_call ...

Вот что-то похожее и ищу. Это общепринятая реализация или как? Если да, можно на man ссылку?

Zodd ★★★★★
() автор топика
Ответ на: комментарий от Zodd

>Это общепринятая реализация или как?

Думаю, что нет. Это я сам придумал, а не из книжек вычитал. Мне такой подход нравится, но другим, если они будут читать мой код, понадобятся дополнительные разъяснения. Так же не гарантируется полная универсальность такого подхода, не исключено, что где-то этот подход будет неудобным.

pathfinder ★★★★
()
Ответ на: комментарий от Deleted

ИМХО не надо злоупотреблять исключениями. Я кидаю исключения только перед экстренным завершением программы.

pathfinder ★★★★
()
Ответ на: комментарий от Deleted

Не всегда неверный результат есть исключение. // Ушел читать статью

Zodd ★★★★★
() автор топика

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

Yareg ★★★
()
Ответ на: комментарий от Zodd

>В моей программе может произойти неправильные расчеты в очень многих процедурах из-за неверных входных данных или чего-нибудь другого.

так обрабатывай. лично я сторонник C стиля - возвращать код ошибки, как это у тебя и сделано с return -1. так даже на жабе можно легко и успешно писать, а уж на C++ тем более)))

quest ★★★★
()
Ответ на: комментарий от pathfinder

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

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

б) их медлительность чувствуется только при количестве «многатыщ или мульенов»

вот пример

$ python -m 'timeit' -s 'd=dict(zip(range(1000), range(1000)))' $'1999 in d and d[1999]'10000000 loops, best of 3: 0.0523 usec per loop

$ python -m 'timeit' -s 'd=dict(zip(range(1000), range(1000)))' $'try: d[1999]\nexcept KeyError: pass' 1000000 loops, best of 3: 0.854 usec per loop

в остальном... кто-нибудь, приведите хотя бы одну причину, которая бы четким образом обозначала отрицательное влияние исключения, м?

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

Deleted
()
Ответ на: комментарий от Zodd

> Вот что-то похожее и ищу. Это общепринятая реализация или как? Если да, можно на man ссылку?

Это NEVER DO SO реализация. Не надо повторять такого!

hydraulicbrakefluid
()
Ответ на: комментарий от Deleted

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

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

А в плюсах для некоторых может быть полной неожиданностью прерывание выполнения функции. Ещё там нет блока finally. Исключения вынуждают в каждом месте, где есть выделение/освобождение ресурсов делать блок try/catch с освобождением ресурсов и последующим перевызовом исключения, даже если тебе реакция на исключение в данном месте не нужна.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

поймал себя на мысли, что предрасположенность к использованию исключений ни что иное как латентная goto-филия :)

Deleted
()
Ответ на: комментарий от Boy_from_Jungle

>это возврат системных вызовов.
и что? таким макаром обернуть можно что угодно даже Qt объекты с асинхронными сигналами.

quest ★★★★
()
Ответ на: комментарий от pathfinder

Вы во первых использовали макросы, что само по себе - смертный грех в C++, за который я бы расстреливал, во вторых изначально закладываете архитектуру «где-то внутри бросилось, и ** с ним, - снаружи поймают и помрут», вместо того, что-бы обрабатывать исключения по месту их получения, и соотвественно adjust-ить поведение. Для мелких утилиток - такой метод мож и прокатит, но не в большом проекте.

hydraulicbrakefluid
()
Ответ на: комментарий от hydraulicbrakefluid

>Вы во первых использовали макросы, что само по себе - смертный грех

Вы максималист. Я не спорю, что макросы могут сильно навредить. Но не надо же всё возводить в абсолют. Как показывает практика: любая мысль возведенная в абсолют - уродлива.

во вторых изначально закладываете архитектуру «где-то внутри бросилось, и ** с ним, - снаружи поймают и помрут»

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

Для мелких утилиток - такой метод мож и прокатит, но не в большом проекте.

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

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Если бы ты хотя бы представлял, какое в том же CL ООП, ты бы такой херни не сморозил, даже паясничая как малолетний дебил.

Love5an
()
Ответ на: комментарий от Boy_from_Jungle

Я так понимаю, ты?
Ими вообще невозможно пользоваться, чего только стоит их «совместимость» с деструкторами.

Love5an
()
Ответ на: комментарий от pathfinder

> Вы максималист. Я не спорю, что макросы могут сильно навредить. Но не надо же всё возводить в абсолют. Как показывает практика: любая мысль возведенная в абсолют - уродлива.

Имхо уродливо выглядит связка try_begin() / try_end(), вместо того, что-бы явно написать то, что требуется синтаксисом языка.

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

Не знаю, что вы писали, в моем случае, редко бывают исключения, после которых приложение нужно шатдаунить, — обычно есть обходные пути, как сделать то-же самое, или там по тайм-ауту, итп. Хотя если речь идет не о сервисе, а о one-shot приложении, которое либо сделало что-то, либо нет, то возможно там так и будет проще, как вы делаете.

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

Для разнообразия — попробуйте так не делать ;) Попробовать другой подход.

hydraulicbrakefluid
()

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

SBCL, например, легко отлавливает сегфолты даже в стороннем, скажем, сишном, коде. Вот пример. Для винды, правда:

//gcc -shared -o deref.dll deref.c
int deref(int* pointer)
{
    return *pointer;
}

(cffi:load-foreign-library "deref.dll")

(cffi:defcfun ("deref" deref-pointer)
    :int
  (ptr :pointer))

(defvar *pointer* (cffi:foreign-alloc :int :initial-element 123))

(deref-pointer *pointer*)
;; ==> 123
(handler-case
    (deref-pointer (cffi:make-pointer 0))
  (error (e)
    (format t "An error occurred:~%~a" e)
    0))
;; на stdout ==> An error occurred:
;;               EXCEPTION_ACCESS_VIOLATION
;;==> 0
Love5an
()
Ответ на: комментарий от pathfinder

>>Это NEVER DO SO реализация. Не надо повторять такого!

ЩИТО?

А оно и не надо. Я тебе щас проще объясню.[br] Представль, что ты пишешь модуль ядра. У тебя ошыбка. Что ядро будет делать с твоим «file not found»?

anonymous
()
Ответ на: комментарий от hydraulicbrakefluid

>Имхо уродливо выглядит связка try_begin() / try_end(), вместо того, что-бы явно написать то, что требуется синтаксисом языка.

Зато один раз написал и используешь в нескольких приложениях. Не, можно каждый раз писать полный try/catch, я не спорю.

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

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

Хотя если речь идет не о сервисе, а о one-shot приложении, которое либо сделало что-то, либо нет, то возможно там так и будет проще, как вы делаете.

В силу определенных причин мне приходится писать именно «сервисы», а не one-shot приложения. И весь мой набор софта, в конечном счете, после отладки, должен работать автономно 24/7 на контроллере, куда никто внутрь не заглянет

pathfinder ★★★★
()
Ответ на: комментарий от anonymous

>А оно и не надо. Я тебе щас проще объясню.[br] Представль, что ты пишешь модуль ядра. У тебя ошыбка. Что ядро будет делать с твоим «file not found»?

Ядро не занимается исправлением ошибок в софте. Этим занимается программист.

//Ваш К. О.

pathfinder ★★★★
()
Ответ на: комментарий от hydraulicbrakefluid

>в тех местах где нет ручной проверки возврата функции

Ты не прав. Не должно быть отсутствия проверок. Все коды ошибок должны проверятся. Но это не обязательно должно писаться каждый раз вручную.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

>Ядро не занимается исправлением ошибок в софте. Этим занимается программист.

Ты не правильно понял.[br] Делаешь ты kmalloc какой-нибудь в module_init, а памяти нет. Твой код с try_start/try_end чего сделает?

anonymous
()
Ответ на: комментарий от anonymous

>Ты не правильно понял.[br] Делаешь ты kmalloc какой-нибудь в module_init, а памяти нет. Твой код с try_start/try_end чего сделает?

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

pathfinder ★★★★
()
Ответ на: комментарий от jtootf

>>Я у себя в программах делаю глобальный try/cath

метод покемона. you just got to catch them all!

Раз уж начал, то дай развернутый ответ - почему это плохо. Заодно я пойму, что именно этим ты хотел сказать.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

> Не хочу тебя расстраивать, но на С++ модули для ядра не пишут

4.2

ладно, спишу на неосведомленность.

val-amart ★★★★★
()
Ответ на: комментарий от pathfinder

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

должен работать автономно 24/7 на контроллере, куда никто внутрь не заглянет


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

val-amart ★★★★★
()
Ответ на: комментарий от pathfinder

Раз уж начал, то дай развернутый ответ - почему это плохо

антипаттерн такой есть. что касается меня, то я считаю что исключения должны обрабатываться там, где для их обработки есть контекст; ловить всё подряд (независимо от уровня вложенности) означает нивелировать саму идею исключений

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

Внезапно !

>я считаю что исключения должны обрабатываться там, где для их обработки есть контекст

В данном случае исключения ловятся именно там, где для их обработки есть контекст. Как бы по задумке все эти исключения кидаются лишь для того, чтобы они были обработаны глобальном блоке try/catсh. Если эти исключения будут ловится где-то ещё, то это будет не очень хорошо.

pathfinder ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.