Sentry — трекинг java exception в Java


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


Sentry — инструмент мониторинга исключений (exception), ошибок в ваших приложениях.


Преимущества использования Sentry:


  • не нервничать при размещении приложений на боевом сервере,
  • быстро находить причины возникших проблем,
  • устранять баги раньше, чем о них вам сообщат тестировщики, коллеги из саппорта, пользователи, ПМ или директор,
  • выявлять незаметные остальной команде проблемы, которые портят жизнь пользователям и снижают эффективность вашего продукта,
  • бесплатен,
  • легко интегрируется в проект,
  • ловит ошибки и в браузере пользователя, и на вашем сервере.
  • Если в ELK один и тот же exception происходит несколько раз, то они идут как отдельные записи, занимают место на диске и в ОЗУ. Если в Sentry один и тот же exception происходит несколько раз, то поле EVENTS увеличивается, тем самым экономя место на диске и в ОЗУ.

    Пример


Основные возможности:


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

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


Устанавливаем Sentry


  • через скрипт, который в docker-compose поднимет все компоненты (скрипт и репо находятся здесь https://github.com/getsentry/onpremise/)
  • собираем RPM пакеты все зависимостей Sentry и устанавливаем через RPM пакеты https://habr.com/ru/post/500632/

После установки Sentry у вас должен быть DSN



Здесь будет обзор основных примеров отправки java exception в Sentry. Примеры будем брать из официального репозитория https://github.com/getsentry/examples/tree/master/java.


Зависимости


На машине где будете запускать эти примеры необходимо иметь установленными JDK и Maven.


# Пример для CentOS
wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
yum install -y apache-maven java-1.8.0-openjdk-devel git

Скачиваем репозиторий


git clone https://github.com/getsentry/examples.git
cd examples/java/

Sentry Basic Example


Переходим к https://github.com/getsentry/examples/tree/master/java/basic


cd basic

Запускаем компилирование проекта


mvn compile exec:java

Запускаем Java приложение и передаем ему SENTRY_DSN


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 mvn exec:java

Как выглядят exception в Sentry








Запустим приложение несколько раз:


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 mvn exec:java

Видно что счетчик EVENTS для этого конкретного exception увеличился. Скриншот в начале поста.


Sentry Grails Example


Переходим к https://github.com/getsentry/examples/tree/master/java/grails-3.x


Устанавливаем зависимости для SDK


yum install -y unzip zip

Устанавливаем grails используя SDK https://grails.org/download.html


curl -s https://get.sdkman.io | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install grails

Запускаем grails c SENTRY_DSN


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 grails run-app

Открываем в браузере http://localhost:8080/hello/index и exception отправляются в Sentry


Как выглядят exception в Sentry












Запустим приложение несколько раз:


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 grails run-app

Видно что счетчик EVENTS для этого конкретного exception увеличился.


Sentry java.util.logging Example


Переходим к https://github.com/getsentry/examples/tree/master/java/java.util.logging


Собираем пакет JAR


mvn clean package

Запускаем JAR


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 java -Djava.util.logging.config.file=src/main/resources/logging.properties -cp ./target/sentry-java-jul-example-1.0-SNAPSHOT-jar-with-dependencies.jar io.sentry.example.Application

Как выглядят exception в Sentry







Sentry Log4j 1.x Example


Переходим к https://github.com/getsentry/examples/tree/master/java/log4j-1.x


Компилируем


mvn compile exec:java

Запускаем


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 mvn exec:java

Как выглядят exception в Sentry







Sentry Log4j 2.x Example


Переходим к https://github.com/getsentry/examples/tree/master/java/log4j-2.x


Компилируем


mvn compile exec:java

Запускаем


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 mvn exec:java

Как выглядят exception в Sentry







Sentry Logback Example


Переходим к https://github.com/getsentry/examples/tree/master/java/logback


Компилируем


mvn compile exec:java

Запускаем


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 mvn exec:java

Как выглядят exception в Sentry







Sentry Spring Boot Example


Переходим к https://github.com/getsentry/examples/tree/master/java/spring-boot


Запускаем


SENTRY_DSN=http://31bc93d7d64e4fd6b2e77d6d7780be6c@172.26.10.64:9000/1 mvn spring-boot:run

Открываем в браузере http://localhost:8080/ и exception отправляются в Sentry


В логах мы видим
2020-06-20 12:35:47.249 ERROR 13939 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
    at io.sentry.example.Application.home(Application.java:44) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_252]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_252]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_252]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_252]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:798) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_252]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_252]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_252]

В http://localhost:8080/ мы видим:



Как выглядят exception в Sentry








Телеграм чат по Sentry https://t.me/sentry_ru