{-# LANGUAGE RecordWildCards #-} module System.Log.FastLogger.SingleLogger ( SingleLogger , newSingleLogger ) where import Control.Concurrent (forkIO, newEmptyMVar, MVar, takeMVar, putMVar) import Control.Concurrent.STM import System.Log.FastLogger.FileIO import System.Log.FastLogger.IO import System.Log.FastLogger.Imports import System.Log.FastLogger.LogStr import System.Log.FastLogger.Write ---------------------------------------------------------------- -- | A non-scale but time-ordered logger. data SingleLogger = SingleLogger { slgrRef :: IORef (LogStr ,[LogStr])-- writer queue , slgrKill :: IO () , slgrWakeup :: IO () , slgrBuffer :: Buffer , slgrBufSize :: BufSize , slgrFdRef :: IORef FD } instance Loggers SingleLogger where stopLoggers = System.Log.FastLogger.SingleLogger.stopLoggers pushLog = System.Log.FastLogger.SingleLogger.pushLog flushAllLog = System.Log.FastLogger.SingleLogger.flushAllLog ---------------------------------------------------------------- writer :: BufSize -> Buffer -> IORef FD -> TVar Int -> IORef (LogStr, [LogStr]) -> MVar () -> IO () writer bufsize buf fdref tvar ref mvar = loop (0 :: Int) where loop cnt = do cnt' <- atomically $ do n <- readTVar tvar check (n /= cnt) return n msgs <- reverse <$> atomicModifyIORef' ref (\(msg,q) -> ((msg,[]),q)) cont <- go msgs if cont then loop cnt' else putMVar mvar () go [] = return True go (msg@(LogStr len _):msgs) | len < 0 = return False | len <= bufsize = writeLogStr buf fdref msg >> go msgs | otherwise = writeBigLogStr fdref msg >> go msgs ---------------------------------------------------------------- -- | Creating `SingleLogger`. newSingleLogger :: BufSize -> IORef FD -> IO SingleLogger newSingleLogger bufsize fdref = do tvar <- newTVarIO 0 ref <- newIORef (mempty,[]) mvar <- newEmptyMVar buf <- getBuffer bufsize _ <- forkIO $ writer bufsize buf fdref tvar ref mvar let wakeup = atomically $ modifyTVar' tvar (+ 1) kill = do let fin = LogStr (-1) mempty atomicModifyIORef' ref (\(old,q) -> ((mempty,fin:old:q),())) wakeup takeMVar mvar return $ SingleLogger { slgrRef = ref , slgrKill = kill , slgrWakeup = wakeup , slgrBuffer = buf , slgrBufSize = bufsize , slgrFdRef = fdref } ---------------------------------------------------------------- pushLog :: SingleLogger -> LogStr -> IO () pushLog SingleLogger{..} nlogmsg@(LogStr nlen _) | nlen > slgrBufSize = do atomicModifyIORef' slgrRef (\(old,q) -> ((mempty,nlogmsg:old:q),())) slgrWakeup | otherwise = do wake <- atomicModifyIORef' slgrRef checkBuf when wake slgrWakeup where checkBuf (ologmsg@(LogStr olen _),q) | slgrBufSize < olen + nlen = ((nlogmsg, ologmsg:q), True) | otherwise = ((ologmsg <> nlogmsg, q), False) flushAllLog :: SingleLogger -> IO () flushAllLog SingleLogger{..} = do atomicModifyIORef' slgrRef (\(old,q) -> ((mempty,old:q),())) slgrWakeup stopLoggers :: SingleLogger -> IO () stopLoggers SingleLogger{..} = do slgrKill freeBuffer slgrBuffer