Sentry Performance in Java: Comprehensive Application Performance Monitoring

Sentry Performance (part of Sentry's APM offering) provides detailed performance monitoring and tracing for Java applications. This guide covers comprehensive integration for monitoring application performance, errors, and distributed traces.


Dependencies and Setup

Maven Dependencies
<properties>
<sentry.version>7.3.0</sentry.version>
<spring-boot.version>3.1.0</spring-boot.version>
</properties>
<dependencies>
<!-- Sentry Core -->
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry</artifactId>
<version>${sentry.version}</version>
</dependency>
<!-- Sentry Spring Boot Starter -->
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring-boot-starter</artifactId>
<version>${sentry.version}</version>
</dependency>
<!-- Sentry Logback Integration -->
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-logback</artifactId>
<version>${sentry.version}</version>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- For HTTP Client tracing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>${spring-boot.version}</version>
</dependency>
</dependencies>
Gradle Configuration
dependencies {
implementation 'io.sentry:sentry:7.3.0'
implementation 'io.sentry:sentry-spring-boot-starter:7.3.0'
implementation 'io.sentry:sentry-logback:7.3.0'
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0'
}

Core Configuration

1. Application Properties
# application.yml
sentry:
dsn: https://[email protected]/1234567
environment: production
release: [email protected]
# Performance Monitoring
traces-sample-rate: 1.0
profiles-sample-rate: 1.0
enable-tracing: true
# Debugging
debug: false
send-default-pii: false
# Server Name
server-name: user-service-01
# In-App Includes
in-app-includes:
- com.example
- com.yourapp
# Context
send-client-reports: true
attach-stacktrace: true
attach-threads: true
logging:
level:
io.sentry: INFO
2. Programmatic Configuration
package com.example.config;
import io.sentry.Sentry;
import io.sentry.spring.tracing.SentryTracingConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SentryConfig {
@Value("${sentry.dsn:}")
private String sentryDsn;
@Value("${sentry.environment:development}")
private String environment;
@Bean
public Sentry.OptionsConfiguration<SentryOptions> sentryOptionsConfiguration() {
return options -> {
options.setDsn(sentryDsn);
options.setEnvironment(environment);
options.setRelease("[email protected]");
// Performance Monitoring
options.setTracesSampleRate(1.0);
options.setProfilesSampleRate(1.0);
options.setEnableTracing(true);
// Debugging
options.setDebug(false);
options.setSendDefaultPii(false);
// Server identification
options.setServerName("user-service");
// In-app stack frames
options.addInAppInclude("com.example");
options.addInAppInclude("com.yourapp");
// Context
options.setSendClientReports(true);
options.setAttachStacktrace(true);
options.setAttachThreads(true);
// Before send filter
options.setBeforeSend((event, hint) -> {
// Filter out sensitive data
return filterSensitiveData(event);
});
// Before breadcrumb filter
options.setBeforeBreadcrumb((breadcrumb, hint) -> {
// Filter breadcrumbs
return filterBreadcrumb(breadcrumb);
});
};
}
@Bean
public SentryTracingConfiguration sentryTracingConfiguration() {
return new SentryTracingConfiguration();
}
private SentryEvent filterSensitiveData(SentryEvent event) {
if (event.getRequest() != null) {
// Remove sensitive headers
event.getRequest().getHeaders().remove("Authorization");
event.getRequest().getHeaders().remove("Cookie");
}
return event;
}
private Breadcrumb filterBreadcrumb(Breadcrumb breadcrumb) {
// Filter out health check breadcrumbs
if (breadcrumb.getMessage() != null && 
breadcrumb.getMessage().contains("/health")) {
return null;
}
return breadcrumb;
}
}
3. Logback Configuration
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Sentry Appender -->
<appender name="SENTRY" class="io.sentry.logback.SentryAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<!-- Optional: add breadcrumbs for lower levels -->
<breadcrumbFilter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</breadcrumbFilter>
</appender>
<!-- Console Appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Async Sentry Appender for Better Performance -->
<appender name="ASYNC_SENTRY" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="SENTRY"/>
<queueSize>1000</queueSize>
<discardingThreshold>0</discardingThreshold>
<includeCallerData>true</includeCallerData>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_SENTRY"/>
</root>
<!-- Specific logger for performance monitoring -->
<logger name="com.example.service" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_SENTRY"/>
</logger>
</configuration>

Performance Monitoring Implementation

1. Custom Transaction Instrumentation
package com.example.sentry;
import io.sentry.Sentry;
import io.sentry.SpanStatus;
import io.sentry.TransactionContext;
import io.sentry.protocol.SentryId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class SentryTransactionManager {
private static final Logger logger = LoggerFactory.getLogger(SentryTransactionManager.class);
public <T> T trackTransaction(String operation, String description, 
TransactionOperation<T> operation) {
var transaction = Sentry.startTransaction(
new TransactionContext(operation, description), 
true // bind to scope
);
try {
logger.debug("Starting transaction: {} - {}", operation, description);
T result = operation.execute();
transaction.finish(SpanStatus.OK);
logger.debug("Transaction completed successfully: {}", operation);
return result;
} catch (Exception e) {
transaction.finish(SpanStatus.INTERNAL_ERROR);
transaction.setThrowable(e);
logger.error("Transaction failed: {}", operation, e);
throw e;
}
}
public void trackAsyncTransaction(String operation, String description, 
Runnable operation) {
var transaction = Sentry.startTransaction(
new TransactionContext(operation, description), 
true
);
try {
new Thread(() -> {
try {
operation.run();
transaction.finish(SpanStatus.OK);
} catch (Exception e) {
transaction.finish(SpanStatus.INTERNAL_ERROR);
transaction.setThrowable(e);
}
}).start();
} catch (Exception e) {
transaction.finish(SpanStatus.INTERNAL_ERROR);
throw e;
}
}
@FunctionalInterface
public interface TransactionOperation<T> {
T execute();
}
}
2. HTTP Request Performance Monitoring
package com.example.sentry;
import io.sentry.Sentry;
import io.sentry.SpanStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class SentryPerformanceInterceptor implements HandlerInterceptor {
private static final String TRANSACTION_ATTRIBUTE = "sentry-transaction";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
Object handler) throws Exception {
String operation = "http." + request.getMethod().toLowerCase();
String description = request.getRequestURI();
var transaction = Sentry.startTransaction(
new io.sentry.TransactionContext(operation, description), 
true
);
// Set transaction on request for later access
request.setAttribute(TRANSACTION_ATTRIBUTE, transaction);
// Add request details to transaction
transaction.setTag("http.method", request.getMethod());
transaction.setTag("http.route", request.getRequestURI());
transaction.setTag("http.url", request.getRequestURL().toString());
transaction.setTag("http.user_agent", request.getHeader("User-Agent"));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
Object handler, Exception ex) throws Exception {
var transaction = (io.sentry.ITransaction) 
request.getAttribute(TRANSACTION_ATTRIBUTE);
if (transaction != null) {
// Set response details
transaction.setTag("http.status_code", String.valueOf(response.getStatus()));
if (ex != null) {
transaction.setThrowable(ex);
transaction.finish(SpanStatus.INTERNAL_ERROR);
} else if (response.getStatus() >= 400) {
transaction.finish(SpanStatus.fromHttpStatusCode(response.getStatus()));
} else {
transaction.finish(SpanStatus.OK);
}
}
}
}
3. Database Performance Monitoring
package com.example.sentry;
import io.sentry.Sentry;
import io.sentry.SpanStatus;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DatabasePerformanceAspect {
private static final Logger logger = LoggerFactory.getLogger(DatabasePerformanceAspect.class);
@Around("execution(* com.example.repository.*.*(..))")
public Object monitorDatabasePerformance(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
var span = Sentry.startSpan("db.query", className + "." + methodName);
try {
span.setTag("db.operation", methodName);
span.setTag("db.component", "JDBC");
span.setTag("db.instance", "users-db");
logger.debug("Executing database operation: {}.{}", className, methodName);
Object result = joinPoint.proceed();
span.finish(SpanStatus.OK);
return result;
} catch (Exception e) {
span.setThrowable(e);
span.finish(SpanStatus.INTERNAL_ERROR);
logger.error("Database operation failed: {}.{}", className, methodName, e);
throw e;
}
}
@Around("@annotation(com.example.annotation.SlowQuery)")
public Object monitorSlowQueries(ProceedingJoinPoint joinPoint) throws Throwable {
String queryName = getQueryName(joinPoint);
long startTime = System.currentTimeMillis();
var span = Sentry.startSpan("db.slow_query", queryName);
try {
span.setTag("db.query.name", queryName);
span.setTag("db.system", "postgresql");
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
span.setData("db.duration_ms", duration);
if (duration > 1000) { // 1 second threshold
span.setTag("db.slow_query", "true");
logger.warn("Slow query detected: {} took {}ms", queryName, duration);
}
span.finish(SpanStatus.OK);
return result;
} catch (Exception e) {
span.setThrowable(e);
span.finish(SpanStatus.INTERNAL_ERROR);
throw e;
}
}
private String getQueryName(ProceedingJoinPoint joinPoint) {
return joinPoint.getSignature().getName();
}
}
4. External Service Call Monitoring
package com.example.sentry;
import io.sentry.Sentry;
import io.sentry.SpanStatus;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class SentryRestTemplateInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, 
ClientHttpRequestExecution execution) throws IOException {
String serviceName = extractServiceName(request.getURI().getHost());
var span = Sentry.startSpan("http.client", 
request.getMethod() + " " + request.getURI().getPath());
try {
// Add request details
span.setTag("http.method", request.getMethod().name());
span.setTag("http.url", request.getURI().toString());
span.setTag("http.target", request.getURI().getPath());
span.setTag("net.peer.name", request.getURI().getHost());
span.setTag("net.peer.port", String.valueOf(request.getURI().getPort()));
span.setTag("service.name", serviceName);
// Execute request
ClientHttpResponse response = execution.execute(request, body);
// Add response details
span.setTag("http.status_code", String.valueOf(response.getStatusCode().value()));
if (response.getStatusCode().isError()) {
span.finish(SpanStatus.fromHttpStatusCode(response.getStatusCode().value()));
} else {
span.finish(SpanStatus.OK);
}
return response;
} catch (Exception e) {
span.setThrowable(e);
span.finish(SpanStatus.INTERNAL_ERROR);
throw e;
}
}
private String extractServiceName(String host) {
if (host.contains("payment-service")) return "payment-service";
if (host.contains("inventory-service")) return "inventory-service";
if (host.contains("notification-service")) return "notification-service";
return "external-service";
}
}

Service Implementation with Sentry Performance

1. User Service with Performance Monitoring
package com.example.service;
import com.example.sentry.SentryTransactionManager;
import io.sentry.Sentry;
import io.sentry.SpanStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
private final SentryTransactionManager transactionManager;
private final UserRepository userRepository;
public UserService(SentryTransactionManager transactionManager, 
UserRepository userRepository) {
this.transactionManager = transactionManager;
this.userRepository = userRepository;
}
public User createUser(CreateUserRequest request) {
return transactionManager.trackTransaction(
"user.create", 
"Create new user account", 
() -> {
// Start a child span for validation
var validationSpan = Sentry.startSpan("user.validation", "Validate user data");
try {
validateUserRequest(request);
} finally {
validationSpan.finish();
}
// Check for existing user
var checkSpan = Sentry.startSpan("user.check_existing", "Check existing user");
boolean userExists;
try {
userExists = userRepository.existsByEmail(request.getEmail());
} finally {
checkSpan.finish();
}
if (userExists) {
throw new UserAlreadyExistsException("User already exists");
}
// Create user
var creationSpan = Sentry.startSpan("user.creation", "Create user entity");
User savedUser;
try {
User user = new User(request);
savedUser = userRepository.save(user);
// Add user context to Sentry
Sentry.configureScope(scope -> {
scope.setUser(new io.sentry.protocol.User(
savedUser.getId(), 
savedUser.getEmail(), 
null, // IP address
savedUser.getUsername()
));
});
} finally {
creationSpan.finish();
}
logger.info("User created successfully: {}", savedUser.getId());
// Record custom metric
Sentry.metrics().increment("user.created", 1, Map.of(
"user.role", request.getRole(),
"user.source", request.getSource()
));
return savedUser;
});
}
public User findUserById(String userId) {
var span = Sentry.startSpan("user.find_by_id", "Find user by ID");
try {
span.setTag("user.id", userId);
logger.debug("Searching for user: {}", userId);
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException("User not found"));
span.setTag("user.found", "true");
span.setTag("user.email", user.getEmail());
return user;
} catch (Exception e) {
span.setThrowable(e);
span.setTag("user.found", "false");
throw e;
} finally {
span.finish();
}
}
public void processUserBatch(List<String> userIds) {
transactionManager.trackAsyncTransaction(
"user.batch_processing", 
"Process batch of users", 
() -> {
Sentry.metrics().distribution("user.batch_size", userIds.size());
userIds.parallelStream().forEach(this::processSingleUser);
logger.info("Batch processing completed for {} users", userIds.size());
});
}
private void processSingleUser(String userId) {
var span = Sentry.startSpan("user.single_processing", "Process single user");
try {
User user = findUserById(userId);
// Process user logic...
Sentry.metrics().increment("user.processed");
} catch (Exception e) {
span.setThrowable(e);
Sentry.metrics().increment("user.processing_failed");
logger.error("Failed to process user: {}", userId, e);
} finally {
span.finish();
}
}
private void validateUserRequest(CreateUserRequest request) {
if (request.getEmail() == null || !request.getEmail().contains("@")) {
throw new IllegalArgumentException("Invalid email address");
}
}
}
2. Order Service with Distributed Tracing
package com.example.service;
import com.example.sentry.SentryTransactionManager;
import io.sentry.Sentry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
private final SentryTransactionManager transactionManager;
private final RestTemplate restTemplate;
public OrderService(SentryTransactionManager transactionManager, 
RestTemplate restTemplate) {
this.transactionManager = transactionManager;
this.restTemplate = restTemplate;
}
public Order processOrder(OrderRequest request) {
return transactionManager.trackTransaction(
"order.process", 
"Process customer order", 
() -> {
var orderSpan = Sentry.getCurrentSpan();
// Add business context
orderSpan.setData("order.amount", request.getAmount());
orderSpan.setData("order.currency", request.getCurrency());
orderSpan.setData("order.customer_id", request.getCustomerId());
orderSpan.setTag("order.priority", request.getPriority());
// Step 1: Validate inventory
var inventorySpan = Sentry.startSpan("inventory.check", "Check inventory");
boolean inventoryAvailable;
try {
inventoryAvailable = checkInventory(request.getItems());
orderSpan.setTag("order.inventory_available", inventoryAvailable);
if (!inventoryAvailable) {
throw new InventoryUnavailableException("Insufficient inventory");
}
} finally {
inventorySpan.finish();
}
// Step 2: Process payment
var paymentSpan = Sentry.startSpan("payment.process", "Process payment");
PaymentResponse paymentResponse;
try {
paymentResponse = processPayment(request);
orderSpan.setTag("order.payment_id", paymentResponse.getPaymentId());
Sentry.addBreadcrumb("Payment processed successfully");
} finally {
paymentSpan.finish();
}
// Step 3: Create order
var orderCreationSpan = Sentry.startSpan("order.creation", "Create order");
Order savedOrder;
try {
Order order = createOrderEntity(request, paymentResponse);
savedOrder = orderRepository.save(order);
orderSpan.setTag("order.id", savedOrder.getId());
} finally {
orderCreationSpan.finish();
}
// Step 4: Update inventory
var inventoryUpdateSpan = Sentry.startSpan("inventory.update", "Update inventory");
try {
updateInventory(request.getItems());
Sentry.addBreadcrumb("Inventory updated");
} finally {
inventoryUpdateSpan.finish();
}
// Step 5: Send notification
var notificationSpan = Sentry.startSpan("notification.send", "Send notification");
try {
sendOrderConfirmation(savedOrder);
Sentry.addBreadcrumb("Notification sent");
} finally {
notificationSpan.finish();
}
// Record success metric
Sentry.metrics().increment("order.processed", 1, Map.of(
"order.currency", request.getCurrency(),
"order.priority", request.getPriority()
));
logger.info("Order processed successfully: {}", savedOrder.getId());
return savedOrder;
});
}
private boolean checkInventory(List<OrderItem> items) {
var span = Sentry.startSpan("http.call", "inventory.check");
try {
span.setTag("service", "inventory-service");
span.setTag("http.route", "/api/inventory/check");
InventoryCheckRequest checkRequest = new InventoryCheckRequest(items);
var response = restTemplate.postForEntity(
"http://inventory-service/api/inventory/check",
checkRequest,
InventoryCheckResponse.class
);
boolean available = response.getBody().isAvailable();
span.setTag("inventory.available", available);
return available;
} finally {
span.finish();
}
}
private PaymentResponse processPayment(OrderRequest request) {
return transactionManager.trackTransaction(
"payment.process", 
"Process payment for order", 
() -> {
var paymentSpan = Sentry.getCurrentSpan();
paymentSpan.setData("payment.amount", request.getAmount());
paymentSpan.setData("payment.currency", request.getCurrency());
PaymentRequest paymentRequest = new PaymentRequest(request);
var response = restTemplate.postForEntity(
"http://payment-service/api/payments/process",
paymentRequest,
PaymentResponse.class
);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new PaymentProcessingException("Payment processing failed");
}
return response.getBody();
});
}
}

Advanced Configuration

1. Environment-Specific Configuration
# application-dev.yml
sentry:
dsn: ${SENTRY_DEV_DSN}
environment: development
traces-sample-rate: 1.0
profiles-sample-rate: 1.0
debug: true
logging:
level:
io.sentry: DEBUG
# application-prod.yml
sentry:
dsn: ${SENTRY_PROD_DSN}
environment: production
traces-sample-rate: 0.1  # Sample 10% in production
profiles-sample-rate: 0.1
debug: false
logging:
level:
io.sentry: WARN
2. Dynamic Sampling Configuration
package com.example.sentry;
import io.sentry.SentryOptions;
import io.sentry.SamplingContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SentrySamplingConfig {
@Bean
public SentryOptions.TracesSamplerCallback tracesSampler() {
return context -> {
// Sample all health checks at lower rate
if (isHealthCheck(context)) {
return 0.01;
}
// Sample high-value transactions at 100%
if (isHighValueTransaction(context)) {
return 1.0;
}
// Sample errors at 100%
if (context.getSamplingContext().getTransactionContext().getSampled() != null 
&& context.getSamplingContext().getTransactionContext().getSampled()) {
return 1.0;
}
// Default sampling rate
return 0.1;
};
}
private boolean isHealthCheck(SamplingContext context) {
return context.getTransactionContext().getName() != null 
&& context.getTransactionContext().getName().contains("health");
}
private boolean isHighValueTransaction(SamplingContext context) {
return context.getTransactionContext().getName() != null 
&& (context.getTransactionContext().getName().contains("checkout")
|| context.getTransactionContext().getName().contains("payment"));
}
}

Testing and Verification

1. Unit Test for Sentry Integration
package com.example.service;
import io.sentry.Sentry;
import io.sentry.SentryEvent;
import io.sentry.SentryOptions;
import io.sentry.testing.FakeTransport;
import io.sentry.testing.TestTransport;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserServiceSentryTest {
@Autowired
private UserService userService;
private TestTransport transport;
@BeforeEach
void setUp() {
transport = new FakeTransport();
Sentry.init(options -> {
options.setDsn("https://key@localhost/1");
options.setTransport(transport);
options.setTracesSampleRate(1.0);
options.setEnableTracing(true);
});
}
@Test
void shouldCaptureTransactionForUserCreation() {
// Given
CreateUserRequest request = new CreateUserRequest("[email protected]", "USER");
// When
User user = userService.createUser(request);
// Then
assertEquals(1, transport.getEvents().size());
SentryEvent event = transport.getEvents().get(0);
assertNotNull(event.getTransaction());
}
@Test
void shouldCaptureErrorForInvalidUser() {
// Given
CreateUserRequest request = new CreateUserRequest("invalid-email", "USER");
// When & Then
assertThrows(IllegalArgumentException.class, () -> {
userService.createUser(request);
});
// Verify error was captured
assertTrue(transport.getEvents().stream()
.anyMatch(event -> event.getThrowable() != null));
}
}
2. Integration Test
package com.example.integration;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SentryIntegrationTest {
@LocalServerPort
private int port;
private TestRestTemplate restTemplate = new TestRestTemplate();
@Test
void shouldCapturePerformanceData() {
// When
ResponseEntity<String> response = restTemplate.getForEntity(
"http://localhost:" + port + "/api/users/123", String.class);
// Then
assertEquals(200, response.getStatusCodeValue());
// Performance data is captured by Sentry automatically
}
}

Best Practices

  1. Meaningful Transaction Names: Use descriptive names for transactions
  2. Reasonable Sampling: Adjust sampling rates based on traffic and importance
  3. Context Enrichment: Add business context to spans and transactions
  4. Error Correlation: Link errors with performance data
  5. Sensitive Data: Never include PII in transaction data
  6. Performance Impact: Monitor the performance impact of tracing
// Good practice - meaningful transaction names
Sentry.startTransaction("order.checkout", "Process customer checkout");
// Bad practice - unclear transaction names  
Sentry.startTransaction("process", "do stuff");

Monitoring and Alerting

1. Custom Performance Metrics
package com.example.monitoring;
import io.sentry.Sentry;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class PerformanceMetricsCollector {
@Scheduled(fixedRate = 60000) // Every minute
public void collectApplicationMetrics() {
// Custom business metrics
Sentry.metrics().gauge("application.active_users", getActiveUsersCount());
Sentry.metrics().gauge("application.pending_orders", getPendingOrdersCount());
Sentry.metrics().gauge("system.memory.used", getUsedMemory());
Sentry.metrics().gauge("system.cpu.usage", getCpuUsage());
}
private long getActiveUsersCount() {
// Implementation to get active users
return 150L;
}
private long getPendingOrdersCount() {
// Implementation to get pending orders
return 23L;
}
private double getUsedMemory() {
Runtime runtime = Runtime.getRuntime();
return (runtime.totalMemory() - runtime.freeMemory()) / (1024.0 * 1024.0);
}
private double getCpuUsage() {
// Implementation to get CPU usage
return 45.5;
}
}
2. Health Check with Performance Monitoring
package com.example.health;
import io.sentry.Sentry;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Override
public Health health() {
var span = Sentry.startSpan("health.check", "database");
try {
long startTime = System.currentTimeMillis();
// Perform database health check
boolean isHealthy = checkDatabaseHealth();
long duration = System.currentTimeMillis() - startTime;
span.setData("health.duration_ms", duration);
span.setTag("health.status", isHealthy ? "healthy" : "unhealthy");
if (isHealthy) {
return Health.up()
.withDetail("response_time", duration + "ms")
.build();
} else {
return Health.down()
.withDetail("response_time", duration + "ms")
.build();
}
} finally {
span.finish();
}
}
private boolean checkDatabaseHealth() {
// Implementation to check database health
return true;
}
}

Conclusion

Sentry Performance provides:

  • Distributed tracing across microservices
  • Performance monitoring with detailed transaction timing
  • Error correlation with performance data
  • Custom metrics for business and system monitoring
  • Real-user monitoring (when combined with frontend Sentry)

By implementing comprehensive Sentry Performance monitoring, you gain deep insights into your application's performance characteristics, enabling you to identify bottlenecks, optimize critical paths, and maintain excellent user experience. The combination of error tracking and performance monitoring in one platform makes Sentry a powerful observability tool for modern Java applications.

Advanced Java Supply Chain Security, Kubernetes Hardening & Runtime Threat Detection

Sigstore Rekor in Java – https://macronepal.com/blog/sigstore-rekor-in-java/
Explains integrating Sigstore Rekor into Java systems to create a transparent, tamper-proof log of software signatures and metadata for verifying supply chain integrity.

Securing Java Applications with Chainguard Wolfi – https://macronepal.com/blog/securing-java-applications-with-chainguard-wolfi-a-comprehensive-guide/
Explains using Chainguard Wolfi minimal container images to reduce vulnerabilities and secure Java applications with hardened, lightweight runtime environments.

Cosign Image Signing in Java Complete Guide – https://macronepal.com/blog/cosign-image-signing-in-java-complete-guide/
Explains how to digitally sign container images using Cosign in Java-based workflows to ensure authenticity and prevent unauthorized modifications.

Secure Supply Chain Enforcement Kyverno Image Verification for Java Containers – https://macronepal.com/blog/secure-supply-chain-enforcement-kyverno-image-verification-for-java-containers/
Explains enforcing Kubernetes policies with Kyverno to verify container image signatures and ensure only trusted Java container images are deployed.

Pod Security Admission in Java Securing Kubernetes Deployments for JVM Applications – https://macronepal.com/blog/pod-security-admission-in-java-securing-kubernetes-deployments-for-jvm-applications/
Explains Kubernetes Pod Security Admission policies that enforce security rules like restricted privileges and safe configurations for Java workloads.

Securing Java Applications at Runtime Kubernetes Security Context – https://macronepal.com/blog/securing-java-applications-at-runtime-a-guide-to-kubernetes-security-context/
Explains how Kubernetes security contexts control runtime permissions, user IDs, and access rights for Java containers to improve isolation.

Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring-2/
Explains detecting abnormal runtime behavior in Java applications to identify potential security threats using process monitoring techniques.

Achieving Security Excellence CIS Benchmark Compliance for Java Applications – https://macronepal.com/blog/achieving-security-excellence-implementing-cis-benchmark-compliance-for-java-applications/
Explains applying CIS security benchmarks to Java environments to standardize hardening and improve overall system security posture.

Process Anomaly Detection in Java Behavioral Monitoring – https://macronepal.com/blog/process-anomaly-detection-in-java-comprehensive-behavioral-monitoring/
Explains behavioral monitoring of Java processes to detect anomalies and improve runtime security through continuous observation and analysis.

JAVA CODE COMPILER

FREE ONLINE JAVA CODE COMPILER

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper