packages feed

freckle-app-1.10.6.0: library/Freckle/App/Bugsnag/CallStack.hs

module Freckle.App.Bugsnag.CallStack
  ( callStackBeforeNotify
  , attachCallStack
  , callStackToStackFrames
  , callSiteToStackFrame

    -- * Re-exports
  , CallStack
  , SrcLoc
  , StackFrame
  ) where

import Freckle.App.Prelude

import Control.Exception.Annotated (annotatedExceptionCallStack)
import Data.Bugsnag (Exception (..), StackFrame (..), defaultStackFrame)
import qualified Data.Text as T
import Freckle.App.Exception.Types (AnnotatedException)
import GHC.Stack (CallStack, SrcLoc (..), getCallStack)
import Network.Bugsnag (BeforeNotify, updateExceptions)
import Network.Bugsnag.BeforeNotify (updateEventFromOriginalException)

-- | Copy the call stack from an AnnotatedException
callStackBeforeNotify :: BeforeNotify
callStackBeforeNotify =
  updateEventFromOriginalException @(AnnotatedException SomeException) $ \e ->
    foldMap attachCallStack $ annotatedExceptionCallStack e

attachCallStack :: CallStack -> BeforeNotify
attachCallStack cs =
  updateExceptions $ \ex ->
    ex {exception_stacktrace = callStackToStackFrames cs}

-- | Converts a GHC call stack to a list of stack frames suitable
--   for use as the stacktrace in a Bugsnag exception
callStackToStackFrames :: CallStack -> [StackFrame]
callStackToStackFrames = fmap callSiteToStackFrame . getCallStack

callSiteToStackFrame :: (String, SrcLoc) -> StackFrame
callSiteToStackFrame (str, loc) =
  defaultStackFrame
    { stackFrame_method = T.pack str
    , stackFrame_file = T.pack $ srcLocFile loc
    , stackFrame_lineNumber = srcLocStartLine loc
    , stackFrame_columnNumber = Just $ srcLocStartCol loc
    }