Перефразируя Сесила Б. де Милля: « Чтобы сделать пост в блоге, нужно начать с трассировки стека и довести до кульминации » — вот оно:
Детали пока не важны, но в режиме просмотра 100 футов вы можете увидеть трассировку длинного стека с несколькими исключениями, оборачивающими друг друга ( вызывая ). Мы вернемся к этой трассировке стека, но сначала некоторые основы. Если вы сгенерируете исключение, оно будет записано таким образом, чтобы показать, как выглядел стек в тот момент, когда возникло исключение. В самом низу вы увидите статические main () или Thread.run (), обработанные методами, вызываемыми до первой строки трассировки стека, указывающей место, где было выдано действительное исключение. Это очень удобно, так как вы можете увидеть весь поток управления, который привел к исключению:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class BookController { private final BookService bookService = new BookService(); public void alpha() { beta(); } private void beta() { gamma(); } private void gamma() { bookService.delta(); } public static void main(String[] args) { new BookController().alpha(); } } class BookService { private final BookDao bookDao = new BookDao(); public void delta() { epsilon(); } private void epsilon() { zeta(); } private void zeta() { bookDao.eta(); } } class BookDao { public void eta() { theta(); } private void theta() { iota(); } public void iota() { throw new RuntimeException( "Omega server not available" ); } } |
Если вы не знаете греческий алфавит , вы можете начать обучение по трассировке стека (помните, что поток управления начинается снизу и идет вверх):
01
02
03
04
05
06
07
08
09
10
11
|
java.lang.RuntimeException: Omega server not available at BookDao.iota(BookController.java:50) at BookDao.theta(BookController.java:48) at BookDao.eta(BookController.java:46) at BookService.zeta(BookController.java:41) at BookService.epsilon(BookController.java:39) at BookService.delta(BookController.java:37) at BookController.gamma(BookController.java:22) at BookController.beta(BookController.java:20) at BookController.alpha(BookController.java:18) at BookController.main(BookController.java:26) |
Замечательно, правда? При чтении сверху вниз вы можете сказать: iota () была вызвана theta () была вызвана eta ()… Ясно и просто. Однако что, если кто-то решит обернуть оригинальное исключение и повторно его выбросить?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
public class BookController { private static final Logger log = LoggerFactory.getLogger(BookController. class ); private final BookService bookService = new BookService(); public void alpha() { beta(); } private void beta() { gamma(); } private void gamma() { try { bookService.delta(); } catch (Exception e) { throw new RuntimeException( "Sorry, try again later" , e); } } public static void main(String[] args) { try { new BookController().alpha(); } catch (Exception e) { log.error( "" , e); } } } class BookService { private final BookDao bookDao = new BookDao(); public void delta() { epsilon(); } private void epsilon() { zeta(); } private void zeta() { try { bookDao.eta(); } catch (Exception e) { throw new RuntimeException( "Unable to save order" , e); } } } class BookDao { public void eta() { theta(); } private void theta() { iota(); } public void iota() { try { throw new RuntimeException( "Omega server not available" ); } catch (Exception e) { throw new RuntimeException( "Database problem" , e); } } } |
Теперь быстро: найдите основную причину в трассировке стека!
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
java.lang.RuntimeException: Sorry, try again later at BookController.gamma(BookController.java:26) at BookController.beta(BookController.java:20) at BookController.alpha(BookController.java:18) at BookController.main(BookController.java:32) Caused by: java.lang.RuntimeException: Unable to save order at BookService.zeta(BookController.java:51) at BookService.epsilon(BookController.java:45) at BookService.delta(BookController.java:43) at BookController.gamma(BookController.java:24) ... 8 common frames omitted Caused by: java.lang.RuntimeException: Database problem at BookDao.iota(BookController.java:66) at BookDao.theta(BookController.java:60) at BookDao.eta(BookController.java:58) at BookService.zeta(BookController.java:49) ... 11 common frames omitted Caused by: java.lang.RuntimeException: Omega server not available at BookDao.iota(BookController.java:64) ... 14 common frames omitted |
Оказывается, что main () больше не последняя строка. Хуже того, все кажется искаженным, попробуйте снова прочитать греческий алфавит … Теперь давайте вернемся к нашей первоначальной трассировке стека. Это происходит из-за сбоя запуска Spring Framework, представьте, что это может быть несколько страниц.
Для вашего удобства я добавил стрелки, чтобы обозначить путь, по которому вы должны следовать, чтобы восстановить поток управления: начиная с хвоста красной стрелки ( Thread.run () ) где-то посередине вы поднимаетесь, а затем… прыгаете к хвосту оранжевой стрелки , С головы оранжевой стрелки ты прыгаешь на хвост зеленой и так далее … Не очень интуитивно понятно, тебе не кажется? И что показывает этот красный эллипс? Да, это основная причина сбоя (самое внутреннее исключение). С другой стороны, исключение, напечатанное в самом начале (то, которое вы обычно читаете на первом месте), что-то говорит об ошибке при создании DefaultAnnotationHandlerMapping # 0 (что?) Истинная ошибка ( Нет подходящего компонента типа… ) скрыто скрыта…
Так что же это за новая функция ? Опять наш простой пример. После обновления до 0.9.30 просто добавьте % rEx (или эквивалентное % rootException ) в конце вашего шаблона регистрации:
1
2
3
4
5
|
< appender name = "STDOUT" class = "ch.qos.logback.core.ConsoleAppender" > < encoder > < pattern >%d{ISO8601} | %-5level | %thread | %logger{1} | %m%n%rEx</ pattern > </ encoder > </ appender > |
Это заменит стандартную маршрутизацию печати трассировки стека на ту, которую я смиренно внес. Теперь та же греческая программа напечатает:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
java.lang.RuntimeException: Omega server not available at BookDao.iota(BookController.java:64) Wrapped by: java.lang.RuntimeException: Database problem at BookDao.iota(BookController.java:66) at BookDao.theta(BookController.java:60) at BookDao.eta(BookController.java:58) at BookService.zeta(BookController.java:49) Wrapped by: java.lang.RuntimeException: Unable to save order at BookService.zeta(BookController.java:51) at BookService.epsilon(BookController.java:45) at BookService.delta(BookController.java:43) at BookController.gamma(BookController.java:24) Wrapped by: java.lang.RuntimeException: Sorry, try again later at BookController.gamma(BookController.java:26) at BookController.beta(BookController.java:20) at BookController.alpha(BookController.java:18) at BookController.main(BookController.java:32) |
Пожалуйста, сравните это внимательно с предыдущим выводом. Прежде всего, самая первая строка указывает на проблему. Не копайтесь в исключениях « вызвано », большую часть времени вы, вероятно, пропустите остальные. Во-вторых, поток управления непрерывен и непрерывен — вы все равно можете читать его сверху вниз или наоборот. И последнее по порядку, но не по значению — тот факт, что исключения были перенесены в это время, сохраняется, но не искажает трассировку стека.
Теперь вы заслуживаете того, чтобы исходное исключение Spring использовало преимущества печати% rEx:
Наблюдения точно такие же: первопричина проблемы возникает в самом начале, сокращая время, необходимое для исследования проблемы. Также при анализе потока управления нет скачков — Thread.main () находится внизу, и вы можете непрерывно читать трассировку снизу вверх.
Если вы много «работаете» со следами стека (либо в разработке, либо в производстве / поддержке) — подумайте о переходе на первопричину в первую очередь. Это сэкономит вам несколько секунд каждый раз, когда вы анализируете конкретное исключение. Но я также заметил, что неопытные разработчики иногда даже не знают о правиле « Причины »: найдите первое исключение и посмотрите на последнее «Причинено» — оставаясь не понимающими, в чем заключается проблема, глядя только на самую внешнюю, наименее конкретную, наиболее общую ошибку. Это также поможет им.
Кстати — всей этой суеты можно избежать, если вы вообще избежите оборачивания и повторного выброса исключения. Я знаю, слишком часто мы вынуждены делать это по проверенным исключениям …
Ссылки: Первичная причина регистрации исключений — сначала от нашего партнера по JCG Томека Нуркевича в NoBlogDefFound
Удачного кодирования! Не забудь поделиться!
Статьи по Теме:
- Java Logging Mess
- 10 советов для правильной регистрации приложений
- Практическое введение в внедрение кода с AspectJ, Javassist и Java Proxy
- Серия Java Best Practices
- Вещи, которые должен знать каждый программист
- 9 советов по выживанию в процессе развития Дикого Запада
- Законы разработки программного обеспечения