bugsnag-1.1.0.0: src/Network/Bugsnag/BeforeNotify.hs
module Network.Bugsnag.BeforeNotify
( BeforeNotify
, beforeNotify
, runBeforeNotify
-- * Modifying the underlying Exceptions
, updateExceptions
, filterExceptions
, updateStackFrames
, filterStackFrames
, setStackFramesCode
, setStackFramesInProject
, setStackFramesInProjectByFile
, setStackFramesInProjectBy
-- * Modifying the Event
, updateEvent
, updateEventFromOriginalException
, setGroupingHash
, setGroupingHashBy
, setDevice
, setContext
, setRequest
, setWarningSeverity
, setErrorSeverity
, setInfoSeverity
) where
import Prelude
import qualified Control.Exception as Exception
import qualified Control.Exception.Annotated as Annotated
import Data.Bugsnag
import Data.Maybe (isJust)
import Data.Text (Text, unpack)
import Network.Bugsnag.CodeIndex
import Network.Bugsnag.StackFrame
-- | A function from 'Event' to 'Event' that is applied before notifying
--
-- The wrapped function also accepts the original exception, for cases in which
-- that's useful -- but it's often not. Most 'BeforeNotify's use 'updateEvent',
-- which discards it.
--
-- 'BeforeNotify' implements 'Semigroup' and 'Monoid', which means the /do
-- nothing/ 'BeforeNotify' is 'mempty' and two 'BeforeNotify's @doThis@ then
-- @doThat@ can be implemented as @doThat <> doThis@.
newtype BeforeNotify = BeforeNotify
{ _unBeforeNotify :: forall e. Exception.Exception e => e -> Event -> Event
}
instance Semigroup BeforeNotify where
BeforeNotify f <> BeforeNotify g = BeforeNotify $ \e -> f e . g e
instance Monoid BeforeNotify where
mempty = BeforeNotify $ const id
beforeNotify
:: (forall e. Exception.Exception e => e -> Event -> Event)
-> BeforeNotify
beforeNotify = BeforeNotify
runBeforeNotify :: Exception.Exception e => BeforeNotify -> e -> Event -> Event
runBeforeNotify (BeforeNotify f) = f
updateExceptions :: (Exception -> Exception) -> BeforeNotify
updateExceptions f = updateEvent $
\event -> event {event_exceptions = map f $ event_exceptions event}
filterExceptions :: (Exception -> Bool) -> BeforeNotify
filterExceptions p = updateEvent $ \event ->
event {event_exceptions = filter p $ event_exceptions event}
updateStackFrames :: (StackFrame -> StackFrame) -> BeforeNotify
updateStackFrames f = updateExceptions $
\e -> e {exception_stacktrace = map f $ exception_stacktrace e}
filterStackFrames :: (StackFrame -> Bool) -> BeforeNotify
filterStackFrames p = updateExceptions $
\e -> e {exception_stacktrace = filter p $ exception_stacktrace e}
setStackFramesCode :: CodeIndex -> BeforeNotify
setStackFramesCode =
(setStackFramesInProjectBy (isJust . stackFrame_code) <>)
. updateStackFrames
. attachBugsnagCode
setStackFramesInProject :: Bool -> BeforeNotify
setStackFramesInProject = setStackFramesInProjectBy . const
setStackFramesInProjectByFile :: (FilePath -> Bool) -> BeforeNotify
setStackFramesInProjectByFile f =
setStackFramesInProjectBy $ f . unpack . stackFrame_file
setStackFramesInProjectBy :: (StackFrame -> Bool) -> BeforeNotify
setStackFramesInProjectBy f =
updateStackFrames $ \sf -> sf {stackFrame_inProject = Just $ f sf}
updateEvent :: (Event -> Event) -> BeforeNotify
updateEvent f = beforeNotify $ \_e event -> f event
-- | Update the 'Event' based on the original exception
--
-- This allows updating the Event after casting to an exception type that this
-- library doesn't know about (e.g. @SqlError@). Because the result of your
-- function is itself a 'BeforeNotify', you can (and should) use other
-- helpers:
--
-- @
-- myBeforeNotify =
-- 'defaultBeforeNotify'
-- <> 'updateEventFromOriginalException' asSqlError
-- <> 'updateEventFromOriginalException' asHttpError
-- <> -- ...
--
-- asSqlError :: SqlError -> BeforeNotify
-- asSqlError SqlError{..} =
-- 'setGroupingHash' sqlErrorCode <> 'updateException' (\e -> e
-- { exception_errorClass = sqlErrorCode
-- , exception_message = Just sqlErrorMessage
-- })
-- @
--
-- If the cast fails, the event is unchanged.
--
-- The cast will match either @e@ or @'AnnotatedException' e@.
updateEventFromOriginalException
:: forall e. Exception.Exception e => (e -> BeforeNotify) -> BeforeNotify
updateEventFromOriginalException f = beforeNotify $ \e event ->
let bn =
maybe mempty (f . Annotated.exception) $
Exception.fromException $
Exception.toException e
in runBeforeNotify bn e event
setGroupingHash :: Text -> BeforeNotify
setGroupingHash hash = setGroupingHashBy $ const $ Just hash
setGroupingHashBy :: (Event -> Maybe Text) -> BeforeNotify
setGroupingHashBy f =
updateEvent $ \event -> event {event_groupingHash = f event}
-- | Set the Event's Context
setContext :: Text -> BeforeNotify
setContext context =
updateEvent $ \event -> event {event_context = Just context}
-- | Set the Event's Request
--
-- See 'bugsnagRequestFromWaiRequest'
setRequest :: Request -> BeforeNotify
setRequest request =
updateEvent $ \event -> event {event_request = Just request}
-- | Set the Event's Device
--
-- See 'bugsnagDeviceFromWaiRequest'
setDevice :: Device -> BeforeNotify
setDevice device = updateEvent $ \event -> event {event_device = Just device}
-- | Set to 'ErrorSeverity'
setErrorSeverity :: BeforeNotify
setErrorSeverity = setSeverity errorSeverity
-- | Set to 'WarningSeverity'
setWarningSeverity :: BeforeNotify
setWarningSeverity = setSeverity warningSeverity
-- | Set to 'InfoSeverity'
setInfoSeverity :: BeforeNotify
setInfoSeverity = setSeverity infoSeverity
setSeverity :: Severity -> BeforeNotify
setSeverity severity =
updateEvent $ \event -> event {event_severity = Just severity}