Packages

Telemetry.Metrics reporter for riemann-compatible metric servers

Current section

Files

Jump to
telemetry_metrics_riemann lib telemetry_metrics_riemann.ex
Raw

lib/telemetry_metrics_riemann.ex

defmodule TelemetryMetricsRiemann do
@moduledoc """
`Telemetry.Metrics` reporter for riemann-compatible metric servers.
To use it, start the reporter with the `start_link/1` function, providing it a list of
`Telemetry.Metrics` metric definitions:
import Telemetry.Metrics
TelemetryMetricsStatsd.start_link(
metrics: [
counter("http.request.count"),
sum("http.request.payload_size"),
last_value("vm.memory.total")
]
)
> Note that in the real project the reporter should be started under a supervisor, e.g. the main
> supervisor of your application.
By default the reporter sends metrics to localhost:8125 - both hostname and port number can be
configured using the `:host` and `:port` options.
Note that the reporter doesn't aggregate metrics in-process - it sends metric updates to StatsD
whenever a relevant Telemetry event is emitted.
## Translation between Telemetry.Metrics and riemann
TBD
### Counter
TBD
### Last value
TBD
### Sum
TBD
### Distribution
TBD
## Prefixing metric names
Sometimes it's convenient to prefix all metric names with particular value, to group them by the name of the service,
the host, or something else. You can use `:prefix` option to provide a prefix which will be
prepended to all metrics published by the reporter.
"""
use GenServer
require Logger
alias Telemetry.Metrics
alias TelemetryMetricsRiemann.EventHandler
@type option ::
{:client, Atom}
| {:metrics, [Metrics.t()]}
| {:prefix, String.t()}
@type options :: [option]
@doc """
Reporter's child spec.
This function allows you to start the reporter under a supervisor like this:
children = [
{TelemetryMetricsRiemann, options}
]
See `start_link/1` for a list of available options.
"""
@spec child_spec(options) :: Supervisor.child_spec()
def child_spec(options) do
%{id: __MODULE__, start: {__MODULE__, :start_link, [options]}}
end
@doc """
Starts a reporter and links it to the calling process.
The available options are:
* `:metrics` - a list of Telemetry.Metrics metric definitions which will be published by the
reporter
* `:client` - the module that implements riemann client interface
* `:prefix` - a prefix prepended to the name of each metric published by the reporter. Defaults
to `nil`.
You can read more about all the options in the `TelemetryMetricsRiemann` module documentation.
## Example
import Telemetry.Metrics
TelemetryMetricsRiemann.start_link(
metrics: [
counter("http.request.count"),
sum("http.request.payload_size"),
last_value("vm.memory.total")
],
prefix: "my-service",
client: TelemetryMetricsRiemann.Riemannx
)
"""
@spec start_link(options) :: GenServer.on_start()
def start_link(options) do
GenServer.start_link(__MODULE__, options)
end
@doc false
@spec client_error(pid(), reason :: term) :: :ok
def client_error(reporter, reason) do
GenServer.cast(reporter, {:client_error, reason})
end
@impl true
def init(options) do
host = options |> Keyword.get(:host) |> maybe_get_hostname()
metrics = Keyword.fetch!(options, :metrics)
client = Keyword.get(options, :client)
prefix = Keyword.get(options, :prefix)
Process.flag(:trap_exit, true)
handler_ids = EventHandler.attach(metrics, self(), client, prefix, host)
{:ok, %{handler_ids: handler_ids, client: client}}
end
@impl true
def handle_cast({:client_error, reason}, state) do
Logger.error("Failed to publish metrics using riemann client: #{inspect(reason)}")
{:noreply, state}
end
@impl true
def handle_info({:EXIT, _pid, reason}, state) do
{:stop, reason, state}
end
@impl true
def terminate(_reason, state) do
EventHandler.detach(state.handler_ids)
:ok
end
defp maybe_get_hostname(nil) do
:inet.gethostname() |> elem(1) |> List.to_string()
end
defp maybe_get_hostname(host) when is_list(host) do
List.to_string(host)
end
defp maybe_get_hostname(host) when is_binary(host) do
host
end
defp maybe_get_hostname(host) do
raise ArgumentError, message: "The #{inspect(host)} is invalid"
end
end