Мониторинг Kotlin приложений: метрики, алерты и продвинутые техники

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему мониторинг — основа production-стабильности

В современной разработке написать код — лишь половина дела. Чтобы приложение на Kotlin работало стабильно под нагрузкой, необходимо понимать его внутреннее состояние. Базовые логи и health checks дают лишь поверхностную картину. Продвинутый мониторинг с метриками и алертами позволяет не просто фиксировать сбои, а предсказывать их, выявлять узкие места и оперативно реагировать на аномалии. В этой статье мы разберем, как построить production-ready систему мониторинга для Kotlin-приложений на стеке Micrometer + Prometheus + Alertmanager + Grafana.

1. Сбор метрик с помощью Micrometer и Prometheus

Micrometer — это фасад для сбора метрик, который де-факто стал стандартом в мире JVM и Kotlin. Он поддерживает множество бэкендов: Prometheus, Datadog, New Relic и другие. Для Kotlin-приложений (Spring Boot, Ktor, http4k) интеграция тривиальна.

1.1. Подключение и настройка

Добавьте в build.gradle.kts необходимые зависимости:

// Для Spring Boot
implementation("io.micrometer:micrometer-registry-prometheus")

// Для Ktor (ручная настройка)
implementation("io.micrometer:micrometer-core:1.12.0")
implementation("io.micrometer:micrometer-registry-prometheus:1.12.0")

Для Spring Boot автоматически создается бин MeterRegistry. В Ktor вам нужно инициализировать его вручную:

import io.micrometer.prometheus.PrometheusMeterRegistry
import io.micrometer.core.instrument.Clock
import io.prometheus.client.CollectorRegistry

val registry = PrometheusMeterRegistry(
    PrometheusConfig.DEFAULT,
    CollectorRegistry.defaultRegistry,
    Clock.SYSTEM
)

// Экспорт эндпоинта /metrics
embeddedServer(Netty, port = 8080) {
    routing {
        get("/metrics") {
            call.respondText(registry.scrape())
        }
    }
}.start(wait = true)

1.2. Кастомные метрики

Помимо стандартных (JVM, CPU, память), добавляйте бизнес-метрики. Пример счетчика обработанных заказов:

val orderCounter = Counter.builder("orders.processed")
    .description("Total processed orders")
    .register(registry)

fun processOrder(order: Order) {
    // бизнес-логика
    orderCounter.increment()
}

Или гистограмма для времени выполнения запроса:

val requestTimer = Timer.builder("http.requests.duration")
    .description("Duration of HTTP requests")
    .publishPercentiles(0.5, 0.95, 0.99)
    .sla(Duration.ofMillis(100), Duration.ofMillis(500))
    .register(registry)

fun handleRequest(block: () -> Response): Response {
    return requestTimer.record(block)
}

2. Продвинутые алерты: от пороговых значений к предсказаниям

Алерты на основе простых порогов (например, CPU > 90%) часто дают ложные срабатывания. На продвинутом уровне мы используем Alertmanager и правила PromQL с агрегацией и прогнозированием.

2.1. Настройка Alertmanager

Конфигурация alertmanager.yml для отправки уведомлений в Telegram и Slack:

route:
  receiver: 'team-alerts'
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 1h

receivers:
- name: 'team-alerts'
  telegram_configs:
  - bot_token: 'YOUR_BOT_TOKEN'
    chat_id: -123456789
    parse_mode: 'HTML'
  slack_configs:
  - api_url: 'https://hooks.slack.com/services/...'
    channel: '#alerts'

2.2. Правила PromQL для умных алертов

В rules.yml опишем правило, которое срабатывает, если 95-й перцентиль времени ответа превышает 500 мс в течение 5 минут:

groups:
- name: http_latency
  rules:
  - alert: HighLatencyP95
    expr: |
      histogram_quantile(0.95,
        sum(rate(http_requests_duration_seconds_bucket[5m])) by (le, endpoint)
      ) > 0.5
    for: 5m
    annotations:
      summary: "Высокая задержка на {{ $labels.endpoint }}"

Предсказательный алерт на основе скорости роста очереди задач:

  - alert: QueueGrowthPrediction
    expr: |
      predict_linear(job_queue_size[1h], 3600) > 1000
    for: 2m
    annotations:
      summary: "Очередь задач превысит 1000 через час"

3. Интеграция с Grafana: дашборды и визуализация

Grafana — стандартный инструмент для визуализации метрик Prometheus. Создадим дашборд с ключевыми панелями.

3.1. Базовый дашборд для Kotlin-приложения

Пример JSON для импорта в Grafana (фрагмент панели "JVM Heap"):

{
  "panels": [
    {
      "title": "JVM Heap Memory",
      "type": "graph",
      "targets": [
        {
          "expr": "jvm_memory_used_bytes{area=\"heap\"} / 1024^3",
          "legendFormat": "Used (GB)",
          "refId": "A"
        },
        {
          "expr": "jvm_memory_max_bytes{area=\"heap\"} / 1024^3",
          "legendFormat": "Max (GB)",
          "refId": "B"
        }
      ]
    }
  ]
}

3.2. Продвинутые панели: heatmap и статистика

Heatmap для распределения времени ответа:

{
  "title": "HTTP Request Duration Heatmap",
  "type": "heatmap",
  "targets": [
    {
      "expr": "sum(increase(http_requests_duration_seconds_bucket[5m])) by (le)",
      "format": "heatmap",
      "legendFormat": "{{ le }}"
    }
  ]
}

Панель "Health Score" — агрегированный показатель здоровья:

  - expr: |
      (
        (1 - rate(http_requests_errors_total[5m]) / rate(http_requests_total[5m])) * 0.4 +
        (1 - rate(jvm_gc_pause_seconds_sum[5m]) / 60) * 0.3 +
        (jvm_memory_used_bytes{area="heap"} / j                

Блоги

Рекомендаций по книгам