Packages

Small wrappers to enhance usage of the telemetry library.

Current section

Files

Jump to
telemetry_wrappers lib telemetry_wrappers.ex
Raw

lib/telemetry_wrappers.ex

defmodule TelemetryWrappers do
@moduledoc """
Documentation for TelemetryWrappers.
"""
defmacro __using__(_opts) do
quote do
import TelemetryWrappers, only: [deftimed: 2, deftimed: 3, deftimedp: 2, deftimedp: 3]
require TelemetryWrappers
end
end
@doc """
Defines a function that will have its execution time measured and sent as a telemetry event.
As an example you can define the following module
defmodule TelemetryWrappers.Support.TestModule do
use TelemetryWrappers
deftimed timed_function(a, b), [:a, :b] do
a + b
end
deftimed timed_function2(a, b) do
a + b
end
end
Then both functions will work as expected:
iex> TelemetryWrappers.Support.TestModule.timed_function(1, 2)
3
iex> TelemetryWrappers.Support.TestModule.timed_function2(1, 2)
3
but it will also emit a `:telemetry` event `[:a, :b]` with the contents `%{call: timing}`
where `timing` is the time the function took to execute in microseconds.
The metric name is optional and will default to `[:timing, name]` where `name` is the name of the function (without arity).
Note that type specs can still be defined for function defined with `deftimed` just as you would normally, e.g.
@spec timed_function(number(), number()) :: number()
deftimed timed_function(a, b), [:a, :b] do
a + b
end
"""
defmacro deftimed(function_name, metric_name \\ [], do: expr) do
{fname, _, _} = function_name
actual_name = get_actual_name(metric_name, fname)
quote do
def unquote(function_name) do
{timing, result} = :timer.tc(fn -> unquote(expr) end)
:telemetry.execute(unquote(actual_name), %{call: timing})
result
end
end
end
@doc """
Defines a private function that will have its execution time measured and sent as a telemetry event.
In principle, same as `deftimed/3` but defines a private function instead.
As an example you can define the following module
defmodule TelemetryWrappers.Support.TestModule do
use TelemetryWrappers
def invoke_private(a) do
private_fun(a)
end
deftimedp(private_fun(a), [:something], do: a)
end
And then invoke the function:
iex> TelemetryWrappers.Support.TestModule.invoke_private(15)
15
which will also emit a `:telemetry` event `[:something]` with the contents `%{call: timing}`
where `timing` is the time the function took to execute the private function in microseconds.
The metric name is optional and will default to `[:timing, name]` where `name` is the name of the function (without arity),
just like in `deftimed/3`
"""
defmacro deftimedp(function_name, metric_name \\ [], do: expr) do
{fname, _, _} = function_name
actual_name = get_actual_name(metric_name, fname)
quote do
defp unquote(function_name) do
{timing, result} = :timer.tc(fn -> unquote(expr) end)
:telemetry.execute(unquote(actual_name), %{call: timing})
result
end
end
end
defp get_actual_name([], name), do: [:timing, name]
defp get_actual_name(metric, _), do: metric
end