Capturing Java Exceptions with Sentry
Getting started with Java exception handling can be an intimidating
prospect. The large number of dependency managers, logging frameworks,
configuration methods, and programming languages that run on the JVM can create
a dizzying number of options for a programmer to choose between, so we’ve put
this guide together to help you navigate the world of modern Java error tracking
with Sentry.
Choosing a Java Integration
The Sentry Java SDK (sentry-java
) is hosted at Maven Central, a repository
that hosts many widely distributed Java packages (JARs) and works with many of
the popular JVM dependency managers, including Maven. A search for Sentry on
Maven Central yields several results for different distributions of the
project, and in this post we’ll help you figure out which is the right one to
use with your application.
The major components of the SDK can be divided into two categories:
Logging integrations, commonly referred to as appenders, used to
translate log records generated by your application via a logging framework
into Sentry events. These integrations are packaged assentry-logback
,sentry-log4j
, andsentry-log4j2
.Sentry, used by the logging integrations to send events to a Sentry server.
This is packaged assentry
and also includes the logging handler forjava.util.logging
.
For most applications, we recommend using one of the logging integrations to
communicate with a Sentry server, and we’ll be mainly focusing on those in
this post. For existing applications that are already using a logging
framework, integrating with Sentry is often as simple as adding or changing a
few lines in your logging configuration files. We recommend using the logging
integrations over interfacing with Sentry directly for several reasons:
The logging APIs are easy to understand. The patterns and vocabulary used
by logging APIs are familiar to many developers, even across different
platforms.Producing messages via the logging frameworks reduce the amount of
boilerplate code. For example, compare the logback appender event building
method with a call to thelogger.error
method.Using a logging framework provides greater control over reporting
configuration. It is easy to replace reporting to Sentry in your
development environment with console logging by using a different logging
configuration file. Contrast this with having to wrap all calls in
conditionals or mocking out the Sentry interface with a fake instance.Many other libraries also utilize logging frameworks. Using the logging
framework enables collecting better data from other libraries in your
application that also utilize the logging system, such as database drivers or
a web framework.
The Java Logging Ecosystem
If you’re new to Java and have a background in a platform such as Python with
a de facto logging solution, one of the first things that
you will notice is the large number of logging frameworks that are available
and remain used today. To understand why so many different frameworks exist,
we’ll start with a quick history lesson.
Log4j was introduced in 1999 and was one of the first widely
adopted logging frameworks. A few years later in 2002, Sun introduced an
alternative framework, java.util.logging (commonly referred to by the acronym
“JUL”), which is distributed with the Java platform. In reaction, the Apache
Commons Logging project was created with the intention of providing a unified
logging API that could be used to interact with whichever backing logging
implementation that the user preferred, including Log4j and JUL (among others.)
However, yet another alternative “unified” logging API emerged in
2005 from the author of Log4j — SLF4J, the “Simple Logging Facade for Java.”
Later, the author of Log4j and SLF4J released logback, which provided a
native implementation of the SLF4J interface. Around the same time, development
also began on the (now stable) Log4j 2 — without the author of the first
version. Simple, right?
There was a lot of code that was written at different points in time during
this history and like so much software there was — and still is — lot of
debates on which framework is the best choice. Since many of these projects are
still in use today, Sentry provides integrations for several of the most widely
adopted frameworks including JUL, logback,
Log4j, and Log4j 2. Getting started is typically
a matter of configuration, and the documentation for each integration contains
instructions on how to include the integration as a dependency as well as a
sample configuration to help you get started.
Choosing the Right Integration for Your Projects
When choosing which logging framework to use for your own projects, that
decision is often mandated to you by your choice of framework. For projects
that don’t depend on a framework, targeting the SLF4J interface is often
recommended due to its ubiquity and flexibility. The backing logging
implementation is largely a matter of developer preference, with logback and
Log4j 2 being common choices.
Starting from Scratch
For example, it only takes a few moments to start reporting to Sentry with
SLF4J and logback.
Add sentry-logback to your project dependencies. If you’re using Maven,
dependencies are declared inpom.xml
, and you can see an example in our
logback example project. If you’re not using Maven,
the project details on Maven Central have dependency information for a variety
of different package managers.<dependency> <groupId>io.sentry</groupId> <artifactId>sentry-logback</artifactId> <version>1.2.0</version> </dependency>
Add the Sentry appender to your
logback.xml
configuration file. You can
set the Sentry DSN for your project via JDNI, as theSENTRY_DSN
environment
variable orsentry.dsn
system property.<appender name="Sentry" class="io.sentry.logback.SentryAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>WARN</level> </filter> </appender> <root level="INFO"> <appender-ref ref="Sentry"/> </root>
Add logging calls to your application. It is usually most effective to
addlogging.error
calls as far towards the top of the stack in your
application as possible — good [examples are amain
method in a console
application] _logback-example/app, or request handler in a web application —
to capture as many exceptions as possible. It can also be helpful to log errors
as a warning or lower level in othertry
/catch
blocks where checked
exceptions are captured.public static void main(String[] args) { try { application.run() } catch (Exception e) { logger.error("Caught exception!", e); } }
That’s it! Your project should now be reporting Java errors to Sentry. Tell your
boss that you deserve a promotion!
We have several other example integrations in our sentry-java-examples
repository, and are adding more all the time.
Beyond Log Messages
The Sentry Java SDK also provides additional features that extend beyond simple
log messages and tracebacks.
HTTP Request Details
When developing a web application, much of the state that is useful for
identifying patterns among exception reports is contained within the attributes
of the HTTP request, such as the logged in user account, request URL, headers,
and query parameters. If your deployment environment utilizes Java Servlets,
these attributes are included automatically on all log messages that occur
during the context of a request.
Mapped Diagnostic Context
In addition to HTTP request attributes, it can often be helpful to add
additional context to exception reports that are specific to your application.
For example, an if you’re running an e-commerce application, it can be helpful
to track what step of a checkout process a customer was in when an error
occurred. For a consumer product, it can be helpful to track the subscription
that the user is a member of so you can effectively triage issues by customer
impact. For a stream processing system, it can be helpful to track the
component in the processing pipeline where the error occurred.
The SLF4J API provides a feature called the Mapped Diagnostic Context, or MDC,
for recording this data. This data is represented as tags or extra data in the
Sentry UI. (This feature is available when using the Log4j, Log4j 2, and
logback appenders.)
MDC.put("checkout-step", "submit");
try {
// Any exceptions raised during order submission will include the
// `checkout-step` tag.
order.submit();
} finally {
MDC.remove("checkout-step");
}
In addition to user supplied tags, a few additional tags are included in each
event, including the logger, log level, and hostname.
Beyond Log Messages
Asynchronous Logging
A common concern with over-the-network logging is the performance hit incurred
from communicating synchronously with a remote server. Asynchronous logging
prevents network latency due to logging from directly impacting your
application. The SDK design also enables asynchronous logging by frameworks
that do not natively support asynchronous appenders. The ability to tune thread
pool size, priority, and queue size also allows you to have fine-grained
control over how much logging effects your application’s CPU, memory, and
latency profile.
Not Just for Java
Although the project is named sentry-java
on GitHub, the Sentry Java SDK can
be used by any JVM language that provides Java interoperability capabilities,
including Clojure, Scala, Kotlin, and Groovy. Because idiomatic Clojure is quite
different than the other languages, we offer a sentry-clojure SDK wrapper
for a more native experience.
Take a look at the documentation for details on implementing the Sentry SDK. If you’re not yet a Sentry user, you can sign up at any time.