google-site-verification: google61fe8ba583a51912.html
Sentry Performance in Java

Introduction to Sentry Performance Monitoring

Sentry Performance provides distributed tracing and performance monitoring for Java applications. It helps identify bottlenecks, track slow transactions, and monitor application performance across microservices.

Maven Configuration

<properties> <sentry.version>6.28.0</sentry.version> <opentelemetry.version>1.28.0</opentelemetry.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> <!-- OpenTelemetry Integration --> <dependency> <groupId>io.sentry</groupId> <artifactId>sentry-opentelemetry</artifactId> <version>${sentry.version}</version> </dependency> <!-- Database Performance Monitoring --> <dependency> <groupId>io.sentry</groupId> <artifactId>sentry-jdbc</artifactId> <version>${sentry.version}</version> </dependency> </dependencies>

Core Configuration

Sentry Spring Boot Auto-Configuration

@Configuration @EnableConfigurationProperties(SentryProperties.class) public class SentryConfig { private static final Logger logger = LoggerFactory.getLogger(SentryConfig.class); @Bean @ConditionalOnProperty(name = "sentry.enabled", havingValue = "true") public SentryOptions sentryOptions(SentryProperties properties) { try { SentryOptions options = new SentryOptions(); // Basic configuration options.setDsn(properties.getDsn()); options.setEnvironment(properties.getEnvironment()); options.setRelease(properties.getRelease()); options.setDebug(properties.isDebug()); // Performance monitoring options.setTracesSampleRate(properties.getTracesSampleRate()); options.setProfilesSampleRate(properties.getProfilesSampleRate()); options.setEnableTracing(properties.isEnableTracing()); options.setTracePropagationTargets(properties.getTracePropagationTargets()); // Session tracking options.setEnableSessionTracking(properties.isEnableSessionTracking()); options.setSessionTrackingIntervalMillis(properties.getSessionTrackingIntervalMillis()); // In-app excludes options.setInAppIncludes(properties.getInAppIncludes()); options.setInAppExcludes(properties.getInAppExcludes()); // Transport configuration options.setEnableShutdownHook(properties.isEnableShutdownHook()); options.setFlushTimeoutMillis(properties.getFlushTimeoutMillis()); // Before send callback for filtering options.setBeforeSend((event, hint) -> { // Filter out health check events if (event.getTransaction() != null && event.getTransaction().contains("health")) { return null; } return event; }); // Before breadcrumb callback options.setBeforeBreadcrumb((breadcrumb, hint) -> { // Filter out noisy breadcrumbs if ("http".equals(breadcrumb.getCategory()) && breadcrumb.getData().containsKey("url") && breadcrumb.getData().get("url").toString().contains("actuator")) { return null; } return breadcrumb; }); logger.info("Sentry initialized for environment: {}", properties.getEnvironment()); return options; } catch (Exception e) { logger.error("Failed to initialize Sentry", e); throw new RuntimeException("Sentry initialization failed", e); } } @Bean @ConditionalOnMissingBean public SentryTransactionPerformanceCollector sentryPerformanceCollector() { return new SentryTransactionPerformanceCollector(); } } @ConfigurationProperties(prefix = "sentry") public class SentryProperties { private boolean enabled = true; private String dsn; private String environment = "development"; private String release = "1.0.0"; private boolean debug = false; // Performance monitoring private double tracesSampleRate = 1.0; private double profilesSampleRate = 0.0; private boolean enableTracing = true; private List<String> tracePropagationTargets = Arrays.asList(".*"); // Session tracking private boolean enableSessionTracking = true; private long sessionTrackingIntervalMillis = 30000; // Application boundaries private List<String> inAppIncludes = Arrays.asList("com.yourcompany"); private List<String> inAppExcludes = Arrays.asList("org.springframework", "ch.qos.logback"); // Transport settings private boolean enableShutdownHook = true; private long flushTimeoutMillis = 15000; // Getters and setters public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getDsn() { return dsn; } public void setDsn(String dsn) { this.dsn = dsn; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { this.environment = environment; } public String getRelease() { return release; } public void setRelease(String release) { this.release = release; } public boolean isDebug() { return debug; } public void setDebug(boolean debug) { this.debug = debug; } public double getTracesSampleRate() { return tracesSampleRate; } public void setTracesSampleRate(double tracesSampleRate) { this.tracesSampleRate = tracesSampleRate; } public double getProfilesSampleRate() { return profilesSampleRate; } public void setProfilesSampleRate(double profilesSampleRate) { this.profilesSampleRate = profilesSampleRate; } public boolean isEnableTracing() { return enableTracing; } public void setEnableTracing(boolean enableTracing) { this.enableTracing = enableTracing; } public List<String> getTracePropagationTargets() { return tracePropagationTargets; } public void setTracePropagationTargets(List<String> tracePropagationTargets) { this.tracePropagationTargets = tracePropagationTargets; } public boolean isEnableSessionTracking() { return enableSessionTracking; } public void setEnableSessionTracking(boolean enableSessionTracking) { this.enableSessionTracking = enableSessionTracking; } public long getSessionTrackingIntervalMillis() { return sessionTrackingIntervalMillis; } public void setSessionTrackingIntervalMillis(long sessionTrackingIntervalMillis) { this.sessionTrackingIntervalMillis = sessionTrackingIntervalMillis; } public List<String> getInAppIncludes() { return inAppIncludes; } public void setInAppIncludes(List<String> inAppIncludes) { this.inAppIncludes = inAppIncludes; } public List<String> getInAppExcludes() { return inAppExcludes; } public void setInAppExcludes(List<String> inAppExcludes) { this.inAppExcludes = inAppExcludes; } public boolean isEnableShutdownHook() { return enableShutdownHook; } public void setEnableShutdownHook(boolean enableShutdownHook) { this.enableShutdownHook = enableShutdownHook; } public long getFlushTimeoutMillis() { return flushTimeoutMillis; } public void setFlushTimeoutMillis(long flushTimeoutMillis) { this.flushTimeoutMillis = flushTimeoutMillis; } }

Manual Transaction Instrumentation

Custom Transaction Management

@Service public class OrderProcessingService { private final ITransaction transaction; public OrderProcessingService() { this.transaction = Sentry.startTransaction("Order Processing", "task"); } public Order processOrder(OrderRequest request) { // Set transaction context transaction.setOperation("order.process"); transaction.setData("order.id", request.getOrderId()); transaction.setData("customer.id", request.getCustomerId()); ISpan processSpan = transaction.startChild("process"); try { // Process order steps with spans validateOrder(request); reserveInventory(request); processPayment(request); sendConfirmation(request); // Mark as successful processSpan.setStatus(SpanStatus.OK); transaction.setStatus(SpanStatus.OK); return createOrderResponse(request); } catch (Exception e) { // Record failure processSpan.setThrowable(e); processSpan.setStatus(SpanStatus.INTERNAL_ERROR); transaction.setThrowable(e); transaction.setStatus(SpanStatus.INTERNAL_ERROR); throw e; } finally { processSpan.finish(); transaction.finish(); } } private void validateOrder(OrderRequest request) { ISpan span = transaction.startChild("validation"); span.setDescription("Validate order requirements"); try { if (request.getAmount() <= 0) { throw new ValidationException("Invalid order amount"); } // Simulate validation logic Thread.sleep(50); span.setStatus(SpanStatus.OK); } catch (InterruptedException e) { Thread.currentThread().interrupt(); span.setThrowable(e); span.setStatus(SpanStatus.INTERNAL_ERROR); throw new RuntimeException("Validation interrupted", e); } catch (Exception e) { span.setThrowable(e); span.setStatus(SpanStatus.INVALID_ARGUMENT); throw e; } finally { span.finish(); } } private void processPayment(OrderRequest request) { ISpan span = transaction.startChild("payment"); span.setOperation("payment.process"); span.setData("payment.amount", request.getAmount()); span.setData("payment.method", request.getPaymentMethod()); try { boolean success = paymentGateway.charge(request); if (!success) { throw new PaymentException("Payment processing failed"); } span.setStatus(SpanStatus.OK); } catch (Exception e) { span.setThrowable(e); span.setStatus(SpanStatus.FAILED_PRECONDITION); throw e; } finally { span.finish(); } } }

Spring Boot Integration

Web Request Tracing

@Component public class SentryPerformanceFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(SentryPerformanceFilter.class); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { chain.doFilter(request, response); return; } HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; // Start transaction for this request String transactionName = httpRequest.getMethod() + " " + httpRequest.getRequestURI(); ITransaction transaction = Sentry.startTransaction(transactionName, "http.server"); // Set transaction context transaction.setOperation("http.server"); transaction.setData("http.method", httpRequest.getMethod()); transaction.setData("http.url", httpRequest.getRequestURL().toString()); transaction.setData("http.query_string", httpRequest.getQueryString()); transaction.setData("http.user_agent", httpRequest.getHeader("User-Agent")); try { chain.doFilter(request, response); // Record successful completion transaction.setData("http.status_code", httpResponse.getStatus()); transaction.setStatus(SpanStatus.OK); } catch (Exception e) { // Record failure transaction.setThrowable(e); transaction.setStatus(SpanStatus.INTERNAL_ERROR); throw e; } finally { transaction.finish(); } } } @Configuration public class SentryWebConfiguration { @Bean public FilterRegistrationBean<SentryPerformanceFilter> sentryPerformanceFilter() { FilterRegistrationBean<SentryPerformanceFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new SentryPerformanceFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return registrationBean; } @Bean public SentryRequestListener sentryRequestListener() { return new SentryRequestListener(); } } @Component public class SentryRequestListener { @EventListener public void handleRequestEvent(ServletRequestHandledEvent event) { // Capture request timing metrics Map<String, Object> data = new HashMap<>(); data.put("request_url", event.getRequestUrl()); data.put("client_address", event.getClientAddress()); data.put("method", event.getMethod()); data.put("processing_time_ms", event.getProcessingTimeMillis()); Sentry.addBreadcrumb(Breadcrumb.http( event.getRequestUrl(), event.getMethod(), (Integer) event.getStatusCode() )); // Record custom metric Sentry.metrics().timing("http.request.duration", event.getProcessingTimeMillis(), TimeUnit.MILLISECONDS, Map.of( "method", event.getMethod(), "status", String.valueOf(event.getStatusCode()), "url", event.getRequestUrl() )); } }

Database Performance Monitoring

JDBC Integration

@Configuration public class SentryJdbcConfig { @Bean @Primary public DataSource dataSource(SentryProperties properties) { DataSource realDataSource = createRealDataSource(); if (properties.isEnabled() && properties.isEnableTracing()) { return SentryDataSource.create(realDataSource); } return realDataSource; } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } } @Aspect @Component public class RepositoryPerformanceAspect { private static final Logger logger = LoggerFactory.getLogger(RepositoryPerformanceAspect.class); @Around("execution(* org.springframework.data.repository.Repository+.*(..))") public Object traceRepositoryMethods(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); String repositoryName = joinPoint.getTarget().getClass().getSimpleName(); ISpan span = Sentry.startSpan("db.repository", repositoryName + "." + methodName); try { Object result = joinPoint.proceed(); // Record result metrics if (result instanceof Collection) { span.setData("result.count", ((Collection<?>) result).size()); } span.setStatus(SpanStatus.OK); return result; } catch (Exception e) { span.setThrowable(e); span.setStatus(SpanStatus.INTERNAL_ERROR); throw e; } finally { span.finish(); } } } @Service public class OrderRepositoryService { private final JdbcTemplate jdbcTemplate; public OrderRepositoryService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @SentryTransaction(operation = "db.query") public List<Order> findOrdersByCustomer(String customerId, Date startDate, Date endDate) { String sql = """ SELECT * FROM orders WHERE customer_id = ? AND created_date BETWEEN ? AND ? ORDER BY created_date DESC """; // This query will be automatically traced by Sentry JDBC integration return jdbcTemplate.query(sql, new OrderRowMapper(), customerId, startDate, endDate); } @SentryTransaction(operation = "db.update") public void updateOrderStatus(String orderId, String status) { String sql = "UPDATE orders SET status = ? WHERE id = ?"; // This update will be automatically traced jdbcTemplate.update(sql, status, orderId); // Add custom breadcrumb Sentry.addBreadcrumb(Breadcrumb.info( "Order status updated", Map.of("order_id", orderId, "new_status", status) )); } }

Custom Performance Monitoring

Business Transaction Monitoring

@Component public class BusinessTransactionMonitor { public <T> T monitorBusinessTransaction(String transactionName, String operation, Supplier<T> operationLogic) { ITransaction transaction = Sentry.startTransaction(transactionName, "business"); transaction.setOperation(operation); try { T result = operationLogic.get(); transaction.setStatus(SpanStatus.OK); return result; } catch (Exception e) { transaction.setThrowable(e); transaction.setStatus(SpanStatus.INTERNAL_ERROR); throw e; } finally { transaction.finish(); } } public void monitorAsyncOperation(String operationName, CompletableFuture<?> future) { ISpan span = Sentry.startSpan("async", operationName); future.whenComplete((result, throwable) -> { if (throwable != null) { span.setThrowable(throwable); span.setStatus(SpanStatus.INTERNAL_ERROR); } else { span.setStatus(SpanStatus.OK); } span.finish(); }); } } @Service public class OrderService { private final BusinessTransactionMonitor monitor; public OrderService(BusinessTransactionMonitor monitor) { this.monitor = monitor; } public Order processComplexOrder(ComplexOrderRequest request) { return monitor.monitorBusinessTransaction( "Complex Order Processing", "order.complex.process", () -> { // Complex business logic with multiple steps validateComplexOrder(request); calculatePricing(request); applyDiscounts(request); processMultiplePayments(request); scheduleFulfillment(request); return createComplexOrderResponse(request); }); } public CompletableFuture<Order> processOrderAsync(OrderRequest request) { CompletableFuture<Order> future = CompletableFuture.supplyAsync(() -> { return processOrder(request); }); monitor.monitorAsyncOperation("async.order.process", future); return future; } }

Performance Metrics Collection

Custom Metrics and Monitoring

@Component public class OrderPerformanceMetrics { private final Timer orderProcessingTimer; private final Counter orderSuccessCounter; private final Counter orderFailureCounter; private final Gauge inventoryLevelGauge; public OrderPerformanceMetrics() { // Initialize metrics this.orderProcessingTimer = Sentry.metrics().timer("order.processing.duration"); this.orderSuccessCounter = Sentry.metrics().counter("order.success.total"); this.orderFailureCounter = Sentry.metrics().counter("order.failure.total"); this.inventoryLevelGauge = Sentry.metrics().gauge("inventory.level"); } public void recordOrderSuccess(long processingTimeMs, String orderType, String customerTier) { orderSuccessCounter.increment(1, Map.of( "order_type", orderType, "customer_tier", customerTier )); orderProcessingTimer.record(processingTimeMs, TimeUnit.MILLISECONDS, Map.of( "status", "success", "order_type", orderType )); // Record custom event for business analytics Sentry.captureMessage( String.format("Order processed successfully in %d ms", processingTimeMs), SentryLevel.INFO, scope -> { scope.setContexts("order", Map.of( "processing_time_ms", processingTimeMs, "type", orderType, "customer_tier", customerTier )); } ); } public void recordOrderFailure(long processingTimeMs, String orderType, String errorReason) { orderFailureCounter.increment(1, Map.of( "order_type", orderType, "error_reason", errorReason )); orderProcessingTimer.record(processingTimeMs, TimeUnit.MILLISECONDS, Map.of( "status", "failure", "order_type", orderType )); } public void updateInventoryMetrics(Map<String, Integer> inventoryLevels) { inventoryLevels.forEach((productId, level) -> { inventoryLevelGauge.set(level, Map.of("product_id", productId)); }); } public void recordSlowOperation(String operation, long durationMs, Map<String, String> tags) { if (durationMs > 1000) { // Threshold for slow operations Sentry.captureMessage( String.format("Slow operation detected: %s took %d ms", operation, durationMs), SentryLevel.WARNING, scope -> { scope.setContexts("performance", Map.of( "operation", operation, "duration_ms", durationMs, "tags", tags )); } ); } } }

Distributed Tracing

Cross-Service Tracing

@Component public class DistributedTracingService { public <T> T executeWithContextPropagation(String operation, Supplier<T> operationLogic) { // Get current Sentry trace context TraceContext traceContext = Sentry.getTraceContext(); if (traceContext != null) { // Propagate trace context to downstream services propagateTraceContext(traceContext); } ISpan span = Sentry.startSpan("rpc", operation); try { T result = operationLogic.get(); span.setStatus(SpanStatus.OK); return result; } catch (Exception e) { span.setThrowable(e); span.setStatus(SpanStatus.INTERNAL_ERROR); throw e; } finally { span.finish(); } } private void propagateTraceContext(TraceContext traceContext) { // Extract trace headers for propagation Map<String, String> traceHeaders = new HashMap<>(); if (traceContext.getTraceId() != null) { traceHeaders.put("sentry-trace", traceContext.getTraceId()); } if (traceContext.getBaggage() != null) { traceHeaders.put("baggage", traceContext.getBaggage()); } // Store in thread-local for HTTP clients to pick up SentryTraceHeaderStorage.setHeaders(traceHeaders); } } @Component public class TracingRestTemplateInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // Get trace headers from storage Map<String, String> traceHeaders = SentryTraceHeaderStorage.getHeaders(); if (traceHeaders != null) { traceHeaders.forEach(request.getHeaders()::set); } // Start span for outgoing request ISpan span = Sentry.startSpan("http.client", request.getMethod() + " " + request.getURI().getHost()); span.setData("http.method", request.getMethod().name()); span.setData("http.url", request.getURI().toString()); try { ClientHttpResponse response = execution.execute(request, body); span.setData("http.status_code", response.getRawStatusCode()); span.setStatus(SpanStatus.OK); return response; } catch (IOException e) { span.setThrowable(e); span.setStatus(SpanStatus.INTERNAL_ERROR); throw e; } finally { span.finish(); } } } // Thread-local storage for trace headers public class SentryTraceHeaderStorage { private static final ThreadLocal<Map<String, String>> TRACE_HEADERS = new ThreadLocal<>(); public static void setHeaders(Map<String, String> headers) { TRACE_HEADERS.set(headers); } public static Map<String, String> getHeaders() { return TRACE_HEADERS.get(); } public static void clear() { TRACE_HEADERS.remove(); } }

Error Tracking with Performance Context

Enhanced Error Reporting

@Service public class ErrorTrackingService { public void trackErrorWithPerformanceContext(String errorMessage, Throwable throwable) { ITransaction currentTransaction = Sentry.getCurrentTransaction(); Sentry.withScope(scope -> { // Add performance context to error if (currentTransaction != null) { scope.setContexts("performance", Map.of( "transaction_id", currentTransaction.getEventId(), "transaction_name", currentTransaction.getName(), "transaction_operation", currentTransaction.getOperation() )); } // Add custom fingerprints for grouping scope.setFingerprints(Arrays.asList( errorMessage, throwable.getClass().getSimpleName() )); // Capture the exception Sentry.captureException(throwable); }); } public void trackBusinessError(String errorCode, String message, Map<String, Object> context) { Sentry.withScope(scope -> { scope.setTag("error_type", "business"); scope.setTag("error_code", errorCode); scope.setContexts("business", context); scope.setLevel(SentryLevel.WARNING); Sentry.captureMessage(message); }); } } @ControllerAdvice public class GlobalExceptionHandler { private final ErrorTrackingService errorTrackingService; public GlobalExceptionHandler(ErrorTrackingService errorTrackingService) { this.errorTrackingService = errorTrackingService; } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleException(Exception e, HttpServletRequest request) { // Track error with performance context errorTrackingService.trackErrorWithPerformanceContext( "Unhandled exception in web request", e); // Create error response ErrorResponse errorResponse = new ErrorResponse( "INTERNAL_ERROR", "An unexpected error occurred", request.getRequestURI() ); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(errorResponse); } @ExceptionHandler(BusinessException.class) public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) { // Track business error errorTrackingService.trackBusinessError( e.getErrorCode(), e.getMessage(), e.getContext() ); ErrorResponse errorResponse = new ErrorResponse( e.getErrorCode(), e.getMessage(), null ); return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(errorResponse); } }

Configuration Examples

application.yml

sentry: enabled: true dsn: ${SENTRY_DSN:} environment: ${ENVIRONMENT:development} release: ${APP_VERSION:1.0.0} debug: false traces-sample-rate: 0.5 profiles-sample-rate: 0.1 enable-tracing: true enable-session-tracking: true session-tracking-interval-millis: 30000 in-app-includes: - "com.yourcompany" - "org.yourproject" in-app-excludes: - "org.springframework" - "ch.qos.logback" - "io.sentry" trace-propagation-targets: - ".*\\.yourcompany\\.com" - ".*\\.yourapi\\.com" logging: level: io.sentry: INFO

Programmatic Configuration

@Configuration @Profile("production") public class ProductionSentryConfig { @Bean public SentryOptions customSentryOptions() { SentryOptions options = new SentryOptions(); options.setDsn(System.getenv("SENTRY_DSN")); options.setEnvironment("production"); options.setRelease(System.getenv("APP_VERSION")); // Sample 10% of transactions in production options.setTracesSampleRate(0.1); options.setProfilesSampleRate(0.05); // Custom tags for all events options.setTag("data_center", System.getenv("DATA_CENTER")); options.setTag("service_instance", System.getenv("HOSTNAME")); // Custom transport for high throughput options.setTransportFactory(new AsyncHttpTransportFactory()); return options; } }

Testing and Development

Test Configuration

@SpringBootTest @TestPropertySource(properties = { "sentry.enabled=false", "sentry.dsn=http://test@localhost/1" }) public abstract class BaseIntegrationTest { @MockBean protected IHub sentryHub; @BeforeEach void setupSentryMocks() { // Mock Sentry behavior for tests when(sentryHub.isEnabled()).thenReturn(false); when(sentryHub.captureTransaction(any(Transaction.class))).thenReturn(SentryId.EMPTY_ID); } } @Configuration @Profile("test") public class TestSentryConfig { @Bean @Primary public IHub mockSentryHub() { return new NoOpHub(); } } @Component @Primary @Profile("test") public class NoOpPerformanceMonitor extends BusinessTransactionMonitor { @Override public <T> T monitorBusinessTransaction(String transactionName, String operation, Supplier<T> operationLogic) { return operationLogic.get(); } }

Performance Analysis Utilities

@Component public class PerformanceAnalyzer { private final Map<String, PerformanceStats> stats = new ConcurrentHashMap<>(); public void recordOperation(String operation, long durationMs, boolean success) { stats.computeIfAbsent(operation, k -> new PerformanceStats()) .recordOperation(durationMs, success); } public PerformanceReport generateReport() { Map<String, OperationStats> operationStats = new HashMap<>(); stats.forEach((operation, stat) -> { operationStats.put(operation, stat.getStats()); }); return new PerformanceReport(operationStats); } public void reportSlowOperations() { stats.forEach((operation, stat) -> { OperationStats opStats = stat.getStats(); if (opStats.getP95() > 1000) { // 1 second threshold Sentry.captureMessage( String.format("Slow operation detected: %s (p95: %d ms)", operation, opStats.getP95()), SentryLevel.WARNING ); } }); } private static class PerformanceStats { private final List<Long> durations = new CopyOnWriteArrayList<>(); private final AtomicLong successCount = new AtomicLong(); private final AtomicLong failureCount = new AtomicLong(); public void recordOperation(long durationMs, boolean success) { durations.add(durationMs); if (success) { successCount.incrementAndGet(); } else { failureCount.incrementAndGet(); } } public OperationStats getStats() { if (durations.isEmpty()) { return new OperationStats(0, 0, 0, 0, 0); } List<Long> sorted = new ArrayList<>(durations); Collections.sort(sorted); return new OperationStats( sorted.get(0), // min sorted.get(sorted.size() - 1), // max sorted.get(sorted.size() / 2), // median calculatePercentile(sorted, 95), // p95 successCount.get() + failureCount.get() // total ); } private long calculatePercentile(List<Long> sorted, double percentile) { int index = (int) Math.ceil(percentile / 100.0 * sorted.size()); return sorted.get(Math.min(index, sorted.size() - 1)); } } }

Conclusion

This Sentry Performance implementation for Java provides:

  1. Comprehensive Tracing - Automatic and manual transaction monitoring
  2. Spring Boot Integration - Seamless Spring ecosystem integration
  3. Database Performance - JDBC and repository-level performance monitoring
  4. Business Metrics - Custom metrics for business transactions
  5. Distributed Tracing - Cross-service context propagation
  6. Error Correlation - Link errors with performance data
  7. Custom Monitoring - Business-specific performance tracking
  8. Testing Support - Mock implementations for testing environments

The implementation enables full-stack performance monitoring with minimal code changes while providing deep insights into application performance bottlenecks and user experience impacts.

Leave a Reply

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


Macro Nepal Helper