Skip to content

Cookbook: Prometheus integration

Mike R edited this page Aug 9, 2019 · 4 revisions

Because prometheus.erl requires declaring metrics before using them you need to call Tesla.Middleware.Prometheus.setup() before your application starts.

Requires {:prometheus, "~> 4.0"} dependency.

defmodule Tesla.Middleware.Prometheus do
  @moduledoc """
  Prometheus metrics

  Usage:
      defmodule MyApi do
        use Tesla

        plug Tesla.Middleware.Prometheus
      end

      # lib/my_app/application.ex
      defmodule MyApp.Application do
        def start(_type, _args) do
          Tesla.Middleware.Prometheus.setup()

          # ...
        end
      end


  Metrics:
    - tesla_request_duration_milliseconds{host,status}
  """

  @buckets [
    1,
    2,
    4,
    8,
    16,
    32,
    64,
    128,
    256,
    512,
    1024,
    2048,
    4096
  ]

  def setup do
    :prometheus_histogram.declare(
      name: :tesla_request_duration_milliseconds,
      help: "Time spent executing the request in milliseconds",
      labels: [:host, :status],
      buckets: @buckets
    )
  end

  def call(env, next, _opts) do
    start = System.monotonic_time()
    response = Tesla.run(env, next)
    stop = System.monotonic_time()
    time_ms = System.convert_time_unit(stop - start, :native, :millisecond)

    labels = [
      URI.parse(env.url).host,
      status(response)
    ]

    :prometheus_histogram.observe(:tesla_request_duration_milliseconds, labels, time_ms)

    response
  end

  defp status({:ok, %{status: status}}), do: status
  defp status(_error), do: "error"
end