Current section

Files

Jump to
electric lib electric interval_timer.ex
Raw

lib/electric/interval_timer.ex

defmodule Electric.Telemetry.IntervalTimer do
@moduledoc """
Times intervals between calls to `start_interval/2`. This is useful if you
want to find out which part of a process took the longest time. It works
out simpler than wrapping each part of the process in a timer, and
guarentees no gaps in the timings.
The simplest way to use the timer is to store the timer state in the
process memory, see `OpenTelemetry.start_interval`. This module should
only be used directly if you do not want to use the process memory.
"""
@type t() :: [{term(), non_neg_integer()}]
def init, do: []
def start_interval(state, interval_name, opts \\ [])
def start_interval(nil, _, _), do: nil
def start_interval(state, interval_name, opts) do
[{interval_name, time(opts)} | state]
end
def durations(state, opts \\ [])
def durations(nil, _), do: %{}
def durations(state, opts) do
calculate_durations(state, time(opts))
|> Enum.group_by(
fn {interval_name, _} -> interval_name end,
fn {_, duration} -> duration end
)
|> Map.new(fn {interval_name, durations} ->
{interval_name, Enum.sum(durations)}
end)
end
def total_time(durations) when durations == %{}, do: 0
def total_time(durations) do
durations
|> Enum.map(fn {_, duration} -> duration end)
|> Enum.sum()
end
defp calculate_durations([{interval_name, start_time} | rest], end_time) do
duration = {interval_name, end_time - start_time}
# since we're moving backwards through the intervals, the next interval's end time
# is this interval's start time:
[duration | calculate_durations(rest, _next_interval_end_time = start_time)]
end
defp calculate_durations([], _), do: []
defp time(opts) do
opts[:time] || time()
end
def time, do: System.monotonic_time(:microsecond)
end