Tech Blog

Facebook Icon Twitter Icon Linkedin Icon

Facebook Icon Twitter Icon Linkedin Icon

[Tech Blog] Getting started Sentry setup with Spring Boot integration

Greeting, I’m Wanchalerm Popee, a senior backend engineer of AnyTag/AnyCreator production team at AnyMind group. In this article, we’re going to set up Sentry integrated with Spring Boot, an application for monitoring and error tracking.

Covering topics

  • What is Sentry?
  • Sentry Project Setup
  • Integrating Sentry with Spring Boot
  • Testing our first event
  • Grouping exceptions by application runtime environment
  • Capturing errors events only unhandled exceptions
  • Conclusion

What is Sentry?

Sentry is the tool to monitor and track application error especially unhandled exception that may happen unintentionally. It automates the exception handling for every popular programming languages, and platforms, so it would be the best choice if you’re running the microservices and want to have centralized tracking tool in one place. To know more about Sentry, please visit the website sentry.io.

Supported Sentry platforms.

Sentry Project Setup

1.) Steps to create new Sentry account is very easy and straight forward. If you don’t have please register an account first here .

2.) Next is to create new project and choose the platform. In our case, let’s choose Spring Boot!

3.) Congratulations! Now everything is ready, and you will be able to see Sentry Dashboard like this!

Integrating Sentry with Spring Boot

1.) Install Sentry SDK

First of all, we need to install Sentry SDK which will be used to capture data in our application’s runtime. We can simply install SDK dependency using Gradle.

implementation 'io.sentry:sentry-spring-boot-starter:5.2.0'

2.) Make sure you define the sentry.dsn, which as know as Data Source Name, on application properties file (src/main/application.yml), so our project can know where error reports should be logged to.

sentry:
  dsn: https://examplePublicKey@o0.ingest.sentry.io/0

3.) Now we’re all set! Next let’s make our first event.

Testing our first event

1.) Before we start, I will create simple REST API to have unhandled IndexOutOfBoundsException unintentionally.

package com.example.demosentry.controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

data class APIResponse(val ok: Boolean)

@RestController
class SampleController {

    @GetMapping("/unhandled/error")
    fun unhandledException(): APIResponse {

        // let create unhandled error exception
        val aList = listOf("a", "b", "c")
        print(aList[5])

        return APIResponse(
            ok = true
        )
    }
}

2.) I will call my API and in the result, it should raise an exception. In meantime, the event will be passed to Sentry.

3.) Open the Sentry dashboard and verify whether we have received the error message.

Finally! the error event has been captured! The best thing is, it’s showing the entire details together with the line number where an exception is happened.

Grouping exceptions by application runtime environment

It would be nice in the real world application if the exceptions can be grouped by runtime environment such as STAGING or PRODUCTION because it can help developer to easily filter the issues. Fortunately, Sentry provides this feature, and it’s very easy to set up within few seconds.

Basically, if you’re running application on multiple environments, you properly need to set active profile by passing SPRING_PROFILES_ACTIVE environment variable. In order to make it works, let’s split application properties files for staging and production. Lastly, passing sentry.environment property like following will be required.

For STAGING /src/main/resources/application-staging.yml

sentry:
  dsn: https://examplePublicKey@o0.ingest.sentry.io/0
  environment: staging

For PRODUCTION /src/main/resources/application-production.yml

sentry:
  dsn: https://examplePublicKey@o0.ingest.sentry.io/0
  environment: production

Let’s open Sentry dashboard again and check what it looks like after the new events have been captured!

As you can see, that’s all of we need, Sentry automatically creates production and staging environment filtering for us. In my opinion as developer, this is a very useful feature because I can save my times to filter out the unwanted issues and efficiently focus on what I’m looking for.

Capturing errors events only unhandled exceptions

Most of the time, the custom domain error may be needed for a specific application instead of regularly raising an internal server error even thought it’s handled error in our try-catch or validation. Alternatively, it can be considered as the business domain error. Those kinds of exception should not be caught and reported to Sentry because it’s not actual unexpected errors. Luckily, Sentry comes with an ability to catch only an exception’s certain type by implementing the SentryOptions.BeforeSendCallback bean.

In the next example, we will create an additional custom error called DomainException which is implementing RuntimeException. Apparently this is our business domain error which should not be reported to Sentry. Next step is, we need to implement SentryOptions.BeforeSendCallback to not catch an exception (return null) if DomainException has been raised from our runtime.

package com.example.demosentry

import io.sentry.SentryEvent
import io.sentry.SentryOptions
import org.springframework.stereotype.Component

class DomainException(message:String): RuntimeException(message)

@Component
class SentryConfig : SentryOptions.BeforeSendCallback {
    override fun execute(event: SentryEvent, hint: Any?): SentryEvent? {
        if (event.throwable is DomainException) {
            return null
        }
        return event
    }
}

Also let’s add new REST API which have a handled exception properly.

package com.example.demosentry.controller

import com.example.demosentry.DomainException
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

data class APIResponse(val ok: Boolean)

@RestController
class SampleController {
    @GetMapping("/unhandled/error")
    fun unhandledException(): APIResponse {

        // let create unhandled error exception
        val aList = listOf("a", "b", "c")
        print(aList[5])

        return APIResponse(
            ok = true
        )
    }

    @GetMapping("/handled/error")
    fun handledException(): APIResponse {

        try {
            val aList = listOf("a", "b", "c")
            print(aList[5])
        } catch (e: Exception) {
            throw DomainException("there is something wrong but I know")
        }

        return APIResponse(
            ok = true
        )
    }
}

Lastly, I will call my new API by Postman and open the Sentry dashboard UI to see what will happen.

At the result, we’re not be able to see the exception which was raised by DomainException and yeah! this is what we expected!

Conclusion

Picking an application for monitoring and error tracking nowadays is very common in the real world because opening and checking log files line-by-line to find where is my error would be very painful especially the large-scale application. One of popular tool that we, AnyTag/AnyCreator team, chose is Sentry where developer can easily integrate with many programming languages, and platforms. Personally I’ve been using it a few years and feel very happy. Here are reasons.

  • I can identify the error and solve it very quickly which is partially one of our company values, Move Faster.
  • Steps to setup are simple and Sentry doc is easy to read.
  • Pay on-demand if we exceed the quota of the plan. The less errors sent, the less we pay. Or in other words, the more we fix the error, the less we pay the cost. It sounds motivating you to fix error? 🙂

Thank you for reading.

Latest News