@@ -28,28 +28,36 @@ module Katip.Monadic
2828 , AnyLogContext
2929 , LogContexts
3030 , liftPayload
31+
32+ -- * LogT - Utility transformer that provides Katip and KatipContext instances
33+ , LogT(..)
34+ , runLogT
35+ , LogTState(..)
3136 ) where
3237
3338
3439-------------------------------------------------------------------------------
40+import Control.Applicative
41+import Control.Monad.Base
3542import Control.Monad.Catch
36−import Control.Monad.Trans.Class
43+import Control.Monad.Error.Class
44+import Control.Monad.IO.Class
45+import Control.Monad.Reader
46+import Control.Monad.State
3747import Control.Monad.Trans.Either (EitherT)
3848import Control.Monad.Trans.Except (ExceptT)
3949import Control.Monad.Trans.Identity (IdentityT)
4050import Control.Monad.Trans.List (ListT)
4151import Control.Monad.Trans.Maybe (MaybeT)
42−import Control.Monad.Trans.Reader (ReaderT)
4352import Control.Monad.Trans.Resource (ResourceT)
4453import Control.Monad.Trans.RWS (RWST)
4554import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST)
46−import Control.Monad.Trans.State (StateT)
4755import qualified Control.Monad.Trans.State.Strict as Strict (StateT)
48−import Control.Monad.Trans.Writer (WriterT)
4956import qualified Control.Monad.Trans.Writer.Strict as Strict (WriterT)
57+import Control.Monad.Writer
5058import Data.Aeson
5159import qualified Data.HashMap.Strict as HM
52−import Data.Monoid
60+import Data.Monoid as M
5361import Data.Text (Text)
5462import Language.Haskell.TH
5563-------------------------------------------------------------------------------
@@ -179,4 +187,69 @@ logExceptionM
179187logExceptionM action sev = action `catchAll` \e -> f e >> throwM e
180188 where
181189 f e = logFM sev (msg e)
182− msg e = ls ("An exception has occured: " :: Text) <> showLS e
190+ msg e = ls ("An exception has occured: " :: Text) M.<> showLS e
191+
192+
193+-------------------------------------------------------------------------------
194+-- | Provides a simple transformer that defines a 'KatipContext'
195+-- instance for a fixed namespace and context. You will typically only
196+-- use this if you are forced to run in IO but still want to have your
197+-- log context. This is the slightly more powerful version of KatipT
198+-- in that it provides KatipContext instead of just Katip. For instance:
199+--
200+-- @
201+-- threadWithLogging = do
202+-- le <- getLogEnv
203+-- ctx <- getKatipContext
204+-- ns <- getKatipNamespace
205+-- forkIO $ runLogT le ctx ns $ do
206+-- $(logTM) InfoS "Look, I can log in IO and retain context!"
207+-- doOtherStuff
208+-- @
209+newtype LogT m a = LogT {
210+ unLogT :: ReaderT LogTState m a
211+ } deriving ( Functor
212+ , Applicative
213+ , Monad
214+ , MonadIO
215+ , MonadThrow
216+ , MonadCatch
217+ , MonadMask
218+ , MonadBase b
219+ , MonadState s
220+ , MonadWriter w
221+ , MonadError e
222+ , MonadPlus
223+ , Alternative
224+ , MonadFix
225+ , MonadTrans
226+ )
227+
228+
229+data LogTState = LogTState {
230+ ltsLogEnv :: !LogEnv
231+ , ltsContext :: !LogContexts
232+ , ltsNamespace :: !Namespace
233+ }
234+
235+-- Reader is a passthrough. We don't expose our internal reader so as not to conflict
236+instance (MonadReader r m) => MonadReader r (LogT m) where
237+ ask = lift ask
238+ local f (LogT (ReaderT m)) = LogT $ ReaderT $ \r ->
239+ local f (m r)
240+
241+
242+instance (MonadIO m) => Katip (LogT m) where
243+ getLogEnv = LogT $ ReaderT $ \lts -> return (ltsLogEnv lts)
244+
245+
246+instance (MonadIO m) => KatipContext (LogT m) where
247+ getKatipContext = LogT $ ReaderT $ \lts -> return (ltsContext lts)
248+ getKatipNamespace = LogT $ ReaderT $ \lts -> return (ltsNamespace lts)
249+
250+
251+-------------------------------------------------------------------------------
252+runLogT :: (LogItem c) => LogEnv -> c -> Namespace -> LogT m a -> m a
253+runLogT le ctx ns = flip runReaderT lts . unLogT
254+ where
255+ lts = LogTState le (liftPayload ctx) ns