{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} module Keter.LogFile ( LogFile , start , addChunk , close ) where import Keter.Prelude hiding (getCurrentTime) import qualified Data.ByteString as S import Data.Time (getCurrentTime) import qualified System.IO as SIO import qualified Filesystem as F import qualified Data.Text as T data Command = AddChunk S.ByteString | Close newtype LogFile = LogFile (Command -> KIO ()) addChunk :: LogFile -> S.ByteString -> KIO () addChunk (LogFile f) bs = f $ AddChunk bs close :: LogFile -> KIO () close (LogFile f) = f Close start :: FilePath -- ^ folder to contain logs -> KIO (Either SomeException LogFile) start dir = do res <- liftIO $ do createTree dir moveCurrent Nothing case res of Left e -> return $ Left e Right handle -> do chan <- newChan forkKIO $ loop chan handle 0 return $ Right $ LogFile $ writeChan chan where current = dir "current.log" moveCurrent mhandle = do maybe (return ()) SIO.hClose mhandle x <- isFile current when x $ do now <- getCurrentTime rename current $ dir suffix now F.openFile current F.WriteMode suffix now = fromText (T.concatMap fix $ T.takeWhile (/= '.') $ show now) <.> "log" fix ' ' = "_" fix c | '0' <= c && c <= '9' = T.singleton c fix _ = T.empty loop chan handle total = do c <- readChan chan case c of AddChunk bs -> do let total' = total + S.length bs res <- liftIO $ S.hPut handle bs >> SIO.hFlush handle either $logEx return res if total' > maxTotal then do res2 <- liftIO $ moveCurrent $ Just handle case res2 of Left e -> do $logEx e deadLoop chan Right handle' -> loop chan handle' 0 else loop chan handle total' Close -> liftIO (SIO.hClose handle) >>= either $logEx return deadLoop chan = do c <- readChan chan case c of AddChunk _ -> deadLoop chan Close -> return () maxTotal = 5 * 1024 * 1024 -- 5 MB