В какой то момент разработки приложения, каждый из нас приходит к тому, что нам нужно больше информации о том, что происходит внутри приложения или же в возможности мониторить приложение. В случае с Play Framework уже существует готовое решение в виде отличной библиотеки с открытым исходным кодом Kamon в паре с модулем kamon-play.


Но сегодня мы собираемся взглянуть на альтернативное решение, интеграцию и использование Drowizard Metrics ранее известное как Codahale Metrics с Play Framework.



Интеграция


Так я начал искать готовые решения которые могли бы мне помочь в интеграции этих двух инструментов.


Я нашел несколько не полных решений:


  • metrics-scala — Отличная библиотека, изящный API c хорошей поддержкой Scala, но в случае с Play Framework не достаточно поддрежки.
  • metrics-play — Одно из первых решений которыми Google пытается удовлетворить ваш запрос, но эта библиотека уже не поддерживается и не совместима с последними версиями Play Framework и Dropwizard Metrics. Но имеется форк, который обновлен до последних версий, так что я решил попробовать его.

К сожалению модуль metrics-play предоставляет только базовый функционал из всего что имеется в среде Dropwizard Metrics. Это может быть достаточно, если вам нужны простые метрики которые доступны через REST api, но у меня были более высокие требования и я решил дополнить функционал этого модуля написав следующие модули:



Собственно об этом мы будем говорить далее.


Поддержка Metrics репортеров в Play Framework


Metrics предоставляет мощный инструментарий для мониторинга поведения критических компонентов в продакшн среде. Так же, предоставляет средства отправки измеренных данных через репортеров. Metrics репортеры — это отличный способ отправки данных из самого приложения в предпочитаемую систему хранения и визуализации метрик.


На момент написания статьи поддерживаемые репортеры следующие:


  • console — Периодически отправляет данные в стандартный поток выхода приложения
  • graphite — Периодически отправляет данные в Graphite.

Dropwizard Metrics и комьюнити также предосталяют другие репортеры, на пример Ganglia Reporter, CSV Reporter, InfluxDB Reporter, ElasticSearch Reporter и другие.


Добавление фэктори для поддержки репортеров в библиотеку является легкой задачей.


Поддержка аннотаций Metrics для Play Framework через Guice AOP


По умолчанию, для того что бы использовать метрики нужно вызвать Metric Registry для создания метрик, создать контекст и в ручную управлять им. На пример:


def doSomethingImportant() = {
    val timer = registry.timer(name(classOf[WebProxy], "get-requests"))
    val context = timer.time()
    try // critical business logic
    finally context.stop()
}

Что бы держать все DRY есть аннотации, модуль metrics-annotation-play будет создавать и надлежащим образом вызвать Timer для @Timed, Meter для @Metered, Counter для @Counted и Gauge для @Gauge. @ExceptionMetered тоже поддерживается, он создает Meter, который измеряет частоту выбрасывания исключений.


Предыдущий пример можно переписать следующим образом:


@Timed
def doSomethingImportant = {
    // critical business logic
}

или же можно сдекарировать весь класс, что создаст метрики для всех явных методов:


@Timed
class SuperCriticalFunctionality {
    def doSomethingImportant = {
        // critical business logic
    }
}

Данный функционал поддерживается только для классов созданных через Guice, так же имеются некоторые ограничения AOP.


Пример использования


Давайте попробуем использовать библиотеку в реальном приложении и рассмотрим, как все работает. Исходный код приложения можно найти тут.


Я использую шаблон activator play-scala с sbt plugin. Мы должны добавить JCenter в список resolvers и зависимости:


name := """play_metrics_example"""

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.11.8"

resolvers += Resolver.jcenterRepo

libraryDependencies ++= Seq(
  "de.khamrakulov.metrics-reporter-play" %% "reporter-core" % "1.0.0",
  "de.khamrakulov" %% "metrics-annotation-play" % "1.0.2",
  "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test
)

Для примера я использую сonsole репортер, давайте добавим конфигурацию в application.conf.


metrics {
  jvm = false
  logback = false

  reporters = [
    {
      type: "console"
      frequency: "10 seconds"
    }
  ]
}

Как вы видите я деактивировал метрики jvm и logback, что бы не потерять наши метрики и добавил репортер, который будет выводить метрики в stdout c периодичностью 10 секунд.


Теперь мы можем начать использовать аннотации, я сдекорирую метод index контроллера HomeController:


@Singleton
class HomeController @Inject() extends Controller {

  @Counted(monotonic = true)
  @Timed
  @Metered
  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

}

На самом деле вы не должны использовать все аннотации разом, т.к. @Timed комбинирует в себе Counter и Meter, но я это сдел��л для демонстрации возможностей.


После старта приложения и запроса Главной Страницы, репортер должен вывести метрики в stdout:


-- Counters --------------------------------------------------------------------
controllers.HomeController.index.current
             count = 1

-- Meters ----------------------------------------------------------------------
controllers.HomeController.index.meter
             count = 1
         mean rate = 0.25 events/second
     1-minute rate = 0.00 events/second
     5-minute rate = 0.00 events/second
    15-minute rate = 0.00 events/second

-- Timers ----------------------------------------------------------------------
controllers.HomeController.index.timer
             count = 1
         mean rate = 0.25 calls/second
     1-minute rate = 0.00 calls/second
     5-minute rate = 0.00 calls/second
    15-minute rate = 0.00 calls/second
               min = 14.59 milliseconds
               max = 14.59 milliseconds
              mean = 14.59 milliseconds
            stddev = 0.00 milliseconds
            median = 14.59 milliseconds
              75% <= 14.59 milliseconds
95% <= 14.59 milliseconds
98% <= 14.59 milliseconds
99% <= 14.59 milliseconds
99.9% <= 14.59 milliseconds

Конечно же вы все еще можете просмотреть метрики через REST api, для этого надо добавить конфигурацию в routes файл:


GET /admin/metrics com.kenshoo.play.metrics.MetricsController.metrics

Что дальше?


Автоматическая проверка работоспособности приложения (Health Checks)

Metrics так же поддерживает возможность использования автоматических проверок работоспособности приложения(health checks). Больше информации можно найти в официальной документации.


Больше репортеров

Для создания надлежащей среды использования метрик нужна поддержка большего количества репортеров. Это должно быть еще одним направлением развития библиотеки.


На��лежащая поддержка Future

На данный момент, что бы измерить время выполнения Future нужно в ручную выполнять все действия. Надлежащая поддержка Future может помочь в асинхронной среде Play Framework и может быть хорошим дополнением.


Поддержка HdrHistogram

Hdrhistogram предоставляет альтернативную реализацию коллектора (reservoir) высокого качества, который может быть использован для Histogram и Timer.