Current section
Files
Jump to
Current section
Files
lib/mobius.ex
defmodule Mobius do
@moduledoc """
Local telemetry metrics reporter
"""
use Supervisor
alias Mobius.Metrics.Table
alias Mobius.History
alias Telemetry.Metrics
@typedoc """
Arguments for the `Mobius` reporter
* `:metrics` - list of telemetry metrics `Mobius` should report (required)
* `:snapshot_interval` - the interval (in miliseconds) to record metric history
(optional, default: `1_000`)
* `:history_size` - number of metric records to keep in history (optional,
defaul: `500`)
* `:table_name` - the metrics table name (optional, deafult:
`Mobius.MetricsTable`)
"""
@type arg() ::
{:metrics, [Metrics.t()]}
| {:table_name, atom()}
| {:history_size, non_neg_integer()}
| {:snapshot_interval, non_neg_integer()}
@typedoc """
Options for print info
* `:table_name` - the name of the table that is used to store the current metrics
"""
@type info_opt() :: {:table_name, atom()}
@typedoc """
Options filer data for the plot
* `:event_name` - the telemetry event name
* `:metric` - the type of metric
* `:tags` - meta data tags and their values you want to filter on
* `:previous` - the previous `n` number of entries (defaults 90)
"""
@type plot_opt() ::
{:event_name, :telemetry.event_name()}
| {:metric, :counter | :last_value}
| {:tags, map()}
| {:previous, non_neg_integer()}
@spec start_link([arg()]) :: Supervisor.on_start()
def start_link(args) do
Supervisor.start_link(__MODULE__, args, name: Mobius.Supervisor)
end
@impl Supervisor
def init(args) do
args = Keyword.put_new_lazy(args, :table_name, fn -> Table end)
# by creating the ETS table here we tie it to the supervisor process
# so the table should stay around unless this supervisor crashes.
:ok = Table.init(args)
children = [
{Mobius.Reporter, args},
{Mobius.History, args}
]
Supervisor.init(children, strategy: :one_for_one)
end
@doc """
Plot metric information to the console
"""
@spec plot([plot_opt()]) :: :ok
def plot(opts \\ []) do
opts = Keyword.merge([previous: 90], opts)
opts
|> History.view()
|> Enum.group_by(&name_type_meta_group/1, &name_type_meta_group_value/1)
|> Enum.each(fn {{event_name, type, meta}, series} ->
{:ok, chart} = Mobius.Asciichart.plot(series, height: 10)
chart = [
"\t\t",
IO.ANSI.yellow(),
"Event: ",
make_event_name(event_name, type),
IO.ANSI.reset(),
", ",
IO.ANSI.magenta(),
"Metric: #{inspect(type)}, ",
IO.ANSI.cyan(),
"Tags: #{inspect(meta)}",
IO.ANSI.reset(),
"\n\n",
chart
]
IO.puts(chart)
end)
end
defp name_type_meta_group({_date_time, {name, type, _value, meta}}), do: {name, type, meta}
defp name_type_meta_group_value({_date_time, {_name, _type, value, _meta}}), do: value
defp make_event_name(event_name, :counter),
do: event_name |> Enum.take(length(event_name) - 1) |> Enum.join(".")
defp make_event_name(event_name, _type), do: Enum.join(event_name, ".")
@doc """
Print current metric information to the console
"""
@spec info([info_opt()]) :: :ok
def info(opts \\ []) do
opts
|> Keyword.get(:table_name, Table)
|> Table.get_entries()
|> Enum.group_by(fn {event_name, _type, _value, meta} -> {event_name, meta} end)
|> Enum.each(fn {{event_name, meta}, metrics} ->
reports =
Enum.map(metrics, fn {_event_name, type, value, _meta} ->
"#{to_string(type)}: #{inspect(value)}\n"
end)
[
"Event: ",
Enum.join(event_name, "."),
"\n",
"Tags: #{inspect(meta)}\n",
reports
]
|> IO.puts()
end)
end
end