# A method for getting stack traces in Servant apps

**URL:** <https://discourse.haskell.org/t/a-method-for-getting-stack-traces-in-servant-apps/6091>\
**Category:** Show and Tell\
**Created:** [April 4, 2023, 7:44pm UTC](https://discourse.haskell.org/t/a-method-for-getting-stack-traces-in-servant-apps/6091 "2023-04-04T19:44:43Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [April 4, 2023, 7:44pm UTC](https://discourse.haskell.org/t/a-method-for-getting-stack-traces-in-servant-apps/6091/1 "2023-04-04T19:44:43Z")

</div>

I was thinking about how to get some sort of stack trace when a [Servant](https://hackage.haskell.org/package/servant-server) handler throws an exception (like, say, an [`IOException`](https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Exception-Base.html#t:IOException)).

I was ok with it not being automatic, requiring manual annotation of each function.

I was not ok with changing the type of the exception while inside the handler, or having to change [throw](https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Exception-Base.html#v:throwIO)/[catch](https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Exception.html#v:catch) sites inside the handler. That would require some careful thought, because it might break some assumption a layer makes about the exceptions thrown by a lower layer.

I came up with [this](https://github.com/danidiaz/artisanal-servant-exceptions/tree/v1). The basic idea is, for each request, [allocate](https://github.com/danidiaz/artisanal-servant-exceptions/blob/907131f6f53f04097ed0c78722f6e3da101e6001/app/StackTrace.hs#L22) an `IORef` that will hold the stack trace for the thread servicing the request:

```haskell
-- | Just a convenient type synonym
type RIO e = ReaderT e IO

type Annotation = String

type StackTrace = [Annotation]

type StackTraceRef = IORef StackTrace

with :: (StackTraceRef -> IO r) -> IO r
with f = do
  ref <- newIORef []
  er <- try @SomeException (f ref)
  case er of
    Left exception -> case fromException @SomeAsyncException exception of
      Just _ -> do
        -- Asynchronous exceptions pass through undecorated.
        throwIO exception
      Nothing -> do
        stackTrace <- readIORef ref
        throwIO (ExceptionWithStackTrace stackTrace exception)
    Right r -> pure r

```

Where the [decorated exception type](https://github.com/danidiaz/artisanal-servant-exceptions/blob/907131f6f53f04097ed0c78722f6e3da101e6001/app/StackTrace.hs#L52) is

```haskell
data ExceptionWithStackTrace
  = ExceptionWithStackTrace StackTrace SomeException
  deriving (Show)

instance Exception ExceptionWithStackTrace

```

So, when we want to add some debug context to a function, we can use [`annotate`](https://github.com/danidiaz/artisanal-servant-exceptions/blob/907131f6f53f04097ed0c78722f6e3da101e6001/app/StackTrace.hs#L41):

```haskell
annotate :: Annotation -> RIO StackTraceRef a -> RIO StackTraceRef a
annotate frame action = do
  ref <- ask
  let clear = liftIO $ modifyIORef' ref (const [])
      add = liftIO $ modifyIORef' ref (frame :)
  clear
  r <- withRunInIO $ \runInIO ->
    runInIO action `onException` runInIO add
  clear
  pure r

```

Note that `annotate` only adds the annotation to the ref’s contents when an exception bubbles up. Normal entries and exits clear the ref’s contents.

How to use this in a Servant server? Imagine we have these “components”

```haskell
newtype Foo m = Foo {runFoo :: m ()}

newtype Bar m = Bar {runBar :: m ()}

newtype Baz m = Baz {runBaz :: m ()}

makeFoo :: Bar m -> Foo m
makeFoo bar = Foo {runFoo = runBar bar}

makeBar :: Baz m -> Bar m
makeBar baz = Bar {runBar = runBaz baz}

makeBaz :: Baz (RIO e)
makeBaz = Baz {runBaz = liftIO $ throwIO $ userError "some exception"}

```

And this trivial Servant API definition

```haskell
type API = PostNoContent

makeFooServer :: Foo (RIO StackTraceRef) -> ServerT API (RIO StackTraceRef)
makeFooServer foo = runFoo foo $> NoContent

```

We can wire the components and run the server like this

```haskell
main :: IO ()
main = do
  let fooServer = makeFooServer foo
      -- Construct the compoents and add the stack trace annoations
      foo = makeFoo bar & \Foo {runFoo} -> Foo {runFoo = StackTrace.annotate "runFoo" runFoo}
      bar = makeBar baz & \Bar {runBar} -> Bar {runBar = StackTrace.annotate "runBar" runBar}
      baz = makeBaz & \Baz {runBaz} -> Baz {runBaz = StackTrace.annotate "runBaz" runBaz}
      -- We allocate an stack trace ref per request, when hoisting the server.
      hoistRequest action = Servant.Handler $ lift $ StackTrace.with $ runReaderT action
  run 8000 $ serve (Proxy @API) $ hoistServer (Proxy @API) hoistRequest fooServer

```

Note that we call [`StackTrace.with`](https://github.com/danidiaz/artisanal-servant-exceptions/blob/907131f6f53f04097ed0c78722f6e3da101e6001/app/StackTrace.hs#L22) in the function that we pass to [`hoistServer`](https://hackage.haskell.org/package/servant-server-0.19.2/docs/Servant-Server.html#v:hoistServer). It will run for each request.

If we start the server and perform a `curl -v -X POST localhost:8000`, the following will be logged:

```haskell
ExceptionWithStackTrace ["runFoo","runBar","runBaz"] user error (some exception)

```

This method has (at least one) [problem](https://github.com/danidiaz/artisanal-servant-exceptions/blob/907131f6f53f04097ed0c78722f6e3da101e6001/app/Main.hs#L35). If we catch an exception inside a `StackTrace.annotate` and throw a different exception without having exited the `annotate`, the old exception will remain in the stack trace 😢. I don’t know hot to solve this. 🤔

Are there other problems with this method? What are other methods methods for getting “stack trace”-like context for uncaught exceptions in Servant?

---

<div class="post-metadata">

**Author:** ![Swordlash](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/swordlash/32/4604_2.png) [@Swordlash](https://discourse.haskell.org/u/Swordlash)\
**Post date:** [April 4, 2023, 9:13pm UTC](https://discourse.haskell.org/t/a-method-for-getting-stack-traces-in-servant-apps/6091/2 "2023-04-04T21:13:05Z")

</div>

I’m not sure at which point does `wai` handle the exceptions, but isn’t it much easier to write using middleware?

---

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [April 5, 2023, 6:23am UTC](https://discourse.haskell.org/t/a-method-for-getting-stack-traces-in-servant-apps/6091/3 "2023-04-05T06:23:33Z")

</div>

I could allocate the `IORef` in middleware, but I wouldn’t know how to pass it down to the `Application` and to the handlers that make use of it.

---

<div class="post-metadata">

**Author:** ![velveteer](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/velveteer/32/3474_2.png) [@velveteer](https://discourse.haskell.org/u/velveteer)\
**Post date:** [July 19, 2023, 2:23pm UTC](https://discourse.haskell.org/t/a-method-for-getting-stack-traces-in-servant-apps/6091/4 "2023-07-19T14:23:40Z")

</div>

Regarding the middleware approach, I think you could put the `IORef` in the WAI `Vault`: [Network.Wai](https://hackage.haskell.org/package/wai-3.2.3/docs/Network-Wai.html#v:vault). Then you could access it in Servant handlers using the `Vault` type: [Servant.API](https://hackage.haskell.org/package/servant-0.20/docs/Servant-API.html#t:Vault)
