简体   繁体   中英

Automatically print info including stacktrace for uncaught errors in Elixir

I am currently learning Elixir and setting up a toy REST service example using Plug . Here is the code that I wrote initially. It has a bug on the row that generates a random number. I was able to figure out the issue. However, when the error occurs there's no helpful info to figure out what the problem is.

defmodule HelloWorld.Web do
  use Plug.Router

  plug :match
  plug :dispatch

  get "/random" do
    conn = Plug.Conn.fetch_query_params(conn)
    max = Integer.parse(Map.fetch!(conn.params, "max"))
    r = Enum.random(0..max) # This throws an error
    conn
    |> Plug.Conn.put_resp_content_type("text/plain")
    |> Plug.Conn.send_resp(200, Integer.to_string(r))
  end

  def child_spec(_) do
    Plug.Adapters.Cowboy.child_spec(
      scheme: :http,
      options: [port: 8080],
      plug: __MODULE__)
  end

end

To figure out the problem, I had to modify the original and add a try/rescue block and print out the error info and stacktrace, which gave me a meaningful error message. So I was able to figure out that Integer.parse also returns the remainder.

defmodule HelloWorld.Web do
  require Logger
  use Plug.Router

  plug :match
  plug :dispatch

  get "/random" do
    try do
      conn = Plug.Conn.fetch_query_params(conn)
      max = Integer.parse(Map.fetch!(conn.params, "max"))
      r = Enum.random(0..max)
      conn
      |> Plug.Conn.put_resp_content_type("text/plain")
      |> Plug.Conn.send_resp(200, Integer.to_string(r))
    rescue
      err ->
        Logger.error(Exception.format(:error, err, __STACKTRACE__))
      reraise err, __STACKTRACE__
    end
  end

  def child_spec(_) do
    Plug.Adapters.Cowboy.child_spec(
      scheme: :http,
      options: [port: 8080],
      plug: __MODULE__)
  end

end

Is there any way for Elixir to do this automatically when it encounters an uncaught error just before it terminates the process?

Update: I ran the app as the book suggested by running iex -S mix . When I curl the URL, I see the following error in the console:

17:35:12.218 [error] #PID<0.386.0> running HelloWorld.Web (cowboy_protocol) terminated
Server: localhost:8080 (http)
Request: GET /random?max=100
** (exit) {%Plug.Conn.WrapperError{conn: %Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...}, assigns: %{}, body_params: %Plug.Conn.Unfetched{aspect: :body_params}, cookies: %Plug.Conn.Unfetched{aspect: :cookies}, halted: false, host: "localhost", method: "GET", owner: #PID<0.386.0>, params: %{}, path_info: ["random"], path_params: %{}, port: 8080, private: %{plug_route: {"/random", #Function<1.75067752/2 in HelloWorld.Web.do_match/4>}}, query_params: %Plug.Conn.Unfetched{aspect: :query_params}, query_string: "max=100", remote_ip: {127, 0, 0, 1}, req_cookies: %Plug.Conn.Unfetched{aspect: :cookies}, req_headers: [{"mime-version", "1.0"}, {"connection", "keep-alive"}, {"extension", "Security/Digest Security/SSL"}, {"host", "localhost:8080"}, {"accept-encoding", "gzip"}, {"accept", "*/*"}, {"content-length", "0"}], request_path: "/random", resp_body: nil, resp_cookies: %{}, resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}], scheme: :http, script_name: [], secret_key_base: nil, state: :unset, status: nil}, kind: :error, reason: %ArgumentError{message: "ranges (first..last) expect both sides to be integers, got: 0..{100, \"\"}"}, stack: [{Range, :new, 2, [file: 'lib/range.ex', line: 193]}, {HelloWorld.Web, :"-do_match/4-fun-1-", 2, [file: 'lib/hello_world/web.ex', line: 10]}, {HelloWorld.Web, :"-dispatch/2-fun-0-", 4, [file: 'lib/plug/router.ex', line: 246]}, {:telemetry, :span, 3, [file: '/projects/hello_world/deps/telemetry/src/telemetry.erl', line: 321]}, {HelloWorld.Web, :dispatch, 2, [file: 'lib/plug/router.ex', line: 242]}, {HelloWorld.Web, :plug_builder_call, 2, [file: 'lib/hello_world/web.ex', line: 1]}, {Plug.Adapters.Cowboy.Handler, :upgrade, 4, [file: 'lib/plug/cowboy/handler.ex', line: 18]}, {:cowboy_protocol, :execute, 4, [file: '/projects/hello_world/deps/cowboy/src/cowboy_protocol.erl', line: 442]}]}, []}

It heavily depends on how do you test it. Generally speaking, errors when the process crashes are dumped out to the default stderr .

This is the excerpt from my iex session testing it.

iex|💧|1 ▶ Mix.install([:plug_cowboy]) 
iex|💧|2 ▶ … # COPY-PASTE OF YOUR CODE
iex|💧|3 ▶ {:ok, pid} = Supervisor.start_link([{Plug.Cowboy, scheme: :http, plug: HelloWorld.Web, options: [port: 4040]}], strategy: :one_for_one)
{:ok, #PID<0.297.0>}
# Go access http://localhost:4040/random/?max=1000
iex|💧|4 ▶ 
13:37:21.346 [error] #PID<0.403.0> running HelloWorld.Web (connection #PID<0.402.0>, stream id 1) terminated
Server: localhost:4040 (http)
Request: GET /random/?max=1000
** (exit) an exception was raised:
    ** (ArgumentError) ranges (first..last) expect both sides to be integers, got: 0..{1000, ""}
        (elixir 1.14.3) lib/range.ex:193: Range.new/2
        iex:11: anonymous fn/2 in HelloWorld.Web.do_match/4
        lib/plug/router.ex:246: anonymous fn/4 in HelloWorld.Web.dispatch/2
        lib/plug/router.ex:242: HelloWorld.Web.dispatch/2
        iex:2: HelloWorld.Web.plug_builder_call/2
        (plug_cowboy 2.6.0) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
        (cowboy 2.9.0) 
        …

You also can test the router as shown in the doc Plug.Router , which would also dump the error message.

That said, it's unclear what suppressed it in your environment, but it should have been printed by default.

I don't know why you are not seeing the errors in the console, I get similar results to Aleksei. Here's a couple of other pointers.


You can prevent some logic errors at write-time by using elixir-ls and Dialyzer . Here is how your code looks when I open it in my editor:

The warning is a bit cryptic, but it's telling you that max should be an integer, but it's analysed that max will either be an {integer, binary} tuple or :error .


Is there any way for Elixir to do this automatically when it encounters an uncaught error just before it terminates the process?

If I add use Plug.Debugger to your router, loading the page produces this:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM