google-site-verification: google61fe8ba583a51912.html
OpenTelemetry Collector Integration in Java: Comprehensive Observability Implementation

OpenTelemetry Collector is a vendor-agnostic agent that can receive, process, and export telemetry data. It serves as a central hub for collecting traces, metrics, and logs from your Java applications.


Architecture Overview

OpenTelemetry Collector Components:

  • Receivers: How data gets into the Collector (OTLP, Jaeger, Zipkin, etc.)
  • Processors: Filter, transform, and enhance telemetry data
  • Exporters: Where data gets sent (Jaeger, Prometheus, logging, etc.)
  • Extensions: Additional functionality (health check, pprof)

Java Application → OTel Collector → Backend

  • Application sends telemetry via OTLP (OpenTelemetry Protocol)
  • Collector processes and routes data
  • Data exported to monitoring backends

Dependencies and Setup

Maven Dependencies
<properties> <opentelemetry.version>1.31.0</opentelemetry.version> <micrometer.version>1.11.5</micrometer.version> <spring-boot.version>3.1.0</spring-boot.version> </properties> <dependencies> <!-- OpenTelemetry API --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- OpenTelemetry SDK --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- OTLP Exporter --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- OpenTelemetry Logging --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-logging</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- OpenTelemetry Metrics --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-metrics</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Micrometer for metrics --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-core</artifactId> <version>${micrometer.version}</version> </dependency> </dependencies>
OpenTelemetry Collector Configuration
# otel-collector-config.yaml receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: timeout: 10s send_batch_size: 1000 memory_limiter: check_interval: 2s limit_mib: 1500 spike_limit_mib: 512 exporters: logging: loglevel: debug jaeger: endpoint: jaeger:14250 tls: insecure: true prometheus: endpoint: "0.0.0.0:8889" const_labels: service: "java-app" extensions: health_check: pprof: endpoint: :1888 zpages: endpoint: :55679 service: extensions: [health_check, pprof, zpages] pipelines: traces: receivers: [otlp] processors: [batch] exporters: [logging, jaeger] metrics: receivers: [otlp] processors: [batch] exporters: [logging, prometheus] logs: receivers: [otlp] processors: [batch] exporters: [logging]
Docker Compose for Development
# docker-compose.yml version: '3.8' services: otel-collector: image: otel/opentelemetry-collector:0.87.0 command: ["--config=/etc/otel-collector-config.yaml"] volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml ports: - "4317:4317" # OTLP gRPC - "4318:4318" # OTLP HTTP - "8889:8889" # Prometheus metrics - "13133:13133" # Health check - "55679:55679" # zPages networks: - otel-network jaeger: image: jaegertracing/all-in-one:1.48 ports: - "16686:16686" # Jaeger UI - "14250:14250" # Jaeger gRPC environment: - COLLECTOR_OTLP_ENABLED=true networks: - otel-network prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml networks: - otel-network grafana: image: grafana/grafana:latest ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=admin volumes: - ./grafana-dashboard.yml:/etc/grafana/provisioning/dashboards/dashboard.yml networks: - otel-network networks: otel-network: driver: bridge

Core OpenTelemetry Configuration

1. OpenTelemetry Configuration Builder
@Configuration public class OpenTelemetryConfig { private static final String SERVICE_NAME = "order-service"; private static final String SERVICE_VERSION = "1.0.0"; @Bean public OpenTelemetry openTelemetry() { // Resource configuration Resource resource = Resource.getDefault() .merge(Resource.builder() .put(SemanticResourceAttributes.SERVICE_NAME, SERVICE_NAME) .put(SemanticResourceAttributes.SERVICE_VERSION, SERVICE_VERSION) .put(SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT, "development") .build()); // OTLP exporter configuration OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder() .setEndpoint("http://localhost:4317") .setTimeout(Duration.ofSeconds(10)) .build(); OtlpGrpcMetricExporter metricExporter = OtlpGrpcMetricExporter.builder() .setEndpoint("http://localhost:4317") .setTimeout(Duration.ofSeconds(10)) .build(); // Span processor BatchSpanProcessor spanProcessor = BatchSpanProcessor.builder(spanExporter) .setScheduleDelay(Duration.ofSeconds(5)) .setMaxExportBatchSize(1000) .build(); // Metric reader PeriodicMetricReader metricReader = PeriodicMetricReader.builder(metricExporter) .setInterval(Duration.ofSeconds(30)) .build(); // SDK setup SdkTracerProvider tracerProvider = SdkTracerProvider.builder() .addSpanProcessor(spanProcessor) .setResource(resource) .build(); SdkMeterProvider meterProvider = SdkMeterProvider.builder() .registerMetricReader(metricReader) .setResource(resource) .build(); // Context propagation ContextPropagators propagators = ContextPropagators.create( W3CTraceContextPropagator.getInstance() ); return OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) .setMeterProvider(meterProvider) .setPropagators(propagators) .build(); } @Bean public Tracer tracer(OpenTelemetry openTelemetry) { return openTelemetry.getTracer(SERVICE_NAME, SERVICE_VERSION); } @Bean public Meter meter(OpenTelemetry openTelemetry) { return openTelemetry.getMeter(SERVICE_NAME); } }
2. Auto-configuration with Environment Variables
@Configuration public class AutoConfigOpenTelemetry { @Bean @ConditionalOnProperty(name = "otel.enabled", havingValue = "true") public OpenTelemetry openTelemetry() { // Use environment variables for configuration // OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, etc. return AutoConfiguredOpenTelemetrySdk.initialize() .getOpenTelemetrySdk(); } }

Tracing Implementation

1. Instrumented REST Controller
@RestController @RequestMapping("/api/orders") public class OrderController { private static final Logger logger = LoggerFactory.getLogger(OrderController.class); private final Tracer tracer; private final Meter meter; private final OrderService orderService; // Metrics private final LongCounter ordersCreatedCounter; private final LongCounter ordersFailedCounter; private final DoubleHistogram orderProcessingDuration; public OrderController(Tracer tracer, Meter meter, OrderService orderService) { this.tracer = tracer; this.meter = meter; this.orderService = orderService; // Initialize metrics this.ordersCreatedCounter = meter.counterBuilder("orders.created") .setDescription("Total number of orders created") .setUnit("1") .build(); this.ordersFailedCounter = meter.counterBuilder("orders.failed") .setDescription("Total number of failed orders") .setUnit("1") .build(); this.orderProcessingDuration = meter.histogramBuilder("order.processing.duration") .setDescription("Order processing duration in seconds") .setUnit("s") .build(); } @PostMapping public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) { // Create a span for the controller method Span span = tracer.spanBuilder("OrderController.createOrder") .setAttribute("http.method", "POST") .setAttribute("http.route", "/api/orders") .setAttribute("order.customer.id", request.getCustomerId()) .startSpan(); try (Scope scope = span.makeCurrent()) { logger.info("Creating order for customer: {}", request.getCustomerId()); long startTime = System.nanoTime(); Order order = orderService.createOrder(request); long duration = System.nanoTime() - startTime; orderProcessingDuration.record(duration / 1_000_000_000.0); ordersCreatedCounter.add(1); span.setStatus(StatusCode.OK); return ResponseEntity.ok(order); } catch (Exception e) { ordersFailedCounter.add(1); span.setStatus(StatusCode.ERROR, e.getMessage()); span.recordException(e); logger.error("Failed to create order", e); throw e; } finally { span.end(); } } @GetMapping("/{orderId}") public ResponseEntity<Order> getOrder(@PathVariable String orderId) { return tracer.spanBuilder("OrderController.getOrder") .setAttribute("http.method", "GET") .setAttribute("http.route", "/api/orders/{orderId}") .setAttribute("order.id", orderId) .startSpan() .makeCurrent() .closeable(() -> { try { Order order = orderService.getOrder(orderId); return ResponseEntity.ok(order); } catch (OrderNotFoundException e) { throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Order not found"); } }); } }
2. Service Layer with Manual Instrumentation
@Service public class OrderService { private final Tracer tracer; private final InventoryService inventoryService; private final PaymentService paymentService; public OrderService(Tracer tracer, InventoryService inventoryService, PaymentService paymentService) { this.tracer = tracer; this.inventoryService = inventoryService; this.paymentService = paymentService; } public Order createOrder(CreateOrderRequest request) { Span span = tracer.spanBuilder("OrderService.createOrder") .setAttribute("order.customer.id", request.getCustomerId()) .setAttribute("order.items.count", request.getItems().size()) .startSpan(); try (Scope scope = span.makeCurrent()) { // Validate order validateOrder(request); // Check inventory boolean inventoryAvailable = inventoryService.checkInventory(request.getItems()); if (!inventoryAvailable) { throw new InventoryException("Insufficient inventory"); } // Process payment PaymentResult paymentResult = paymentService.processPayment( request.getCustomerId(), request.getTotalAmount()); if (!paymentResult.isSuccess()) { throw new PaymentException("Payment failed: " + paymentResult.getErrorMessage()); } // Create order Order order = saveOrder(request, paymentResult.getTransactionId()); span.setAttribute("order.id", order.getId()); span.setAttribute("order.total.amount", order.getTotalAmount().doubleValue()); return order; } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR, e.getMessage()); throw e; } finally { span.end(); } } private void validateOrder(CreateOrderRequest request) { Span span = tracer.spanBuilder("OrderService.validateOrder").startSpan(); try (Scope scope = span.makeCurrent()) { // Validation logic if (request.getItems().isEmpty()) { throw new ValidationException("Order must contain at least one item"); } span.setStatus(StatusCode.OK); } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR); throw e; } finally { span.end(); } } private Order saveOrder(CreateOrderRequest request, String transactionId) { // Database operation with tracing return withDbSpan("OrderService.saveOrder", () -> { Order order = new Order(); order.setId(UUID.randomUUID().toString()); order.setCustomerId(request.getCustomerId()); order.setItems(request.getItems()); order.setTotalAmount(request.getTotalAmount()); order.setTransactionId(transactionId); order.setStatus(OrderStatus.CREATED); // Simulate database save // orderRepository.save(order); return order; }); } private <T> T withDbSpan(String spanName, Supplier<T> operation) { Span span = tracer.spanBuilder(spanName) .setSpanKind(SpanKind.INTERNAL) .setAttribute("db.operation", "write") .startSpan(); try (Scope scope = span.makeCurrent()) { return operation.get(); } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR); throw e; } finally { span.end(); } } }
3. HTTP Client Instrumentation
@Component public class InventoryService { private final Tracer tracer; private final RestTemplate restTemplate; public InventoryService(Tracer tracer, RestTemplate restTemplate) { this.tracer = tracer; this.restTemplate = restTemplate; } public boolean checkInventory(List<OrderItem> items) { Span span = tracer.spanBuilder("InventoryService.checkInventory") .setSpanKind(SpanKind.CLIENT) .startSpan(); try (Scope scope = span.makeCurrent()) { String url = "http://inventory-service/api/inventory/check"; // Add tracing headers to outgoing request HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // Inject trace context into headers W3CTraceContextPropagator.getInstance().inject( Context.current().with(span), headers, (carrier, key, value) -> carrier.set(key, value) ); HttpEntity<List<OrderItem>> request = new HttpEntity<>(items, headers); ResponseEntity<Boolean> response = restTemplate.exchange( url, HttpMethod.POST, request, Boolean.class); span.setAttribute("http.status_code", response.getStatusCodeValue()); span.setAttribute("inventory.available", response.getBody()); return Boolean.TRUE.equals(response.getBody()); } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR); throw e; } finally { span.end(); } } }

Metrics Implementation

1. Custom Metrics Collection
@Service public class OrderMetricsService { private final Meter meter; // Counters private final LongCounter ordersTotal; private final LongCounter ordersByStatus; // Histograms private final DoubleHistogram orderValueHistogram; private final DoubleHistogram orderProcessingTime; // Observable gauges private final AtomicLong activeOrders = new AtomicLong(0); public OrderMetricsService(Meter meter) { this.meter = meter; // Initialize counters this.ordersTotal = meter.counterBuilder("orders.total") .setDescription("Total number of orders") .setUnit("1") .build(); this.ordersByStatus = meter.counterBuilder("orders.by_status") .setDescription("Orders count by status") .setUnit("1") .build(); // Initialize histograms this.orderValueHistogram = meter.histogramBuilder("order.value") .setDescription("Order value distribution") .setUnit("USD") .build(); this.orderProcessingTime = meter.histogramBuilder("order.processing.time") .setDescription("Order processing time distribution") .setUnit("ms") .build(); // Initialize gauges meter.gaugeBuilder("orders.active") .setDescription("Number of active orders") .setUnit("1") .buildWithCallback(measurement -> { measurement.record(activeOrders.get()); }); } public void recordOrderCreated(Order order) { ordersTotal.add(1); ordersByStatus.add(1, Attributes.of( AttributeKey.stringKey("status"), order.getStatus().name(), AttributeKey.stringKey("customer_type"), getCustomerType(order.getCustomerId()) )); orderValueHistogram.record(order.getTotalAmount().doubleValue()); activeOrders.incrementAndGet(); } public void recordOrderCompleted(Order order, long processingTimeMs) { orderProcessingTime.record(processingTimeMs); activeOrders.decrementAndGet(); } public void recordOrderFailed(String reason) { ordersByStatus.add(1, Attributes.of( AttributeKey.stringKey("status"), "FAILED", AttributeKey.stringKey("failure_reason"), reason )); } private String getCustomerType(String customerId) { // Business logic to determine customer type return customerId.startsWith("VIP") ? "vip" : "regular"; } }
2. Micrometer Integration
@Configuration public class MicrometerConfig { @Bean public MeterRegistry meterRegistry(OpenTelemetry openTelemetry) { OtlpMeterRegistry otlpMeterRegistry = new OtlpMeterRegistry( key -> { switch (key) { case "otel.exporter.otlp.endpoint": return "http://localhost:4317"; case "otel.resource.attributes": return "service.name=order-service,service.version=1.0.0"; default: return null; } }, Clock.SYSTEM ); return new CompositeMeterRegistry(otlpMeterRegistry); } }

Logging Integration

1. Logback Configuration with OTLP
<!-- src/main/resources/logback-spring.xml --> <configuration> <appender name="OTLP" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender"> <captureExperimentalAttributes>true</captureExperimentalAttributes> <captureCodeAttributes>true</captureCodeAttributes> <captureMarkerAttribute>true</captureMarkerAttribute> <captureKeyValuePairAttributes>true</captureKeyValuePairAttributes> <captureLoggerContext>true</captureLoggerContext> <captureMdc>true</captureMdc> </appender> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{spanId}] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="OTLP" /> <appender-ref ref="CONSOLE" /> </root> </configuration>
2. Structured Logging with Context
@Service @Slf4j public class PaymentService { private final Tracer tracer; public void processPayment(String orderId, BigDecimal amount) { Span currentSpan = Span.current(); String traceId = currentSpan.getSpanContext().getTraceId(); String spanId = currentSpan.getSpanContext().getSpanId(); // Structured logging with trace context log.info("Processing payment for order", kv("orderId", orderId), kv("amount", amount), kv("traceId", traceId), kv("spanId", spanId), kv("service", "payment-service") ); try { // Payment processing logic if (amount.compareTo(new BigDecimal("1000")) > 0) { log.warn("Large payment amount detected", kv("orderId", orderId), kv("amount", amount), kv("threshold", 1000) ); } // Simulate payment processing Thread.sleep(100); log.info("Payment processed successfully", kv("orderId", orderId), kv("status", "success") ); } catch (Exception e) { log.error("Payment processing failed", kv("orderId", orderId), kv("error", e.getMessage()), kv("traceId", traceId) ); throw new PaymentException("Payment failed", e); } } }

Advanced Features

1. Custom Span Processor
@Component public class CustomSpanProcessor implements SpanProcessor { private static final Logger logger = LoggerFactory.getLogger(CustomSpanProcessor.class); @Override public void onStart(Context context, Span span) { // Add custom attributes to all spans span.setAttribute("deployment.environment", getEnvironment()); span.setAttribute("service.instance.id", getInstanceId()); logger.debug("Span started: {}", span.getSpanContext().getSpanId()); } @Override public boolean isStartRequired() { return true; } @Override public void onEnd(SpanData spanData) { // Log slow spans long durationMs = (spanData.getEndEpochNanos() - spanData.getStartEpochNanos()) / 1_000_000; if (durationMs > 1000) { logger.warn("Slow span detected: {} took {}ms", spanData.getName(), durationMs); } // Custom metrics or alerts could be added here } @Override public boolean isEndRequired() { return true; } private String getEnvironment() { return System.getenv().getOrDefault("ENVIRONMENT", "development"); } private String getInstanceId() { return System.getenv().getOrDefault("HOSTNAME", "unknown"); } }
2. Baggage Propagation
@Component public class BaggageService { public void processWithBaggage(String orderId, String customerTier) { // Add baggage to current context Baggage.current() .toBuilder() .put("order.id", orderId) .put("customer.tier", customerTier) .put("processing.origin", "order-service") .build() .makeCurrent() .closeable(() -> { // This baggage will be propagated to downstream services processOrder(orderId); }); } private void processOrder(String orderId) { // Read baggage in downstream processing String customerTier = Baggage.current().getEntryValue("customer.tier"); if ("premium".equals(customerTier)) { // Apply premium processing logic Span.current().setAttribute("processing.priority", "high"); } } }
3. Semantic Conventions Helper
public class SemanticConventionsHelper { public static final AttributeKey<String> HTTP_METHOD = AttributeKey.stringKey("http.method"); public static final AttributeKey<String> HTTP_ROUTE = AttributeKey.stringKey("http.route"); public static final AttributeKey<Long> HTTP_STATUS_CODE = AttributeKey.longKey("http.status_code"); public static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation"); public static final AttributeKey<String> DB_SYSTEM = AttributeKey.stringKey("db.system"); public static void addHttpAttributes(Span span, String method, String route, int statusCode) { span.setAttribute(HTTP_METHOD, method); span.setAttribute(HTTP_ROUTE, route); span.setAttribute(HTTP_STATUS_CODE, (long) statusCode); } public static void addDatabaseAttributes(Span span, String system, String operation) { span.setAttribute(DB_SYSTEM, system); span.setAttribute(DB_OPERATION, operation); } }

Testing

1. Unit Testing with OpenTelemetry
@ExtendWith(SpringExtension.class) @SpringBootTest class OrderServiceTest { @Autowired private OrderService orderService; @Autowired private Tracer tracer; @Test void shouldCreateOrderWithTracing() { // Given CreateOrderRequest request = new CreateOrderRequest("customer123", List.of(new OrderItem("item1", 2)), new BigDecimal("99.99")); // When Order order = orderService.createOrder(request); // Then assertThat(order).isNotNull(); assertThat(order.getCustomerId()).isEqualTo("customer123"); // Verify tracing (you might need to use in-memory span exporter for testing) // This would require additional test configuration } } @SpringBootTest @TestPropertySource(properties = { "otel.traces.exporter=none", "otel.metrics.exporter=none" }) class OpenTelemetryDisabledTest { // Tests with OpenTelemetry disabled }
2. Integration Testing
@Testcontainers @SpringBootTest class OpenTelemetryIntegrationTest { @Container static GenericContainer<?> otelCollector = new GenericContainer<>( "otel/opentelemetry-collector:0.87.0") .withExposedPorts(4317, 4318) .withCopyToContainer( MountableFile.forClasspathResource("otel-collector-config.yaml"), "/etc/otel-collector-config.yaml") .withCommand("--config=/etc/otel-collector-config.yaml"); @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add("otel.exporter.otlp.endpoint", () -> "http://" + otelCollector.getHost() + ":" + otelCollector.getMappedPort(4317)); } @Test void shouldSendTelemetryToCollector() { // Integration test that verifies telemetry is sent to collector } }

Best Practices

  1. Use Semantic Conventions: Follow OpenTelemetry semantic conventions for attribute names
  2. Sample Appropriately: Configure sampling based on your needs
  3. Limit Attribute Cardinality: Avoid high-cardinality attributes in spans
  4. Secure Collector: Use TLS and authentication in production
  5. Monitor Collector: Monitor the collector's health and performance
  6. Use Context Propagation: Ensure trace context is propagated across service boundaries
// Example of safe attribute setting public void setSafeAttributes(Span span, Map<String, Object> attributes) { attributes.forEach((key, value) -> { if (isLowCardinality(key)) { if (value instanceof String) { span.setAttribute(key, (String) value); } else if (value instanceof Long) { span.setAttribute(key, (Long) value); } else if (value instanceof Double) { span.setAttribute(key, (Double) value); } else if (value instanceof Boolean) { span.setAttribute(key, (Boolean) value); } } }); } private boolean isLowCardinality(String key) { return !key.contains("id") && !key.contains("email") && !key.contains("name"); }

Conclusion

OpenTelemetry Collector integration provides:

  • Vendor-agnostic observability data collection
  • Centralized configuration for telemetry data processing
  • Flexible routing to multiple backends
  • Reduced overhead on application instances
  • Standardized telemetry across your entire stack

By implementing the patterns shown above, you can build a comprehensive observability solution that provides deep insights into your Java applications' performance and behavior. The OpenTelemetry Collector serves as the central nervous system for your observability infrastructure, enabling you to monitor, debug, and optimize your distributed systems effectively.

Pyroscope Profiling in Java
Explains how to use Pyroscope for continuous profiling in Java applications, helping developers analyze CPU and memory usage patterns to improve performance and identify bottlenecks.
https://macronepal.com/blog/pyroscope-profiling-in-java/

OpenTelemetry Metrics in Java: Comprehensive Guide
Provides a complete guide to collecting and exporting metrics in Java using OpenTelemetry, including counters, histograms, gauges, and integration with monitoring tools. (MACRO NEPAL)
https://macronepal.com/blog/opentelemetry-metrics-in-java-comprehensive-guide/

OTLP Exporter in Java: Complete Guide for OpenTelemetry
Explains how to configure OTLP exporters in Java to send telemetry data such as traces, metrics, and logs to monitoring systems using HTTP or gRPC protocols. (MACRO NEPAL)
https://macronepal.com/blog/otlp-exporter-in-java-complete-guide-for-opentelemetry/

Thanos Integration in Java: Global View of Metrics
Explains how to integrate Thanos with Java monitoring systems to create a scalable global metrics view across multiple Prometheus instances.

https://macronepal.com/blog/thanos-integration-in-java-global-view-of-metrics

Time Series with InfluxDB in Java: Complete Guide (Version 2)
Explains how to manage time-series data using InfluxDB in Java applications, including storing, querying, and analyzing metrics data.

https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide-2

Time Series with InfluxDB in Java: Complete Guide
Provides an overview of integrating InfluxDB with Java for time-series data handling, including monitoring applications and managing performance metrics.

https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide

Implementing Prometheus Remote Write in Java (Version 2)
Explains how to configure Java applications to send metrics data to Prometheus-compatible systems using the remote write feature for scalable monitoring.

https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide-2

Implementing Prometheus Remote Write in Java: Complete Guide
Provides instructions for sending metrics from Java services to Prometheus servers, enabling centralized monitoring and real-time analytics.

https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide

Building a TileServer GL in Java: Vector and Raster Tile Server
Explains how to build a TileServer GL in Java for serving vector and raster map tiles, useful for geographic visualization and mapping applications.

https://macronepal.com/blog/building-a-tileserver-gl-in-java-vector-and-raster-tile-server

Indoor Mapping in Java
Explains how to create indoor mapping systems in Java, including navigation inside buildings, spatial data handling, and visualization techniques.

Leave a Reply

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


Macro Nepal Helper