Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions lib/reactor/middleware/telemetry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ defmodule Reactor.Middleware.Telemetry do
* `[:reactor, :run, :stop]`
* `[:reactor, :step, :run, :start]`
* `[:reactor, :step, :run, :stop]`
* `[:reactor, :step, :guard, :start]`
* `[:reactor, :step, :guard, :stop]`
* `[:reactor, :step, :process, :start]`
* `[:reactor, :step, :process, :stop]`
* `[:reactor, :step, :compensate, :start]`
Expand Down Expand Up @@ -300,6 +302,73 @@ defmodule Reactor.Middleware.Telemetry do
)
end

def event({:guard_start, guard, arguments}, step, %{__MODULE__ => %{metadata: metadata}}) do
metadata =
metadata
|> Map.merge(%{
step: step,
guard: guard,
arguments: arguments,
status: :guard
})

start_time = System.monotonic_time()
Process.put({__MODULE__, :guard_start_time, step.name, guard}, start_time)

:telemetry.execute(
[:reactor, :step, :guard, :start],
%{system_time: System.system_time()},
metadata
)
end

def event({:guard_fail, guard, result}, step, %{__MODULE__ => %{metadata: metadata}}) do
metadata =
metadata
|> Map.merge(%{
step: step,
guard: guard,
result: result,
status: :error
})

start_time = Process.delete({__MODULE__, :guard_start_time, step.name, guard})
end_time = System.monotonic_time()
duration = end_time - start_time

:telemetry.execute(
[:reactor, :step, :guard, :stop],
%{
system_time: System.system_time(),
duration: duration
},
metadata
)
end

def event({:guard_pass, guard}, step, %{__MODULE__ => %{metadata: metadata}}) do
metadata =
metadata
|> Map.merge(%{
step: step,
guard: guard,
status: :ok
})

start_time = Process.delete({__MODULE__, :guard_start_time, step.name, guard})
end_time = System.monotonic_time()
duration = end_time - start_time

:telemetry.execute(
[:reactor, :step, :guard, :stop],
%{
system_time: System.system_time(),
duration: duration
},
metadata
)
end

def event({:compensate_start, reason}, step, %{__MODULE__ => %{metadata: metadata}}) do
metadata =
metadata
Expand Down
115 changes: 115 additions & 0 deletions test/reactor/middleware/telemetry_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defmodule Reactor.Middleware.TelemetryTest do
[
[:reactor, :run, :start],
[:reactor, :run, :stop],
[:reactor, :step, :guard, :start],
[:reactor, :step, :guard, :stop],
[:reactor, :step, :process, :start],
[:reactor, :step, :process, :stop],
[:reactor, :step, :run, :start],
Expand Down Expand Up @@ -187,4 +189,117 @@ defmodule Reactor.Middleware.TelemetryTest do
[:reactor, :run, :stop]
] = Enum.map(events, & &1.event)
end

test "guard pass events", %{table: table} do
defmodule GuardPassReactor do
@moduledoc false
use Reactor

middlewares do
middleware Reactor.Middleware.Telemetry
end

step :noop do
guard fn _args, _context ->
:cont
end

run fn _, _ -> {:ok, :noop} end
end
end

{:ok, :noop} = Reactor.run(GuardPassReactor)

events = get_events(table)

assert [
[:reactor, :run, :start],
[:reactor, :step, :process, :start],
[:reactor, :step, :guard, :start],
[:reactor, :step, :guard, :stop],
[:reactor, :step, :run, :start],
[:reactor, :step, :run, :stop],
[:reactor, :step, :process, :stop],
[:reactor, :run, :stop]
] = Enum.map(events, & &1.event)

guard_stop = Enum.at(events, 3)
assert guard_stop.metadata.status == :ok
end

test "guard halt events", %{table: table} do
defmodule GuardHaltReactor do
@moduledoc false
use Reactor

middlewares do
middleware Reactor.Middleware.Telemetry
end

step :guard do
guard fn _, _ ->
{:halt, {:error, :hodor}}
end

run fn _, _ -> {:ok, :noop} end
end
end

{:error, _} = Reactor.run(GuardHaltReactor)

events = get_events(table)

assert [
[:reactor, :run, :start],
[:reactor, :step, :process, :start],
[:reactor, :step, :guard, :start],
[:reactor, :step, :guard, :stop],
[:reactor, :step, :process, :stop],
[:reactor, :run, :stop]
] = Enum.map(events, & &1.event)

guard_stop = Enum.at(events, 3)
assert guard_stop.metadata.status == :error
assert guard_stop.metadata.result == {:error, :hodor}
end

test "guard fail events", %{table: table} do
defmodule GuardFailReactor do
@moduledoc false
use Reactor

middlewares do
middleware Reactor.Middleware.Telemetry
end

step :guard do
guard fn _, _ ->
raise "winter is coming"
end

run fn _, _ -> {:ok, :noop} end
end
end

{:error, _} = Reactor.run(GuardFailReactor)

events = get_events(table)

assert [
[:reactor, :run, :start],
[:reactor, :step, :process, :start],
[:reactor, :step, :guard, :start],
[:reactor, :step, :guard, :stop],
[:reactor, :step, :process, :stop],
[:reactor, :run, :stop]
] = Enum.map(events, & &1.event)

guard_stop = Enum.at(events, 3)
assert guard_stop.metadata.status == :error

assert match?(
{:error, %RuntimeError{message: "winter is coming"}},
guard_stop.metadata.result
)
end
end