venkatd

venkatd

Best practices on exception handling and tracking (let it crash vs. try catch tradeoffs)

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!

Most Liked

cmkarlsson

cmkarlsson

How about doing the execution in another process? It is not uncommon to do this for things that can fail. Your GenServer spawns and waits for the result from the execution. It also monitors the newly spawned process for failures. The execute are still synchronized, the monitoring GenServer will not fail and continues with the other cases even if one fails.

I’m not sure what sort of error logging you are looking for when something crashes but perhaps if the execution is done according to OTP standards (either a GenServer or a process started with proc_lib) I think you may get SASL logging which Logger may pick up.

sasajuric

sasajuric

Author of Elixir In Action

You can log the error directly like this:

try do
  # ...
catch
  type, error ->
    Logger.error(Exception.format(type, error, __STACKTRACE__))
end

That said, I agree with @cmkarlsson’s proposal. If you want to ensure that “execution” doesn’t take the GenServer down, doing it in a separate process would give you the strongest guarantees of that.

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement