Introduction: Why Monitoring is Vital for Kotlin Applications
\nModern Kotlin applications running on the JVM (Java Virtual Machine) often become critical infrastructure nodes. Without proper monitoring, you risk encountering sudden failures, memory leaks, or performance degradation that go unnoticed until an incident occurs. In this article, we will break down how to build a comprehensive monitoring system for Kotlin applications "from A to Z": from collecting basic metrics (CPU, memory, GC) to setting up alerts in Grafana and Prometheus.
\nWe will cover three key tools: Micrometer (a metrics facade), Prometheus (storage and collector), and Grafana (visualization and alerts). All code examples are written in Kotlin.
\n\n1. Basics of Metrics Collection: Micrometer and Prometheus
\nMicrometer is the de facto standard facade for monitoring in the Spring and Kotlin ecosystem. It provides a unified API for sending metrics to various systems (Prometheus, Datadog, New Relic, etc.).
\n\n1.1. Adding Dependencies (Gradle)
\ndependencies {\n implementation("io.micrometer:micrometer-registry-prometheus:1.12.0")\n implementation("org.springframework.boot:spring-boot-starter-actuator")\n}\nFor Ktor or plain Kotlin without Spring, you can add only micrometer-core and micrometer-registry-prometheus.
1.2. Basic Micrometer Configuration in Spring Boot
\nIn application.yml, simply enable Actuator and the Prometheus endpoint:
management:\n endpoints:\n web:\n exposure:\n include: health,info,prometheus\n metrics:\n export:\n prometheus:\n enabled: true\nAfter starting the application, metrics will be available at /actuator/prometheus.
1.3. Creating Custom Metrics (Example in Kotlin)
\nSuppose you want to count the number of processed orders and their processing time:
\nimport io.micrometer.core.instrument.MeterRegistry\nimport io.micrometer.core.instrument.Timer\nimport org.springframework.stereotype.Service\nimport java.util.concurrent.atomic.AtomicLong\n\n@Service\nclass OrderService(private val registry: MeterRegistry) {\n\n private val processedOrders: AtomicLong = registry.gauge(\n "orders.processed.count",\n AtomicLong(0)\n )\n private val orderProcessingTime: Timer = registry.timer("orders.processing.time")\n\n fun processOrder(orderId: String) {\n val sample = Timer.start()\n try {\n // Order processing logic\n Thread.sleep((50..200).random().toLong())\n processedOrders.incrementAndGet()\n } finally {\n sample.stop(orderProcessingTime)\n }\n }\n}\nNow the metrics orders_processed_count and orders_processing_time_seconds will appear in Prometheus.
2. Configuring Prometheus for Metrics Collection
\nPrometheus is a server that "scrapes" (requests) metrics from your applications on a schedule.
\n\n2.1. Configuring prometheus.yml
\nAdd your Kotlin service as a target:
\nscrape_configs:\n - job_name: 'kotlin-app'\n metrics_path: '/actuator/prometheus'\n static_configs:\n - targets: ['localhost:8080']\nIf the application runs in Docker/Kubernetes, use service discovery.
\n\n2.2. Important Default JVM Metrics
\nMicrometer automatically exports many JVM metrics:
\n- \n
jvm_memory_used_bytes— used memory (heap/non-heap) \n jvm_gc_pause_seconds— GC pause time \n jvm_threads_live_threads— number of active threads \n process_cpu_usage— CPU usage by the process \n
This data is already available in Prome