{-# LANGUAGE BlockArguments #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} -- | -- Description : Combine eventuo11y instrumentation with crash-only designs. -- Copyright : Copyright 2022 Shea Levy. -- License : Apache-2.0 -- Maintainer : shea@shealevy.com -- -- This module contains helpers to use eventuo11y to instrument crashes in a -- crash-only application design, where it is insufficient to simply crash in -- a top-level exception handler. For example, a "Network.Wai.Handler.Warp" -- server may want to crash in its 'Network.Wai.Handler.Warp.setOnException' -- callback, but only when the exception is due to a server-side issue and only -- after all open requests have been serviced. module Observe.Event.Crash ( withScheduleCrash, ScheduleCrash, DoCrash, hoistScheduleCrash, -- * Instrumentation Crashing (..), renderCrashing, ) where import Control.Concurrent.Async import Control.Concurrent.MVar import Control.Monad import Control.Monad.IO.Unlift import Data.Void import Observe.Event import Observe.Event.Render.JSON -- | Run an action with a 'ScheduleCrash' that can be called to crash the application. withScheduleCrash :: (MonadUnliftIO m) => EventBackend m r Crashing -> -- | Actually perform the crash. DoCrash m -> (ScheduleCrash m r -> m a) -> m a withScheduleCrash backend crash go = withRunInIO $ \runInIO -> do scheduleCrashChan <- newEmptyMVar let waitForCrash = do cause <- takeMVar scheduleCrashChan withEvent (hoistEventBackend runInIO backend) Crashing \ev -> maybe (pure ()) (addProximate ev) cause runInIO crash withAsync waitForCrash \_ -> runInIO $ go $ void . liftIO . tryPutMVar scheduleCrashChan -- | Function to schedule an application crash, perhaps caused by a referenced 'Event'. type ScheduleCrash m r = Maybe r -> m () -- | Function to actually initiate the crash. type DoCrash m = m () -- | Hoist a 'ScheduleCrash' along a given natural transformation into a new functor. hoistScheduleCrash :: -- | Natural transformation from @f@ to @g@. (forall x. f x -> g x) -> ScheduleCrash f r -> ScheduleCrash g r hoistScheduleCrash nt s = nt . s -- | Event selector for 'withScheduleCrash'. data Crashing f where Crashing :: Crashing Void -- | Render a 'Crashing' and its sub-events to JSON. renderCrashing :: RenderSelectorJSON Crashing renderCrashing Crashing = ("crashing", absurd)