packages feed

bugsnag-haskell 0.0.1.2 → 0.0.1.3

raw patch · 5 files changed

+146/−70 lines, 5 filesdep +unliftiodep ~aesonPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: unliftio

Dependency ranges changed: aeson

API changes (from Hackage documentation)

+ Network.Bugsnag.BeforeNotify: defaultBeforeNotify :: BeforeNotify
+ Network.Bugsnag.BeforeNotify: filterStackFrames :: (BugsnagStackFrame -> Bool) -> BeforeNotify
+ Network.Bugsnag.BeforeNotify: redactRequestHeaders :: [HeaderName] -> BeforeNotify
+ Network.Bugsnag.BeforeNotify: setGroupingHash :: Text -> BeforeNotify
+ Network.Bugsnag.BeforeNotify: setGroupingHashBy :: (BugsnagEvent -> Maybe Text) -> BeforeNotify
+ Network.Bugsnag.BeforeNotify: setStackFramesInProject :: (FilePath -> Bool) -> BeforeNotify
- Network.Bugsnag.Settings: BugsnagSettings :: BugsnagApiKey -> Maybe Text -> BugsnagReleaseStage -> [BugsnagReleaseStage] -> BeforeNotify -> (BugsnagException -> Bool) -> (BugsnagEvent -> Maybe Text) -> (FilePath -> Bool) -> (BugsnagStackFrame -> Bool) -> Manager -> BugsnagSettings
+ Network.Bugsnag.Settings: BugsnagSettings :: BugsnagApiKey -> Maybe Text -> BugsnagReleaseStage -> [BugsnagReleaseStage] -> BeforeNotify -> BugsnagException -> Bool -> BugsnagEvent -> Maybe Text -> FilePath -> Bool -> BugsnagStackFrame -> Bool -> Manager -> BugsnagSettings
- Network.Bugsnag.Settings: bugsnagShouldNotify :: BugsnagSettings -> BugsnagException -> Bool
+ Network.Bugsnag.Settings: bugsnagShouldNotify :: BugsnagSettings -> BugsnagEvent -> Bool

Files

bugsnag-haskell.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 7bf291abde6fe3bc6aa43132c387532dae5ab319f44295932acaefa836a13869+-- hash: 36962ecc46dfa7d5ab30da6797310d06145ecdd5d2d487ab6f1ec237b259e721  name:           bugsnag-haskell-version:        0.0.1.2+version:        0.0.1.3 synopsis:       Bugsnag error reporter for Haskell description:    Please see README.md category:       Web@@ -27,7 +27,7 @@       src   ghc-options: -Wall   build-depends:-      aeson+      aeson >=1.3.0.0     , base >=4.8.0 && <5     , bytestring     , case-insensitive@@ -123,6 +123,7 @@       base >=4.8.0 && <5     , bugsnag-haskell     , exceptions+    , unliftio     , wai     , warp     , yesod-core
examples/yesod/Main.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}@@ -9,9 +10,14 @@  import Control.Exception (SomeException, fromException, toException) import Control.Monad (unless)-import Control.Monad.Catch (catch, throwM) import Network.Bugsnag import Network.Wai.Handler.Warp (run)+#if MIN_VERSION_yesod_core(1,6,0)+import UnliftIO.Exception (catch, throwIO)+#else+import Control.Monad.Catch (catch)+import UnliftIO.Exception (throwIO)+#endif import Yesod.Core import Yesod.Core.Types (HandlerContents) @@ -43,7 +49,7 @@         unless (isHandlerContents ex)             $ forkHandler (const $ pure ()) $ liftIO             $ notifyBugsnagWith (updateEventFromWaiRequest request) settings ex-        throwM $ toException ex+        throwIO $ toException ex   where     isHandlerContents :: SomeException -> Bool     isHandlerContents ex =
src/Network/Bugsnag/BeforeNotify.hs view
@@ -1,13 +1,23 @@+{-# LANGUAGE OverloadedStrings #-}+ module Network.Bugsnag.BeforeNotify     ( BeforeNotify+    , defaultBeforeNotify      -- * Modifying the Exception     , updateException+    , filterStackFrames+    , setStackFramesInProject+    , setGroupingHash+    , setGroupingHashBy      -- * Modifying the Event     , updateEventFromSession     , updateEventFromWaiRequest +    -- * Modifying the Request+    , redactRequestHeaders+     -- * Simple setters     , setDevice     , setRequest@@ -19,6 +29,7 @@     , setInfoSeverity     ) where +import Data.Text (Text) import Network.Bugsnag.Device import Network.Bugsnag.Event import Network.Bugsnag.Exception@@ -26,10 +37,30 @@ import Network.Bugsnag.Session import Network.Bugsnag.Severity import Network.Bugsnag.StackFrame+import Network.HTTP.Types.Header (Header, HeaderName) import Network.Wai (Request)  type BeforeNotify = BugsnagEvent -> BugsnagEvent +-- | Used as @'bsBeforeNotify'@ the default Settings value+--+-- Redacts the following Request headers:+--+-- - Authorization+-- - Cookie+-- - X-XSRF-TOKEN (CSRF token header used by Yesod)+--+-- N.B. If you override the value on @'BugsnagSettings'@, you probably want to+-- maintain this as well:+--+-- @+-- settings { 'bsBeforeNotify' = 'defaultBeforeNotify' . myBeforeNotify }+-- @+--+defaultBeforeNotify :: BeforeNotify+defaultBeforeNotify =+    redactRequestHeaders ["Authorization", "Cookie", "X-XSRF-TOKEN"]+ -- | Modify just the Exception part of an Event -- -- Technically this will modify all exceptions in the Event, but if you're using@@ -50,50 +81,90 @@ -- >         _ -> ex -- updateException :: (BugsnagException -> BugsnagException) -> BeforeNotify-updateException f event = event-    { beExceptions = f <$> beExceptions event-    }+updateException f event = event { beExceptions = f <$> beExceptions event } +-- | Filter out StackFrames matching a predicate+filterStackFrames :: (BugsnagStackFrame -> Bool) -> BeforeNotify+filterStackFrames p =+    updateException $ \ex -> ex { beStacktrace = filter p $ beStacktrace ex }++-- | Set @'bsIsInProject'@ using the given predicate, applied to the Filename+setStackFramesInProject :: (FilePath -> Bool) -> BeforeNotify+setStackFramesInProject p = updateException+    $ \ex -> ex { beStacktrace = map updateStackFrames $ beStacktrace ex }+  where+    updateStackFrames :: BugsnagStackFrame -> BugsnagStackFrame+    updateStackFrames sf = sf { bsfInProject = Just $ p $ bsfFile sf }++-- | Set @'beGroupingHash'@+setGroupingHash :: Text -> BeforeNotify+setGroupingHash hash = setGroupingHashBy $ const $ Just hash++-- | Set @'beGroupingHash'@ based on the Event+setGroupingHashBy :: (BugsnagEvent -> Maybe Text) -> BeforeNotify+setGroupingHashBy f event = event { beGroupingHash = f event }+ -- | Set the events @'BugsnagEvent'@ and @'BugsnagDevice'@ updateEventFromWaiRequest :: Request -> BeforeNotify updateEventFromWaiRequest wrequest =-    let mdevice = bugsnagDeviceFromWaiRequest wrequest+    let+        mdevice = bugsnagDeviceFromWaiRequest wrequest         request = bugsnagRequestFromWaiRequest wrequest     in maybe id setDevice mdevice . setRequest request  -- | Update the Event's Context and User from the Session updateEventFromSession :: BugsnagSession -> BeforeNotify-updateEventFromSession session event = event-    { beContext = bsContext session-    , beUser = bsUser session+updateEventFromSession session event =+    event { beContext = bsContext session, beUser = bsUser session }++-- | Redact the given request headers+--+-- Headers like @Authorization@ may contain information you don't want to report+-- to Bugsnag.+--+-- > redactRequestHeaders ["Authorization", "Cookie"]+--+redactRequestHeaders :: [HeaderName] -> BeforeNotify+redactRequestHeaders headers event =+    event { beRequest = redactHeaders headers <$> beRequest event }++-- |+--+-- >>> let headers = [("Authorization", "secret"), ("X-Foo", "Bar")]+-- >>> let req = bugsnagRequest { brHeaders = Just headers }+-- >>> brHeaders $ redactHeaders ["Authorization"] req+-- Just [("Authorization","<redacted>"),("X-Foo","Bar")]+--+redactHeaders :: [HeaderName] -> BugsnagRequest -> BugsnagRequest+redactHeaders headers request = request+    { brHeaders = map redactHeader <$> brHeaders request     }+  where+    redactHeader :: Header -> Header+    redactHeader (k, _) | k `elem` headers = (k, "<redacted>")+    redactHeader h = h  -- | Set the Event's Request -- -- See @'bugsnagRequestFromWaiRequest'@ -- setRequest :: BugsnagRequest -> BeforeNotify-setRequest request event = event-    { beRequest = Just request-    }+setRequest request event = event { beRequest = Just request }  -- | Set the Event's Device -- -- See @'bugsnagDeviceFromWaiRequest'@ -- setDevice :: BugsnagDevice -> BeforeNotify-setDevice device event = event-    { beDevice = Just device-    }+setDevice device event = event { beDevice = Just device }  -- | Set the stacktrace on the reported exception -- -- > notifyBugsnagWith (setStacktrace [$(currentStackFrame) "myFunc"]) ... -- setStacktrace :: [BugsnagStackFrame] -> BeforeNotify-setStacktrace stacktrace = updateException $ \ex -> ex-    { beStacktrace = stacktrace-    }+setStacktrace stacktrace =+    updateException $ \ex -> ex { beStacktrace = stacktrace }  -- | Set to @'ErrorSeverity'@ setErrorSeverity :: BeforeNotify
src/Network/Bugsnag/Notify.hs view
@@ -1,3 +1,5 @@+{-# OPTIONS_GHC -fno-warn-deprecations #-}+ module Network.Bugsnag.Notify     ( notifyBugsnag     , notifyBugsnagWith@@ -12,7 +14,6 @@ import Network.Bugsnag.Report import Network.Bugsnag.Reporter import Network.Bugsnag.Settings-import Network.Bugsnag.StackFrame  -- | Notify Bugsnag of a single exception notifyBugsnag :: BugsnagSettings -> SomeException -> IO ()@@ -26,47 +27,26 @@ -- notifyBugsnagWith :: BeforeNotify -> BugsnagSettings -> SomeException -> IO () notifyBugsnagWith f settings ex = do-    let exception = bugsnagExceptionFromSomeException ex--    -- N.B. all notify functions should go through here. We need to maintain-    -- this as the single point where (e.g.) should-notify is checked,-    -- before-notify is applied, stack-frame filtering, etc.-    when (bugsnagShouldNotify settings exception) $ do-        let event-                = f+    let event =+            f                 . bsBeforeNotify settings-                . updateGroupingHash settings-                . updateStackFramesInProject settings-                . filterStackFrames settings+                . setGroupingHashBy (bsGroupingHash settings)+                . setStackFramesInProject (bsIsInProject settings)+                . filterStackFrames (bsFilterStackFrames settings)                 . createApp settings                 . bugsnagEvent-                $ pure exception--            manager = bsHttpManager settings-            apiKey = bsApiKey settings-            report = bugsnagReport [event]--        reportError manager apiKey report--updateGroupingHash :: BugsnagSettings -> BeforeNotify-updateGroupingHash settings event = event-    { beGroupingHash = bsGroupingHash settings event-    }+                . pure+                $ bugsnagExceptionFromSomeException ex -updateStackFramesInProject :: BugsnagSettings -> BeforeNotify-updateStackFramesInProject settings = updateException $ \ex -> ex-    { beStacktrace = map updateStackFrames $ beStacktrace ex-    }-  where-    updateStackFrames :: BugsnagStackFrame -> BugsnagStackFrame-    updateStackFrames sf = sf-        { bsfInProject = Just $ bsIsInProject settings $ bsfFile sf-        }+        manager = bsHttpManager settings+        apiKey = bsApiKey settings+        report = bugsnagReport [event] -filterStackFrames :: BugsnagSettings -> BeforeNotify-filterStackFrames settings = updateException $ \ex -> ex-    { beStacktrace = filter (bsFilterStackFrames settings) $ beStacktrace ex-    }+    -- N.B. all notify functions should go through here. We need to maintain+    -- this as the single point where (e.g.) should-notify is checked,+    -- before-notify is applied, stack-frame filtering, etc.+    when (bugsnagShouldNotify settings event)+        $ reportError manager apiKey report  -- | --
src/Network/Bugsnag/Settings.hs view
@@ -12,6 +12,7 @@     ) where  import Data.Aeson (FromJSON)+import qualified Data.List.NonEmpty as NE import Data.String import Data.Text (Text) import qualified Data.Text as T@@ -51,7 +52,9 @@     , bsBeforeNotify :: BeforeNotify     -- ^ Modify any events before they are sent     ---    -- For example to attach a user, or set the context.+    -- For example to attach a user, or set the context. By default, we use+    -- @'redactRequestHeaders'@ to strip some sensitive Headers from the+    -- Request.     --     , bsIgnoreException :: BugsnagException -> Bool     -- ^ Exception filtering@@ -86,6 +89,10 @@     --     } +{-# DEPRECATED bsGroupingHash "use setGroupingHashBy with bsBeforeNotify" #-}+{-# DEPRECATED bsIsInProject "use setStackFramesInProject with bsBeforeNotify" #-}+{-# DEPRECATED bsFilterStackFrames "use filterStackFrames with bsBeforeNotify" #-}+ -- | Construct settings purely, given an existing @'Manager'@ bugsnagSettings :: BugsnagApiKey -> Manager -> BugsnagSettings bugsnagSettings apiKey manager = BugsnagSettings@@ -93,7 +100,7 @@     , bsAppVersion = Nothing     , bsReleaseStage = ProductionReleaseStage     , bsNotifyReleaseStages = [ProductionReleaseStage]-    , bsBeforeNotify = id+    , bsBeforeNotify = defaultBeforeNotify     , bsIgnoreException = const False     , bsGroupingHash = const Nothing     , bsIsInProject = const True@@ -101,37 +108,48 @@     , bsHttpManager = manager     } --- | Should this @'BugsnagException'@ trigger notification?+-- | Should this @'BugsnagEvent'@ trigger notification? -- -- >>> :set -XOverloadedStrings -- >>> settings <- newBugsnagSettings ""--- >>> let exception = bugsnagException "" "" []--- >>> bugsnagShouldNotify settings exception+-- >>> let event = bugsnagEvent $ pure $ bugsnagException "" "" []+-- >>> bugsnagShouldNotify settings event -- True -- -- >>> let devSettings = settings { bsReleaseStage = DevelopmentReleaseStage }--- >>> bugsnagShouldNotify devSettings exception+-- >>> bugsnagShouldNotify devSettings event -- False ----- >>> bugsnagShouldNotify devSettings { bsNotifyReleaseStages = [DevelopmentReleaseStage] } exception+-- >>> bugsnagShouldNotify devSettings { bsNotifyReleaseStages = [DevelopmentReleaseStage] } event -- True -- -- >>> let ignore = (== "IgnoreMe") . beErrorClass -- >>> let ignoreSettings = settings { bsIgnoreException = ignore }--- >>> bugsnagShouldNotify ignoreSettings exception+-- >>> let ignoreEvent = bugsnagEvent $ pure $ bugsnagException "IgnoreMe" "" []+-- >>> bugsnagShouldNotify ignoreSettings event -- True ----- >>> bugsnagShouldNotify ignoreSettings exception { beErrorClass = "IgnoreMe" }+-- >>> bugsnagShouldNotify ignoreSettings ignoreEvent -- False ---bugsnagShouldNotify :: BugsnagSettings -> BugsnagException -> Bool-bugsnagShouldNotify settings exception+bugsnagShouldNotify :: BugsnagSettings -> BugsnagEvent -> Bool+bugsnagShouldNotify settings event     | bsReleaseStage settings `notElem` bsNotifyReleaseStages settings = False     | bsIgnoreException settings exception = False     | otherwise = True+  where+    exception+        | NE.length (beExceptions event) == 1 = NE.head $ beExceptions event+        | otherwise = error $ unlines+            [ "Library misused. Event must have exactly one Exception when"+            , " going through our notifyBugsnag functions. To send more than"+            , " one Exception, drop down to reportError."+            ]  -- | Construct settings with a new, TLS-enabled @'Manager'@ --+-- Uses @'getGlobalManager'@.+-- -- >>> :set -XOverloadedStrings -- >>> settings <- newBugsnagSettings "API_KEY" -- >>> bsApiKey settings@@ -144,4 +162,4 @@ -- [ProductionReleaseStage] -- newBugsnagSettings :: BugsnagApiKey -> IO BugsnagSettings-newBugsnagSettings apiKey = bugsnagSettings apiKey <$> newTlsManager+newBugsnagSettings apiKey = bugsnagSettings apiKey <$> getGlobalManager