google-site-verification: google61fe8ba583a51912.html
Kibana Visualization for Java Application Metrics: A Complete Guide

Introduction

Kibana is a powerful data visualization tool that works seamlessly with Elasticsearch to transform your Java application metrics into actionable insights. When properly configured, it provides real-time visibility into application performance, JVM health, and business metrics.

This guide covers setting up comprehensive Kibana visualizations for Java application metrics using popular monitoring tools and frameworks.


Architecture Overview

[Java Application] → [Metrics Collector] → [Elasticsearch] → [Kibana Visualizations] ↓ ↓ ↓ ↓ Micrometer/ Logstash/ Data Store Dashboards/ JMX Metrics Elastic APM & Search Engine Visualizations

Step 1: Setting Up Metrics Export from Java

Maven Dependencies

<properties> <micrometer.version>1.12.0</micrometer.version> <spring-boot.version>3.2.0</spring-boot.version> </properties> <dependencies> <!-- Spring Boot Actuator for metrics --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- Micrometer Registry for Elastic --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-elastic</artifactId> <version>${micrometer.version}</version> </dependency> <!-- Alternatively: Prometheus registry --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> <version>${micrometer.version}</version> </dependency> </dependencies>

Application Configuration

application.yml

management: endpoints: web: exposure: include: health,metrics,prometheus endpoint: health: show-details: always metrics: enabled: true metrics: export: elastic: enabled: true host: http://localhost:9200 index: java-metrics index-date-format: yyyy-MM-dd step: 30s distribution: percentiles-histogram: http.server.requests: true percentiles: http.server.requests: 0.5, 0.95, 0.99 sla: http.server.requests: 100ms, 500ms, 1s # Custom application metrics app: metrics: prefix: myapp

Custom Metrics Configuration

MetricsConfig.java

package com.example.monitoring.config; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Timer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.ConcurrentHashMap; import java.util.Map; @Configuration public class MetricsConfig { private final Map<String, Double> businessMetrics = new ConcurrentHashMap<>(); @Bean public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { return registry -> registry.config() .commonTags("application", "order-service", "environment", "production", "region", "us-east-1"); } @Bean public Gauge businessMetricsGauge(MeterRegistry registry) { return Gauge.builder("app.business.active_orders") .description("Number of active orders") .register(registry, this, config -> businessMetrics.getOrDefault("active_orders", 0.0)); } @Bean public Counter orderCreatedCounter(MeterRegistry registry) { return Counter.builder("app.business.orders.created") .description("Total orders created") .register(registry); } public void setBusinessMetric(String key, Double value) { businessMetrics.put(key, value); } }

Instrumented Service Example

OrderService.java

package com.example.service; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service public class OrderService { private final Counter orderCounter; private final Timer orderProcessingTimer; private final MetricsConfig metricsConfig; public OrderService(MeterRegistry registry, MetricsConfig metricsConfig) { this.orderCounter = registry.counter("app.orders.total"); this.orderProcessingTimer = Timer.builder("app.orders.processing.time") .description("Order processing time") .register(registry); this.metricsConfig = metricsConfig; } public Order processOrder(OrderRequest request) { // Using Timer.Sample to measure method execution time Timer.Sample sample = Timer.start(); try { // Business logic Order order = createOrder(request); orderCounter.increment(); // Update business metric metricsConfig.setBusinessMetric("active_orders", getActiveOrdersCount()); return order; } finally { sample.stop(orderProcessingTimer); } } private Order createOrder(OrderRequest request) { // Simulate processing time try { Thread.sleep(100 + (long)(Math.random() * 100)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return new Order(request); } private double getActiveOrdersCount() { // Implementation to get active orders from database return Math.random() * 1000; // Example value } }

Step 2: Elasticsearch Index Templates

Create Index Template for Java Metrics

Using Kibana Dev Tools:

PUT _index_template/java-metrics-template { "index_patterns": ["java-metrics-*"], "template": { "mappings": { "properties": { "@timestamp": { "type": "date" }, "application": { "type": "keyword" }, "environment": { "type": "keyword" }, "metric.name": { "type": "keyword" }, "metric.value": { "type": "double" }, "jvm.memory.used": { "type": "double" }, "jvm.memory.max": { "type": "double" }, "jvm.gc.pause": { "type": "double" }, "http.server.requests.duration": { "type": "double" }, "system.cpu.usage": { "type": "double" } } }, "settings": { "number_of_shards": 1, "number_of_replicas": 1 } } }

Step 3: Kibana Visualizations

1. JVM Memory Dashboard

Kibana Lens Visualization for Memory Usage:

{ "title": "JVM Memory Usage", "type": "lens", "attributes": { "visualizationType": "lnsXY", "title": "JVM Heap Memory Usage", "references": [], "state": { "datasourceStates": { "formBased": { "layers": { "layer1": { "columns": { "x-axis": { "columnId": "x-axis", "field": "@timestamp", "dataType": "date", "isBucketed": true, "sourceField": "@timestamp", "params": { "interval": "auto" } }, "y-axis": { "columnId": "y-axis", "field": "jvm.memory.used", "dataType": "number", "isBucketed": false, "sourceField": "jvm.memory.used", "params": { "format": { "id": "bytes" } } }, "split": { "columnId": "split", "field": "application", "dataType": "string", "isBucketed": true, "sourceField": "application" } } } } } } } } }

2. Application Performance Overview

Create Visualization via Kibana UI:

  1. Go to Analytics > Dashboard > Create dashboard
  2. Add visualization > Lens
  3. Configure metrics:

HTTP Request Latency:

  • X-axis: @timestamp (Date Histogram)
  • Y-axis: http.server.requests.duration (Average)
  • Break down by: status (if available)

Request Rate:

  • Y-axis: http.server.requests.count (Count)

Error Rate:

  • Y-axis: Count of requests with status: 5xx

3. Custom Business Metrics Visualization

Orders Processing Dashboard:

{ "title": "Business Metrics - Orders", "type": "lens", "attributes": { "visualizationType": "lnsMetric", "title": "Active Orders", "references": [], "state": { "datasourceStates": { "formBased": { "layers": { "layer1": { "columns": { "metric-column": { "columnId": "metric-column", "field": "app.business.active_orders", "dataType": "number", "isBucketed": false, "sourceField": "app.business.active_orders", "params": { "format": { "id": "number" } } } } } } } } } } }

Step 4: Kibana Query Examples

KQL (Kibana Query Language) Examples

# JVM metrics for specific application metric.name: "jvm.memory.used" and application: "order-service" # High latency requests http.server.requests.duration > 1000 # Error analysis status >= 500 and status < 600 # Memory pressure detection jvm.memory.used / jvm.memory.max > 0.8 # Business metrics with time range app.business.active_orders > 100 and @timestamp >= now-1h

Advanced Queries with Aggregations

GET java-metrics-*/_search { "size": 0, "query": { "range": { "@timestamp": { "gte": "now-1h" } } }, "aggs": { "applications": { "terms": { "field": "application.keyword" }, "aggs": { "avg_memory": { "avg": { "field": "jvm.memory.used" } }, "p95_latency": { "percentiles": { "field": "http.server.requests.duration", "percents": [95] } } } } } }

Step 5: Alerting and Anomaly Detection

Kibana Alert Rules

1. High Memory Usage Alert:

{ "rule_type_id": ".index-threshold", "name": "High JVM Memory Usage", "params": { "index": "java-metrics-*", "timeField": "@timestamp", "aggType": "avg", "aggField": "jvm.memory.used", "groupBy": "top", "termSize": 5, "termField": "application.keyword", "timeWindowSize": 5, "timeWindowUnit": "m", "thresholdComparator": ">", "threshold": [8589934592], "actions": [] } }

2. High Error Rate Alert:

{ "rule_type_id": "monitoring_alert", "name": "High HTTP Error Rate", "params": { "criteria": [ { "aggType": "count", "condition": ">", "threshold": 100, "filter": "status:5xx" } ], "index": "java-metrics-*", "timeField": "@timestamp", "timeWindowSize": 10, "timeWindowUnit": "m" } }

Anomaly Detection Jobs

Create machine learning jobs in Kibana:

  1. Go to Analytics > Machine Learning > Anomaly Detection
  2. Create job > Single metric
  3. Configure:
  • Data source: java-metrics-*
  • Field: http.server.requests.duration
  • Partition field: application

Step 6: Complete Dashboard Example

Java Application Monitoring Dashboard

Create a comprehensive dashboard with:

  1. JVM Health Panel:
  • Heap Memory Usage (Area chart)
  • GC Pause Times (Line chart)
  • Thread Count (Metric)
  • CPU Usage (Gauge)
  1. Application Performance Panel:
  • Request Rate (Line chart)
  • Response Time Percentiles (Line chart)
  • Error Rate (Bar chart)
  • Endpoint Performance (Data table)
  1. Business Metrics Panel:
  • Active Orders (Metric)
  • Orders Created (Trend)
  • Processing Time (Gauge)
  1. System Resources Panel:
  • System CPU (Line chart)
  • Disk I/O (Area chart)
  • Network Usage (Line chart)

Best Practices

1. Metric Naming Convention

// Good naming Counter.builder("app.orders.created") Counter.builder("http.requests.total") Gauge.builder("jvm.memory.heap.used") // Avoid Counter.builder("ordersCreated") // inconsistent casing Counter.builder("app_orders_created") // mixed separators

2. Tagging Strategy

registry.counter("http.requests", "method", "GET", "status", "200", "uri", "/api/orders", "application", "order-service");

3. Retention Policy

PUT _ilm/policy/java-metrics-policy { "policy": { "phases": { "hot": { "min_age": "0ms", "actions": { "rollover": { "max_size": "50gb", "max_age": "1d" } } }, "delete": { "min_age": "30d", "actions": { "delete": {} } } } } }

Conclusion

Setting up Kibana visualizations for Java metrics provides powerful insights into your application's performance and health. By combining:

  • Micrometer for metric collection
  • Elasticsearch for storage and search
  • Kibana for visualization and alerting

You create a comprehensive monitoring solution that helps you:

  • Detect performance issues proactively
  • Understand application behavior under load
  • Make data-driven decisions about scaling and optimization
  • Quickly troubleshoot production incidents

The key to success is consistent metric naming, proper tagging, and designing visualizations that provide actionable information for your specific use case.

Leave a Reply

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


Macro Nepal Helper