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
The JustAnalytics Java SDK is distributed via GitHub Packages. You need a one-time setup for authentication, then add the dependency.
One-time setup: Create a GitHub personal access token at github.com/settings/tokens with read:packages scope, then add to ~/.gradle/gradle.properties:
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=YOUR_GITHUB_TOKEN
Gradle (Kotlin DSL):
// build.gradle.kts
repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/specifiedcodes/justanalyticstech")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_USERNAME")
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
implementation("app.justanalytics:justanalytics-spring-boot-starter:0.1.1")
}
Gradle (Groovy):
// build.gradle
repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/specifiedcodes/justanalyticstech")
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_USERNAME")
password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
implementation 'app.justanalytics:justanalytics-spring-boot-starter:0.1.1'
}
Maven:
<!-- pom.xml -->
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/specifiedcodes/justanalyticstech</url>
</repository>
</repositories>
<dependency>
<groupId>app.justanalytics</groupId>
<artifactId>justanalytics-spring-boot-starter</artifactId>
<version>0.1.1</version>
</dependency>
For Maven, add your GitHub credentials to ~/.m2/settings.xml:
<servers>
<server>
<id>github</id>
<username>YOUR_GITHUB_USERNAME</username>
<password>YOUR_GITHUB_TOKEN</password>
</server>
</servers>
Why GitHub Packages? The JustAnalytics Java SDK is currently distributed via GitHub Packages rather than Maven Central. This gives us faster release cycles and simpler CI/CD automation. We plan to publish to Maven Central in the future for convenience.
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 |