← назад к разделу

Когда приложение работает в продакшене, нужно понимать, что внутри происходит: сколько запросов обрабатывается, где задержки, что пишется в логах. Всё это называют наблюдаемостью (observability). Чтобы это работало правильно, нужно однажды настроить четыре вещи: отдельный порт для служебных endpoints, явный список того, что открыто, гистограммы задержек и формат логов. Разберём каждую.

Отдельный порт для Actuator

По умолчанию Spring Boot запускает всё на одном порту: и ваш API (/api/orders), и служебные endpoints Actuator (/actuator/health, /actuator/prometheus). Это удобно локально, но создаёт проблемы в продакшене.

Если Actuator на том же порту, что и API, сетевой политикой нельзя запретить доступ к нему извне: Kubernetes Ingress открывает порт целиком, а не отдельные пути. Prometheus scraper (который собирает метрики каждые 15 секунд) и healthcheck-зонды (каждые 5 секунд) создают нагрузку на тот же поток, что обрабатывает бизнес-запросы.

Решение простое — разные порты:

server:
  port: 8080

management:
  server:
    port: 8081

Теперь Ingress публикует только 8080, а сетевая политика разрешает Prometheus обращаться только на 8081. Служебный трафик не мешает бизнес-логике.

Явный список открытых endpoints

Дефолт Spring Boot открывает только health и info. Когда добавляют '*', чтобы быстро посмотреть всё сразу, — открывается гораздо больше, чем нужно.

Некоторые endpoints небезопасны для публичного доступа:

  • /actuator/env — показывает все конфигурационные свойства, включая те, что попали туда через переменные окружения. Если секрет оказался там случайно, он будет виден.
  • /actuator/heapdump — полный дамп памяти JVM. Он может содержать JWT-токены, пароли, персональные данные пользователей, которые в данный момент обрабатываются.
  • /actuator/threaddump — стек всех потоков. Раскрывает внутреннюю структуру приложения.

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

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus

metrics нужен для ручного дебага через JSON. prometheus — для scraper'а. Остальное закрыто.

Если очень нужен heapdump в продакшене для отладки — Spring Security с ролью администратора и аудит каждого обращения. По умолчанию — не открывать.

Гистограммы задержек и SLO buckets

Стандартная метрика http.server.requests в Micrometer по умолчанию считает только количество запросов и суммарное время. Чтобы знать, сколько запросов уложились в 500 мс или какой p95, нужна гистограмма.

management:
  metrics:
    distribution:
      percentiles-histogram:
        http.server.requests: true
      slo:
        http.server.requests: 100ms,500ms,1s,5s
    tags:
      service: ${spring.application.name}
      env: ${ENV:dev}
      version: ${BUILD_VERSION:unknown}

percentiles-histogram: true заставляет Micrometer публиковать полную гистограмму (~64 bucket'а). В Prometheus это позволяет посчитать точный квантиль через histogram_quantile() — без интерполяции.

slo: 100ms,500ms,1s,5s добавляет явные пороговые bucket'ы. В Prometheus появится метрика http_server_requests_seconds_bucket{le="0.5"} — «сколько запросов завершилось быстрее 500 мс». Это удобно для SLO-алертов: больше не нужно интерполировать.

tags.service/env/version — глобальные метки, которые добавятся ко всем метрикам автоматически. Без них непонятно, от какого сервиса и окружения пришли данные. BUILD_VERSION проставляется из CI как переменная окружения.

Два профиля логирования

В разработке удобны однострочные читаемые логи. В продакшене нужен структурированный JSON, который Logstash или Vector умеют парсить и индексировать.

Logback поддерживает Spring-профили прямо в logback-spring.xml:

<configuration>
    <springProfile name="dev,test">
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %X{traceId:-} %logger{30} - %msg%n</pattern>
            </encoder>
        </appender>
    </springProfile>

    <springProfile name="prod,staging">
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder">
                <includeMdcKeyName>traceId</includeMdcKeyName>
                <includeMdcKeyName>spanId</includeMdcKeyName>
                <includeMdcKeyName>requestId</includeMdcKeyName>
                <includeMdcKeyName>userId</includeMdcKeyName>
            </encoder>
        </appender>
    </springProfile>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

springProfile — это расширение Logback, которое Spring Boot активирует при запуске. Нужный appender включается в зависимости от профиля.

В dev-паттерне %X{traceId:-} берёт traceId из MDC и подставляет в строку лога. Если трейсинг не настроен — пустая строка, ничего не падает. По этому идентификатору потом ищут запрос в Tempo или Jaeger.

В продакшене LogstashEncoder пишет каждую строку как JSON-объект. MDC-поля, перечисленные в <includeMdcKeyName>, попадают в JSON явно. Остальные ключи MDC — в блок mdc автоматически.

Частые ошибки

Один порт для API и Actuator в продакшене. Сетевую изоляцию сделать не получится. management.server.port: 8081 решает проблему.

exposure.include: '*'. Открывает env, beans, mappings, loggers, configprops, heapdump. Каждый из них — потенциальная утечка данных. Явный список надёжнее:

# так не надо
management.endpoints.web.exposure.include: '*'

# так правильно
management.endpoints.web.exposure.include: health,info,metrics,prometheus

Один Logback-паттерн для dev и prod. В разработке теряется читаемость JSON, в продакшене теряется структура. springProfile — стандартный способ разделить.

Нет гистограммы для метрик задержки. Тогда квантили либо не считаются, либо только примерные. percentiles-histogram: true для http.server.requests — минимальный набор.

Коротко

  • Запускайте Actuator на отдельном порту (management.server.port: 8081) — это даёт сетевую изоляцию и не мешает бизнес-трафику.
  • Явно перечисляйте открытые endpoints: health,info,metrics,prometheus. Wildcard '*' — источник утечек.
  • /actuator/env, /actuator/heapdump, /actuator/threaddump не открывают публично — они содержат секреты и дампы памяти.
  • percentiles-histogram: true + slo: 100ms,500ms,1s,5s для http.server.requests — основа для SLO-алертов на задержку.
  • Глобальные теги service/env/version в management.metrics.tags сразу расставляют контекст по всем метрикам.
  • Два профиля в logback-spring.xml: текстовый для dev,test, JSON через LogstashEncoder для prod,staging.

Что почитать дальше

  • Логирование в Java — structured JSON, MDC, уровни
  • Метрики — Micrometer, Prometheus, RED/USE
  • Трассировка — OpenTelemetry, traceparent, sampling
  • Health checks — liveness, readiness, custom HealthIndicator
  • SLO и алерты — error budget и burn rate