{-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : System.Log.Logger.Handler -- Copyright : (C) 2015 Flowbox -- License : Apache-2.0 -- Maintainer : Wojciech DaniƂo -- Stability : stable -- Portability : portable ----------------------------------------------------------------------------- module System.Log.Logger.Thread where import System.Log.Data (MonadRecord, appendRecord, Lvl(Lvl), readData, LevelData(LevelData), LookupDataSet) import System.Log.Logger.Handler (MonadLoggerHandler(addHandler)) import System.Log.Log (LogFormat, MonadLogger) import System.Log.Logger.Base (BaseLoggerT, runRawBaseLoggerT) import System.Log.Logger.Priority (MonadPriorityLogger(getPriority,setPriority)) import Control.Monad.Trans (MonadTrans, lift) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Applicative import qualified Control.Monad.Reader as Reader import Control.Monad.Reader (ReaderT, runReaderT) import Control.Concurrent.Chan.Unagi (readChan, writeChan, newChan, InChan, OutChan) import Control.Exception (throwIO, catch, SomeException) import Control.Concurrent (forkIO) import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar) ---------------------------------------------------------------------- -- ThreadedLogger ---------------------------------------------------------------------- newtype ThreadedLogger' d r m a = ThreadedLogger' { fromThreadedLogger :: ReaderT (InChan (ChMsg d r)) m a } deriving (Monad, MonadIO, Applicative, Functor, MonadTrans) type ThreadedLogger d m a = ThreadedLogger' d a m a type instance LogFormat (ThreadedLogger' d r m) = LogFormat m data ChMsg m a = ChMsg (m ()) | End a | Exc SomeException class MonadThreadLogger m n a | m-> n a where getLogChan :: m (InChan (ChMsg n a)) -- === Utils === runRawThreadedLogger :: InChan (ChMsg d r) -> ThreadedLogger' d r m a -> m a runRawThreadedLogger ch = flip runReaderT ch . fromThreadedLogger -- cutting out all the logs and sending them over channel, computing result runRawBaseThreadedLogger :: InChan (ChMsg d r) -> ThreadedLogger' d r (BaseLoggerT l m) a -> m a runRawBaseThreadedLogger ch = runRawBaseLoggerT . runRawThreadedLogger ch runThreadedLogger :: (MonadIO m, Applicative m) => ThreadedLogger m (BaseLoggerT l IO) a -> m a runThreadedLogger m = do (inChan, outChan) <- liftIO newChan liftIO $ forkIO $ do out <- (End <$> runRawBaseThreadedLogger inChan m) `catch` (\e -> return (Exc e)) writeChan inChan out loop outChan where loop :: (MonadIO m, Applicative m) => OutChan (ChMsg m a) -> m a loop ch = do l <- liftIO $ readChan ch case l of End a -> return a ChMsg d -> d *> loop ch Exc e -> liftIO $ throwIO e liftIOThread :: (MonadIO m, MonadThreadLogger m n a) => (IO () -> IO fa) -> ThreadedLogger' n a (BaseLoggerT l IO) b -> m b liftIOThread f m = do inChan <- getLogChan ret <- liftIO $ newEmptyMVar liftIO . f $ do out <- (End <$> runRawBaseThreadedLogger inChan m) `catch` (\e -> return (Exc e)) case out of End v -> putMVar ret v Exc e -> putMVar ret undefined *> writeChan inChan (Exc e) liftIO $ takeMVar ret fork :: (MonadIO m, MonadThreadLogger m n a) => ThreadedLogger' n a (BaseLoggerT l IO) b -> m b fork = liftIOThread forkIO withTarget :: (MonadThreadLogger m n a, MonadIO m) => n () -> m () withTarget f = do ch <- getLogChan liftIO $ writeChan ch (ChMsg f) -- === Instances === instance Monad m => MonadThreadLogger (ThreadedLogger' d r m) d r where getLogChan = ThreadedLogger' Reader.ask --- instance (MonadIO m, MonadRecord d n) => MonadRecord d (ThreadedLogger' n a m) where appendRecord = withTarget . appendRecord instance (MonadIO m, MonadLoggerHandler h d, LogFormat m ~ LogFormat d) => MonadLoggerHandler h (ThreadedLogger' d a m) where addHandler = withTarget . addHandler instance (MonadIO m, MonadPriorityLogger d) => MonadPriorityLogger (ThreadedLogger' d a m) where setPriority = withTarget . setPriority getPriority = error "Cannot get priority from within ThreadLogger!"