I have some code that allows me to run a series of functions. I currently surround the execution itself by a try-catch because I don’t want one execution crashing to cause all future executions to crash.
defmodule ES.Queue do
use GenServer
def start_link(base_opts, opts \\ []) do
defaults = [log_success: &noop/1, log_failure: &noop/1, timeout: 10_000]
{queue_opts, genserver_opts} =
defaults
|> Keyword.merge(base_opts)
|> Keyword.merge(opts)
|> Keyword.split([:queue_name, :timeout, :log_success, :log_failure])
GenServer.start_link(__MODULE__, Map.new(queue_opts), genserver_opts)
end
def run(pid, func), do: GenServer.call(pid, {:run, func}, 60_000)
def init(%{timeout: timeout}=state) do
{:ok, state, timeout}
end
def handle_call({:run, func}, _from, %{queue_name: queue_name, timeout: timeout, log_success: log_success, log_failure: log_failure}=state) do
resp =
try do
t1 = :erlang.monotonic_time()
val = execute(func)
t2 = :erlang.monotonic_time()
ms_taken = :erlang.convert_time_unit(t2 - t1, :native, :millisecond)
log_success.({queue_name, func, ms_taken})
val
catch
type, error ->
log_failure.({type, error, System.stacktrace()})
end
{:reply, resp, state, timeout}
end
def handle_call(:inspect, _from, %{timeout: timeout}=state) do
{:reply, Kernel.inspect(state), state, timeout}
end
def execute(func) when is_function(func), do: func.()
def execute({mod, func}), do: apply(mod, func, [])
def execute({mod, func, args}), do: apply(mod, func, args)
def noop(_), do: nil
def handle_info(:timeout, state) do
{:stop, :normal, state}
end
end
However there are a few problems with this approach:
- When an exception occurs during a
mix test
, the errors get swallowed. It becomes much harder to track down the error vs. when I remove the try catch. - If this code is in production, exceptions won’t get logged out or sent to an error reporting service like Rollbar
Basically, I still want exceptions to be loud. I still want the red error with the stacktrace to be printed out to the logs when I am running tests. I still want errors to get reported to an error reporting service while the app is running in production. What are some good options for this?
Thanks!