Blog
ArchiveTwitterFeed
}

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 an 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 as sentry-logback, sentry-log4j, and sentry-log4j2.
  • Sentry, used by the logging integrations to send events to a Sentry server. This is packaged as sentry and also includes the logging handler for java.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 the logger.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.

image

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?

xkcd standards

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.

  1. Add sentry-logback to your project dependencies. If you’re using Maven, dependencies are declared in pom.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>
  2. Add the Sentry appender to your logback.xml configuration file. You can set the Sentry DSN for your project via JDNI, as the SENTRY_DSN environment variable or sentry.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>
  3. Add logging calls to your application. It is usually most effective to add logging.error calls as far towards the top of the stack in your application as possible — good examples are a main method in a console application, 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 other try/catch blocks where checked exceptions are captured.

        public static void main(String[] args)
        {
            try {
                application.run()
            } catch (Exception e) {
                logger.error("Caught exception!", e);
            }
        }
  4. 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.

Your code is broken. Let's Fix it.
Start using Sentry