JustAnalytics for Spring Boot
Step-by-step guide to add analytics, error tracking, and APM to your Spring Boot app
JustAnalytics for Spring Boot
Add distributed tracing, error tracking, structured logging, and infrastructure metrics to your Spring Boot application.
Time to data: 5 minutesPrerequisites#
- Spring Boot 3.0+
- Java 17+ (Kotlin 1.9+ also supported)
- Gradle or Maven
- A JustAnalytics account with a Site ID and API key
Add the dependency
Add the JitPack repository, then the dependency:
Gradle (Kotlin DSL):
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
mavenCentral()
maven("https://jitpack.io")
}
}
// build.gradle.kts
dependencies {
implementation("com.github.specifiedcodes.justanalytics-java:justanalytics-spring-boot-starter:0.1.0")
}
Gradle (Groovy):
// settings.gradle
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
// build.gradle
dependencies {
implementation 'com.github.specifiedcodes.justanalytics-java:justanalytics-spring-boot-starter:0.1.0'
}
Maven:
<!-- pom.xml -->
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.specifiedcodes.justanalytics-java</groupId>
<artifactId>justanalytics-spring-boot-starter</artifactId>
<version>0.1.0</version>
</dependency>
Configure application properties
Add your credentials to application.yml:
# src/main/resources/application.yml
justanalytics:
site-id: ${JA_SITE_ID}
api-key: ${JA_API_KEY}
service-name: spring-api
environment: ${SPRING_PROFILES_ACTIVE:development}
release: ${APP_VERSION:0.0.0}
Or application.properties:
justanalytics.site-id=${JA_SITE_ID}
justanalytics.api-key=${JA_API_KEY}
justanalytics.service-name=spring-api
justanalytics.environment=${SPRING_PROFILES_ACTIVE:development}
The Spring Boot starter auto-configures:
- SDK initialization from properties
- Servlet filter for HTTP request tracing
RestTemplateinterceptor for outgoing HTTP spansWebClientfilter for reactive HTTP spans
Automatic request tracing
All incoming HTTP requests are automatically traced via the servlet filter. No annotations needed:
Java:
@RestController
@RequestMapping("/api/users")
public class UserController {
// Automatically traced as "GET /api/users/{id}"
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
// Automatically traced as "POST /api/users"
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@RequestBody @Valid CreateUserRequest request) {
return userService.create(request);
}
}
Kotlin:
@RestController
@RequestMapping("/api/users")
class UserController(private val userService: UserService) {
@GetMapping("/{id}")
fun getUser(@PathVariable id: Long): User =
userService.findById(id) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun createUser(@RequestBody @Valid request: CreateUserRequest): User =
userService.create(request)
}
Add error tracking
Add a global exception handler:
Java:
import com.justanalytics.JustAnalytics;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String, String> handleException(Exception ex) {
JustAnalytics.captureException(ex,
Map.of("source", "global_handler"));
return Map.of("error", "Internal server error");
}
}
Kotlin:
import com.justanalytics.JustAnalytics
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(Exception::class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
fun handleException(ex: Exception): Map<String, String> {
JustAnalytics.captureException(ex, tags = mapOf("source" to "global_handler"))
return mapOf("error" to "Internal server error")
}
}
Verify data is flowing
Start your application and send a test request:
./gradlew bootRun
curl http://localhost:8080/api/users/1
Verify Your Setup
After starting your app and sending a request, check the Traces tab. You should see spans for your Spring Boot endpoints.
Open DashboardAdvanced Features#
Custom spans (Java)#
import com.justanalytics.JustAnalytics;
@Service
public class OrderService {
public Order processOrder(CreateOrderRequest request) {
return JustAnalytics.startSpan("process-order", span -> {
span.setAttribute("order.items", request.getItems().size());
Order order = JustAnalytics.startSpan("db.create-order", innerSpan -> {
return orderRepository.save(new Order(request));
});
JustAnalytics.startSpan("send-confirmation", innerSpan -> {
emailService.sendConfirmation(order);
return null;
});
return order;
});
}
}
Custom spans (Kotlin)#
import com.justanalytics.JustAnalytics
@Service
class OrderService(private val orderRepo: OrderRepository) {
fun processOrder(request: CreateOrderRequest): Order {
return JustAnalytics.startSpan("process-order") { span ->
span.setAttribute("order.items", request.items.size.toString())
val order = JustAnalytics.startSpan("db.create-order") {
orderRepo.save(Order(request))
}
JustAnalytics.startSpan("send-confirmation") {
emailService.sendConfirmation(order)
}
order
}
}
}
User identification#
import com.justanalytics.JustAnalytics;
import com.justanalytics.UserInfo;
@Component
public class AuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
UserDetails user = (UserDetails) auth.getPrincipal();
JustAnalytics.setUser(new UserInfo(user.getUsername(), user.getUsername()));
}
chain.doFilter(request, response);
}
}
Logback integration#
Ship logs to JustAnalytics via the Logback appender:
<!-- src/main/resources/logback-spring.xml -->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="JUSTANALYTICS" class="com.justanalytics.logback.JustAnalyticsAppender">
<minimumLevel>INFO</minimumLevel>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
<appender-ref ref="JUSTANALYTICS" />
</root>
</configuration>
Custom metrics#
JustAnalytics.recordMetric("custom.queue_size", queueSize,
Map.of("queue", "orders"));
JustAnalytics.recordMetric("custom.cache_hit_rate", hitRate);
Environment variables#
| Variable | Description | Required |
|----------|-------------|----------|
| JA_SITE_ID | Your site ID from the dashboard | Yes |
| JA_API_KEY | API key (starts with ja_sk_) | Yes |
| SPRING_PROFILES_ACTIVE | Used as the environment tag | No |
| APP_VERSION | Release version for tracking | No |