rgrinberg / opium

Sinatra like web toolkit for OCaml
MIT License
755 stars 67 forks source link

Backtraces in middleware #208

Closed sapristi closed 4 years ago

sapristi commented 4 years ago

Hello, I have issues to get backtraces from a middleware.

Here's a full example:

open Opium.Std
let () = Printexc.record_backtrace true;;

let re_raise = fun () ->
  let pattern = "AA" in
  let cre = Re.compile (Re.Perl.re pattern) in
  Re.exec cre "aa"

let wrapper_re = fun () -> re_raise () |> ignore

let incode_raise : unit -> unit = fun () -> failwith "someing bad"

let wrapper_incode = fun () -> incode_raise ()

let handle_exc =
  let filter : (Opium_kernel.Request.t, Opium_kernel.Response.t)
      Opium_kernel__Rock.Filter.simple  = fun handler req ->
    (
      let handler' = fun req ->
        try%lwt
          handler req
        with
        | _ as e ->
          let backtrace = Printf.sprintf "An error happened while treating the request:\n%s\n%s"
            (Printexc.get_backtrace ())
            (Printexc.to_string e)
          in
          (`String backtrace)  |> respond'
      in
      handler' req
    )
  in
  Rock.Middleware.create ~name:"handle_exc" ~filter

let run  =
  App.empty
  |> App.port 1234
  |> middleware handle_exc
  |> get "/re_raise" (fun _ -> wrapper_re (); `String "ok" |> respond')
  |> get "/incode_raise" (fun _ -> wrapper_incode (); `String "ok" |> respond')
  |> App.start

let _ = Lwt_main.run run

Whichever the route, i get the following backtrace:

An error happened while treating the request:
Raised at file "string.ml", line 115, characters 19-34
Called from file "src/sexp.ml", line 113, characters 13-4

(the exception name below that is the right one, no problem here).

You will notice one route is raising an exception from the ocaml-re module, while the other is raising directly with failwith. This is because in my usecase I noticed that the backtrace was the right one when I used failwith in my code, but was the same one as given here when an exception was raised by ocaml-re.

Now I don't manage to reproduce a working backtrace, neither in my usecase, nor in the given example, so I don't really know what to make of all this...

Do you have any clues as to what could be going wrong here ?

Context

The code is compiled with the following dune file:

(executable
 (name test_opium)
 (preprocess
  (pps lwt_ppx))
 (libraries re lwt lwt_ppx lwt.unix opium)
)

dune is version 2.7.1, opium is version 0.18.0, and ocaml is 4.08.1

sapristi commented 4 years ago

Ok it turns out I was just using Printexc the wrong way.

Using the following middleware gives the right backtrace:

let handle_exc =
  let filter : (Opium_kernel.Request.t, Opium_kernel.Response.t)
      Opium_kernel__Rock.Filter.simple  = fun handler req ->
    (
      let handler' = fun req ->
        try%lwt
          Lwt.bind (Lwt.return req) handler
        with
        | _ as e ->
          let backtrace = (Printexc.get_backtrace ()) in
          let backtrace_msg = Printf.sprintf "An error happened while treating the request:\n%s\n%s"
              backtrace
              (Printexc.to_string e)
          in
          (`String backtrace_msg)  |> respond'
      in
      handler' req
    )
  in
  Rock.Middleware.create ~name:"handle_exc" ~filter