Honeycomb.io Beeline for Java: Complete Guide

Introduction to Honeycomb Beeline

Honeycomb Beeline is a lightweight instrumentation library that makes it easy to collect and send observability data to Honeycomb.io. It provides automatic instrumentation for common Java frameworks and simplifies adding custom telemetry to your applications.


System Architecture Overview

Honeycomb Beeline Pipeline ├── Automatic Instrumentation │ ├ - HTTP Servers (Servlet, Spring MVC) │ ├ - HTTP Clients (OkHttp, Apache HttpClient) │ ├ - Database Calls (JDBC) │ ├ - Messaging (JMS, Kafka) │ └ - Async Processing ├── Custom Instrumentation │ ├ - Business Metrics │ ├ - Custom Spans │ ├ - Trace Context Propagation │ └ - Log Correlation ├── Data Enrichment │ ├ - Global Fields │ ├ - Dynamic Sampling │ └ - Custom Processors └── Honeycomb Backend ├ - Trace Visualization ├ - Query Interface ├ - Trigger Alerts └ - Data Analysis

Core Implementation

1. Maven Dependencies

<properties> <honeycomb.beeline.version>1.4.0</honeycomb.beeline.version> <opentelemetry.version>1.28.0</opentelemetry.version> <spring.boot.version>2.7.0</spring.boot.version> </properties> <dependencies> <!-- Honeycomb Beeline --> <dependency> <groupId>io.honeycomb.beeline</groupId> <artifactId>beeline</artifactId> <version>${honeycomb.beeline.version}</version> </dependency> <!-- Beeline Auto-Instrumentation --> <dependency> <groupId>io.honeycomb.beeline</groupId> <artifactId>beeline-spring-boot-starter</artifactId> <version>${honeycomb.beeline.version}</version> </dependency> <dependency> <groupId>io.honeycomb.beeline</groupId> <artifactId>beeline-servlet</artifactId> <version>${honeycomb.beeline.version}</version> </dependency> <!-- OpenTelemetry (Beeline uses OTel under the hood) --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>${spring.boot.version}</version> </dependency> <!-- Database --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <!-- HTTP Client --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.14</version> </dependency> </dependencies>

2. Configuration Setup

import io.honeycomb.beeline.autoconfigure.BeelineAutoConfiguration; import io.honeycomb.beeline.tracing.Beeline; import io.honeycomb.beeline.tracing.Span; import io.honeycomb.beeline.tracing.Tracer; import io.honeycomb.beeline.tracing.Tracing; import io.honeycomb.beeline.tracing.propagation.Propagation; import io.honeycomb.beeline.tracing.sampling.Sampling; import io.honeycomb.libhoney.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; @Configuration public class HoneycombConfig { @Value("${honeycomb.api.key:}") private String honeycombApiKey; @Value("${honeycomb.dataset:my-java-service}") private String honeycombDataset; @Value("${honeycomb.service.name:order-service}") private String serviceName; @Bean public Beeline beeline() { // Configure Honeycomb client final HoneyClient honeyClient = LibHoney.create( Options.builder() .setWriteKey(honeycombApiKey) .setDataset(honeycombDataset) .setApiHost("https://api.honeycomb.io") // or your Honeycomb endpoint .build() ); // Configure Beeline with global fields final Beeline beeline = new Beeline( honeyClient, Tracing.createTracer( Propagation.createFactory(), Sampling.alwaysSampler(), serviceName ) ); // Add global fields to all events final Map<String, Object> globalFields = new HashMap<>(); globalFields.put("environment", System.getenv().getOrDefault("ENVIRONMENT", "development")); globalFields.put("service.version", System.getenv().getOrDefault("SERVICE_VERSION", "1.0.0")); globalFields.put("deployment.region", System.getenv().getOrDefault("DEPLOYMENT_REGION", "us-east-1")); honeyClient.addGlobalFields(globalFields); return beeline; } @Bean public Tracer tracer(Beeline beeline) { return beeline.getTracer(); } }

3. Application Properties

# application.yml honeycomb: api-key: ${HONEYCOMB_API_KEY:your-api-key-here} dataset: ${HONEYCOMB_DATASET:my-java-service} service: name: ${SERVICE_NAME:order-service} # Beeline Configuration beeline: enabled: true # Sampling configuration sampler: enabled: true sample-rate: 1 # 100% sampling in development # HTTP instrumentation http: include-request-headers: "User-Agent,Content-Type,Accept" include-response-headers: "Content-Type,Content-Length" # SQL instrumentation sql: enabled: true include-query: true # Logging correlation logging: pattern: level: "%5p [trace_id=%X{trace_id},span_id=%X{span_id}]" management: endpoints: web: exposure: include: health,info,metrics,prometheus

4. Spring Boot Auto-Instrumentation

import io.honeycomb.beeline.autoconfigure.BeelineProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableConfigurationProperties(BeelineProperties.class) public class BeelineAutoConfig implements WebMvcConfigurer { private final Beeline beeline; public BeelineAutoConfig(Beeline beeline) { this.beeline = beeline; } @Override public void addInterceptors(InterceptorRegistry registry) { // Beeline automatically adds interceptors for Spring MVC } @Bean public BeelineHttpServletFilter beelineHttpServletFilter() { return new BeelineHttpServletFilter(beeline); } }

5. Custom Instrumentation Service

import io.honeycomb.beeline.tracing.Beeline; import io.honeycomb.beeline.tracing.Span; import io.honeycomb.beeline.tracing.Tracer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @Service public class HoneycombInstrumentationService { private static final Logger LOGGER = LoggerFactory.getLogger(HoneycombInstrumentationService.class); private final Beeline beeline; private final Tracer tracer; public HoneycombInstrumentationService(Beeline beeline) { this.beeline = beeline; this.tracer = beeline.getTracer(); } /** * Start a custom span for business operations */ public Span startBusinessSpan(String operationName) { return tracer.startSpan(operationName) .addField("span.type", "business_operation") .addField("service.name", "order-service"); } /** * Instrument a runnable operation with tracing */ public void instrumentOperation(String operationName, Runnable operation) { Span span = startBusinessSpan(operationName); try { operation.run(); span.addField("operation.success", true); } catch (Exception e) { span.addField("operation.success", false); span.addField("error.message", e.getMessage()); throw e; } finally { span.close(); } } /** * Instrument a callable operation with tracing */ public <T> T instrumentOperation(String operationName, Callable<T> operation) { Span span = startBusinessSpan(operationName); try { T result = operation.call(); span.addField("operation.success", true); span.addField("result.type", result.getClass().getSimpleName()); return result; } catch (Exception e) { span.addField("operation.success", false); span.addField("error.message", e.getMessage()); throw new RuntimeException(e); } finally { span.close(); } } /** * Add custom fields to current span */ public void addField(String key, Object value) { Span currentSpan = tracer.getActiveSpan(); if (currentSpan != null) { currentSpan.addField(key, value); } } /** * Record business metrics */ public void recordBusinessMetric(String metricName, double value, Map<String, Object> dimensions) { Span span = tracer.startSpan("business.metric") .addField("metric.name", metricName) .addField("metric.value", value) .addField("metric.type", "gauge"); if (dimensions != null) { dimensions.forEach(span::addField); } span.close(); } /** * Record timing information */ public void recordTiming(String operation, long durationMs) { Span span = tracer.startSpan("timing.measurement") .addField("operation", operation) .addField("duration_ms", durationMs) .addField("measurement.type", "timing"); span.close(); } /** * Async operation instrumentation */ public <T> CompletableFuture<T> instrumentAsyncOperation( String operationName, Supplier<CompletableFuture<T>> asyncOperation) { // Capture current trace context Map<String, String> traceContext = tracer.getActiveSpan().getTraceContext(); Span parentSpan = tracer.startSpan(operationName + ".async") .addField("operation.type", "async") .addField("async.trigger", "true"); return asyncOperation.get() .whenComplete((result, throwable) -> { // Restore trace context in async thread try (Span childSpan = tracer.startChildSpan(parentSpan, operationName + ".completion")) { childSpan.addField("async.completed", true); if (throwable != null) { childSpan.addField("async.success", false); childSpan.addField("error.message", throwable.getMessage()); } else { childSpan.addField("async.success", true); if (result != null) { childSpan.addField("result.present", true); } } } parentSpan.close(); }); } /** * Database query instrumentation */ public void instrumentQuery(String queryType, String table, Runnable query) { Span span = tracer.startSpan("database.query") .addField("db.system", "postgresql") .addField("db.operation", queryType) .addField("db.collection", table) .addField("span.type", "database"); long startTime = System.currentTimeMillis(); try { query.run(); long duration = System.currentTimeMillis() - startTime; span.addField("db.duration_ms", duration); span.addField("db.success", true); } catch (Exception e) { span.addField("db.success", false); span.addField("db.error", e.getMessage()); throw e; } finally { span.close(); } } /** * External service call instrumentation */ public <T> T instrumentExternalCall(String serviceName, String endpoint, Supplier<T> call) { Span span = tracer.startSpan("external.service.call") .addField("service.name", serviceName) .addField("service.endpoint", endpoint) .addField("span.type", "http_client"); long startTime = System.currentTimeMillis(); try { T result = call.get(); long duration = System.currentTimeMillis() - startTime; span.addField("http.duration_ms", duration); span.addField("http.success", true); span.addField("http.status_code", 200); // Assuming success return result; } catch (Exception e) { long duration = System.currentTimeMillis() - startTime; span.addField("http.duration_ms", duration); span.addField("http.success", false); span.addField("http.error", e.getMessage()); throw e; } finally { span.close(); } } /** * Add user context to traces */ public void addUserContext(String userId, String userRole) { Span currentSpan = tracer.getActiveSpan(); if (currentSpan != null) { currentSpan.addField("user.id", userId); currentSpan.addField("user.role", userRole); currentSpan.addField("user.authenticated", true); } } /** * Add request context to traces */ public void addRequestContext(String requestId, String clientIp, String userAgent) { Span currentSpan = tracer.getActiveSpan(); if (currentSpan != null) { currentSpan.addField("request.id", requestId); currentSpan.addField("client.ip", clientIp); currentSpan.addField("user_agent", userAgent); } } }

6. Instrumented REST Controller

import io.honeycomb.beeline.tracing.Span; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @RestController @RequestMapping("/api/orders") public class OrderController { private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class); private final OrderService orderService; private final PaymentService paymentService; private final HoneycombInstrumentationService instrumentation; public OrderController(OrderService orderService, PaymentService paymentService, HoneycombInstrumentationService instrumentation) { this.orderService = orderService; this.paymentService = paymentService; this.instrumentation = instrumentation; } @PostMapping public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) { LOGGER.info("Creating order for customer: {}", request.getCustomerId()); // Add user context to trace instrumentation.addUserContext(request.getCustomerId(), "customer"); instrumentation.addField("order.amount", request.getAmount()); instrumentation.addField("order.items.count", request.getItems().size()); return instrumentation.instrumentOperation("order.creation", () -> { Order order = orderService.createOrder(request); // Instrument payment processing instrumentation.instrumentExternalCall("payment-service", "/api/payments", () -> paymentService.processPayment(order)); LOGGER.info("Order created successfully: {}", order.getId()); return ResponseEntity.ok(order); }); } @GetMapping("/{orderId}") public ResponseEntity<Order> getOrder(@PathVariable String orderId) { LOGGER.debug("Fetching order: {}", orderId); instrumentation.addField("order.id", orderId); return instrumentation.instrumentOperation("order.retrieval", () -> { Order order = orderService.getOrder(orderId); if (order == null) { instrumentation.addField("order.found", false); return ResponseEntity.notFound().build(); } instrumentation.addField("order.found", true); instrumentation.addField("order.status", order.getStatus()); return ResponseEntity.ok(order); }); } @GetMapping("/customer/{customerId}") public ResponseEntity<List<Order>> getCustomerOrders(@PathVariable String customerId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { instrumentation.addUserContext(customerId, "customer"); instrumentation.addField("pagination.page", page); instrumentation.addField("pagination.size", size); return instrumentation.instrumentOperation("customer.orders.retrieval", () -> { List<Order> orders = orderService.getOrdersByCustomer(customerId, page, size); instrumentation.addField("orders.count", orders.size()); instrumentation.recordBusinessMetric("customer.orders.count", orders.size(), Map.of("customer.id", customerId)); return ResponseEntity.ok(orders); }); } @PostMapping("/{orderId}/cancel") public ResponseEntity<Order> cancelOrder(@PathVariable String orderId) { LOGGER.info("Cancelling order: {}", orderId); instrumentation.addField("order.id", orderId); return instrumentation.instrumentOperation("order.cancellation", () -> { Order order = orderService.cancelOrder(orderId); // Async notification instrumentation.instrumentAsyncOperation("order.cancellation.notification", () -> orderService.notifyCancellationAsync(order)); instrumentation.recordBusinessMetric("order.cancelled", 1, Map.of("order.id", orderId, "reason", "user_requested")); return ResponseEntity.ok(order); }); } @GetMapping("/{orderId}/analytics") public CompletableFuture<ResponseEntity<OrderAnalytics>> getOrderAnalytics(@PathVariable String orderId) { LOGGER.debug("Fetching analytics for order: {}", orderId); instrumentation.addField("order.id", orderId); return instrumentation.instrumentAsyncOperation("order.analytics.computation", () -> orderService.computeOrderAnalyticsAsync(orderId)) .thenApply(ResponseEntity::ok); } @PutMapping("/{orderId}/status") public ResponseEntity<Order> updateOrderStatus(@PathVariable String orderId, @RequestBody StatusUpdate update) { LOGGER.info("Updating order status: {} -> {}", orderId, update.getStatus()); instrumentation.addField("order.id", orderId); instrumentation.addField("status.old", update.getPreviousStatus()); instrumentation.addField("status.new", update.getNewStatus()); return instrumentation.instrumentOperation("order.status.update", () -> { long startTime = System.currentTimeMillis(); Order order = orderService.updateOrderStatus(orderId, update); long duration = System.currentTimeMillis() - startTime; instrumentation.recordTiming("order.status.update", duration); // Record status change metric instrumentation.recordBusinessMetric("order.status.change", 1, Map.of("from_status", update.getPreviousStatus(), "to_status", update.getNewStatus(), "order.id", orderId)); return ResponseEntity.ok(order); }); } }

7. Database Instrumentation

import org.springframework.stereotype.Repository; import org.springframework.beans.factory.annotation.Autowired; import javax.persistence.EntityManager; import javax.persistence.Query; import java.util.List; @Repository public class InstrumentedOrderRepository { private final EntityManager entityManager; private final HoneycombInstrumentationService instrumentation; public InstrumentedOrderRepository(EntityManager entityManager, HoneycombInstrumentationService instrumentation) { this.entityManager = entityManager; this.instrumentation = instrumentation; } public Order findById(String orderId) { return instrumentation.instrumentOperation("database.order.find_by_id", () -> { instrumentation.instrumentQuery("SELECT", "orders", () -> LOGGER.debug("Executing find by ID query for order: {}", orderId)); return entityManager.find(Order.class, orderId); }); } public List<Order> findByCustomerId(String customerId, int page, int size) { return instrumentation.instrumentOperation("database.order.find_by_customer", () -> { instrumentation.instrumentQuery("SELECT", "orders", () -> LOGGER.debug("Finding orders for customer: {}", customerId)); Query query = entityManager.createQuery( "SELECT o FROM Order o WHERE o.customerId = :customerId ORDER BY o.createdAt DESC"); query.setParameter("customerId", customerId); query.setFirstResult(page * size); query.setMaxResults(size); return query.getResultList(); }); } public Order save(Order order) { return instrumentation.instrumentOperation("database.order.save", () -> { String operation = order.getId() == null ? "INSERT" : "UPDATE"; instrumentation.instrumentQuery(operation, "orders", () -> LOGGER.debug("Saving order: {}", order.getId())); if (order.getId() == null) { entityManager.persist(order); } else { order = entityManager.merge(order); } return order; }); } public int updateOrderStatus(String orderId, String status) { return instrumentation.instrumentOperation("database.order.update_status", () -> { instrumentation.instrumentQuery("UPDATE", "orders", () -> LOGGER.debug("Updating order status: {} -> {}", orderId, status)); Query query = entityManager.createQuery( "UPDATE Order o SET o.status = :status WHERE o.id = :orderId"); query.setParameter("status", status); query.setParameter("orderId", orderId); return query.executeUpdate(); }); } }

8. HTTP Client Instrumentation

import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Map; @Service public class InstrumentedHttpClient { private final RestTemplate restTemplate; private final HoneycombInstrumentationService instrumentation; public InstrumentedHttpClient(RestTemplate restTemplate, HoneycombInstrumentationService instrumentation) { this.restTemplate = restTemplate; this.instrumentation = instrumentation; } public <T> ResponseEntity<T> executeWithTracing(String serviceName, String url, HttpMethod method, Object requestBody, Class<T> responseType, Map<String, String> headers) { return instrumentation.instrumentExternalCall(serviceName, url, () -> { HttpHeaders httpHeaders = new HttpHeaders(); if (headers != null) { headers.forEach(httpHeaders::add); } HttpEntity<Object> entity = new HttpEntity<>(requestBody, httpHeaders); return restTemplate.exchange(url, method, entity, responseType); }); } public <T> T getWithTracing(String serviceName, String url, Class<T> responseType) { return instrumentation.instrumentExternalCall(serviceName, url, () -> restTemplate.getForObject(url, responseType)); } public <T> T postWithTracing(String serviceName, String url, Object request, Class<T> responseType) { return instrumentation.instrumentExternalCall(serviceName, url, () -> restTemplate.postForObject(url, request, responseType)); } }

9. Custom Sampling Strategies

import io.honeycomb.beeline.tracing.sampling.Sampling; import org.springframework.stereotype.Component; import java.util.function.Function; @Component public class CustomSamplingStrategies { /** * Sample important business operations more frequently */ public static Function<String, Boolean> businessOperationSampler() { return (dataset) -> { // Sample all business operations if (dataset.contains("business") || dataset.contains("order")) { return Sampling.alwaysSampler().sample(dataset); } // Sample 10% of other operations return Math.random() < 0.1; }; } /** * Dynamic sampling based on error rates */ public Function<String, Boolean> adaptiveSampler() { return (dataset) -> { // Increase sampling for error scenarios if (isHighErrorRate(dataset)) { return true; // Sample all events during high error rates } return Sampling.alwaysSampler().sample(dataset); }; } /** * User-based sampling - sample more for important customers */ public Function<String, Boolean> userAwareSampler(String userId) { return (dataset) -> { if (isImportantCustomer(userId)) { return true; // Sample all events for important customers } // Sample 20% for regular customers return Math.random() < 0.2; }; } private boolean isHighErrorRate(String dataset) { // Implement error rate monitoring logic return false; } private boolean isImportantCustomer(String userId) { // Implement customer importance logic return userId.startsWith("VIP_") || userId.endsWith("@enterprise.com"); } }

10. Log Correlation Setup

import org.slf4j.MDC; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @Component public class LogCorrelationFilter implements Filter { private final Beeline beeline; public LogCorrelationFilter(Beeline beeline) { this.beeline = beeline; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Extract trace context and add to MDC for logging Span activeSpan = beeline.getTracer().getActiveSpan(); if (activeSpan != null) { Map<String, String> traceContext = activeSpan.getTraceContext(); MDC.put("trace_id", traceContext.get("trace_id")); MDC.put("span_id", traceContext.get("span_id")); } try { chain.doFilter(request, response); } finally { // Clear MDC after request MDC.clear(); } } }

11. Testing Instrumentation

import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @SpringBootTest class HoneycombInstrumentationTest { @Autowired private HoneycombInstrumentationService instrumentation; @MockBean private Beeline beeline; @MockBean private Tracer tracer; @MockBean private Span span; @Test void testBusinessOperationInstrumentation() { when(tracer.startSpan(anyString())).thenReturn(span); when(span.addField(anyString(), any())).thenReturn(span); instrumentation.instrumentOperation("test.operation", () -> { // Test operation logic return "result"; }); verify(span).addField("span.type", "business_operation"); verify(span).addField("operation.success", true); verify(span).close(); } @Test void testExternalCallInstrumentation() { when(tracer.startSpan(anyString())).thenReturn(span); when(span.addField(anyString(), any())).thenReturn(span); String result = instrumentation.instrumentExternalCall("test-service", "/api/test", () -> "response"); verify(span).addField("service.name", "test-service"); verify(span).addField("service.endpoint", "/api/test"); verify(span).addField("http.success", true); } }

12. Monitoring and Health Checks

import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; @Component public class HoneycombHealthIndicator implements HealthIndicator { private final Beeline beeline; public HoneycombHealthIndicator(Beeline beeline) { this.beeline = beeline; } @Override public Health health() { try { // Check if Beeline is properly initialized if (beeline.getTracer() != null) { return Health.up() .withDetail("service", "honeycomb-beeline") .withDetail("status", "connected") .withDetail("tracer", beeline.getTracer().getClass().getSimpleName()) .build(); } else { return Health.down() .withDetail("service", "honeycomb-beeline") .withDetail("error", "Tracer not initialized") .build(); } } catch (Exception e) { return Health.down(e) .withDetail("service", "honeycomb-beeline") .withDetail("error", "Health check failed") .build(); } } }

Best Practices

1. Field Naming Conventions

public class FieldNamingConventions { // Use consistent field naming public static final String SERVICE_NAME = "service.name"; public static final String HTTP_STATUS_CODE = "http.status_code"; public static final String DB_OPERATION = "db.operation"; public static final String ERROR_MESSAGE = "error.message"; // Use dots for hierarchy public static final String METRIC_BUSINESS_ORDER_COUNT = "metric.business.order.count"; public static final String TIMING_DATABASE_QUERY = "timing.database.query"; }

2. Sensitive Data Handling

@Component public class DataSanitization { public Map<String, Object> sanitizeFields(Map<String, Object> fields) { Map<String, Object> sanitized = new HashMap<>(fields); // Remove sensitive fields sanitized.remove("password"); sanitized.remove("credit_card"); sanitized.remove("authorization"); // Hash personal identifiers if (sanitized.containsKey("user_id")) { String userId = (String) sanitized.get("user_id"); sanitized.put("user_id", hashUserId(userId)); } return sanitized; } private String hashUserId(String userId) { return "user_" + Integer.toHexString(userId.hashCode()); } }

3. Performance Optimization

@Component public class PerformanceOptimizations { // Use lazy field evaluation for expensive operations public void addLazyField(Span span, String fieldName, Supplier<Object> valueSupplier) { if (span != null && isSampled(span)) { Object value = valueSupplier.get(); if (value != null) { span.addField(fieldName, value); } } } private boolean isSampled(Span span) { // Check if span is sampled before computing expensive fields return true; // Implementation depends on your sampling strategy } }

Conclusion

This comprehensive Honeycomb Beeline implementation provides:

  • Automatic instrumentation for Spring Boot applications
  • Custom business metrics and tracing
  • Database call instrumentation with timing
  • HTTP client instrumentation for external calls
  • Async operation support with context propagation
  • Custom sampling strategies for cost optimization
  • Log correlation for unified debugging
  • Health monitoring and testing support

Key benefits:

  • Rich observability with detailed traces and metrics
  • Easy integration with Spring Boot auto-configuration
  • Performance optimized with sampling and lazy evaluation
  • Developer friendly with simple instrumentation patterns
  • Production ready with health checks and monitoring

This setup enables comprehensive observability of your Java applications, providing deep insights into performance, errors, and business metrics through Honeycomb.io's powerful analysis platform.

Leave a Reply

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


Macro Nepal Helper