Почему это исключение.printStackTrace () считается плохой практикой?



есть много на материал из здесь что предполагает, что печать трассировки стека исключения является плохой практикой.



например, из RegexpSingleline check in Checkstyle:




эта проверка может быть использована [...] найдите распространена порочная практика, как звонить бывшему.printStacktrace ()




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




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


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


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


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



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

623   9  

9 ответов:

Throwable.printStackTrace() записывает трассировку стека в System.err PrintStream. Элемент System.err поток и базовый стандартный поток вывода "ошибка" процесса JVM могут быть перенаправлены с помощью

  • вызов System.setErr() что меняет пункт назначения, на который указывает System.err.
  • или путем перенаправления потока вывода ошибок процесса. Поток вывода ошибок может быть перенаправлен в файл / устройство
    • содержание которого может быть проигнорировано персонал,
    • файл / устройство может быть не способен к вращению журнала, делая вывод, что перезапуск процесса требуется, чтобы закрыть дескриптор открытого файла/устройства, прежде чем архивировать существующее содержимое файла/устройства.
    • или файл/устройство фактически отбрасывает все данные, записанные в нее, как в случае /dev/null.

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

  • если у вас нет System.err переназначается на протяжении всего срока службы приложения,
  • и если вам не требуется ротация журнала во время работы приложения,
  • и если принято/разработана практика ведения журнала приложение написать System.err (и выходной поток стандартной ошибки JVM).

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

наконец, следует помнить, что выход Throwable.printStackTrace() определенно будет чередоваться с другим контентом, написанным на System.err (и, возможно, даже System.out если оба будете перенаправлены к тому же файлу/устройству). Это раздражение (для однопоточных приложений), с которым нужно иметь дело, поскольку данные вокруг исключений нелегко анализировать в таком случае. Хуже того, весьма вероятно, что многопоточное приложение будет создавать очень запутанные журналы как Throwable.printStackTrace()не является потокобезопасным.

нет механизма синхронизации для синхронизации записи трассировки стека в System.err когда несколько потоков вызова Throwable.printStackTrace() в то же время. Решения это фактически требует, чтобы ваш код синхронизировался на мониторе, связанном с System.err (а также System.out, если конечный файл / устройство одинаковы), и это довольно высокая цена, чтобы заплатить за здравомыслие файла журнала. Чтобы взять пример,ConsoleHandler и StreamHandler классы отвечают за добавление записей журнала в консоль, в объекте ведения журнала, предоставленном java.util.logging; фактическая операция публикации записей журнала синхронизируется - каждый поток, который пытается опубликовать запись журнала, также должен приобретите блокировку на мониторе, связанном с StreamHandler экземпляра. Если вы хотите иметь такую же гарантию наличия не перемежающихся записей журнала с помощью System.out/System.err, вы должны гарантировать то же самое - сообщения публикуются в эти потоки сериализуемым образом.

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


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

вы касаетесь нескольких вопросов здесь:

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

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

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

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

создание трассировки стека происходит, когда исключение будучи созданным / брошенным (вот почему бросание исключения идет с ценой), печать не так уж и дорога. На самом деле вы можете переопределить Throwable#fillInStackTrace() в вашем пользовательском исключении эффективно делает выбрасывание исключения почти таким же дешевым, как простой оператор GOTO.

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

очень хороший момент. Основная проблема здесь: если фреймворк регистрирует исключение для вы ничего не делаете (но убедитесь, что это так!) Если вы хотите зарегистрировать исключение самостоятельно, используйте logging framework, например Logback или настройки log4j, чтобы не поставить их на raw консоли, потому что это очень трудно контролировать его.

С помощью logging framework вы можете легко перенаправить трассировки стека в файл, консоль или даже отправить их на указанный адрес электронной почты. С жестко закодированным printStackTrace() вы должны жить с sysout.

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

снова: log SQLException правильно (с полной трассировкой стека, используя структуру ведения журнала) и показать красиво:"к сожалению, настоящее время мы не можем обработать ваш запрос" сообщение. Вы действительно думаете, что пользователь заинтересован в причинах? Вы видели экран ошибки StackOverflow? Это очень смешно, но не раскрывает любой подробности. Однако это гарантирует пользователю, что проблема будет изучена.

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


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

первым делом printStackTrace () не дорого, как вы заявляете, потому что трассировка стека заполняется, когда исключение создается само.

идея состоит в том, чтобы передать все, что идет в журналы через структуру logger, так что ведение журнала можно контролировать. Поэтому вместо использования printStackTrace, просто используйте что-то вроде Logger.log(msg, exception);

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

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

пример

рассмотрим следующий пример:

try {
  initializeState();

} catch (TheSkyIsFallingEndOfTheWorldException e) {
  e.printStackTrace();
}

continueProcessingAssumingThatTheStateIsCorrect();

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

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

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

почему это стало проблемой?

вероятно, одна из самых больших причин того, что плохая обработка исключений стала более распространенной, связана с тем, как IDE, такие как Eclipse, будут автоматически генерировать код, который будет выполнять e.printStackTrace для обработки исключений:

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

(выше является фактическим try-catch автоматически генерируется Eclipse для обработки InterruptedException выдано Thread.sleep.)

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

Я думаю, что ваш список причин является довольно всеобъемлющим.

один особенно плохой пример, с которым я сталкивался не раз, выглядит так:

    try {
      // do stuff
    } catch (Exception e) {
        e.printStackTrace(); // and swallow the exception
    }

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

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

и, наконец, на более легкой ноте, Божье совершенное исключение.

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

в серверных приложениях stacktrace взрывает ваш файл stdout/stderr. Он может становиться все больше и больше и заполняется бесполезными данными, потому что обычно у вас нет контекста и нет метки времени и так далее.

например, Каталина.при использовании Tomcat в качестве контейнера

Это не плохая практика, потому что что-то "неправильно" в PrintStackTrace(), но потому что это "запах кода". В большинстве случаев вызов PrintStackTrace() происходит потому, что кто-то не смог правильно обработать исключение. Как только вы справляетесь с исключением надлежащим образом, вы обычно больше не заботитесь о StackTrace.

кроме того, отображение stacktrace на stderr обычно полезно только при отладке, а не в производстве, потому что очень часто stderr идет нигде. Ведение журнала имеет больше смысла. Но просто заменив PrintStackTrace () с протоколированием исключение по-прежнему оставляет вас с приложением, которое не удалось, но продолжает работать, как ничего не произошло.

как некоторые ребята тут уже писали проблема с исключением глотания в случае, если вы просто позвоните e.printStackTrace() на catch блок. Он не остановит выполнение потока и будет продолжаться после блока try, как в обычном состоянии.

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

Comments

    Ничего не найдено.