{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RecordWildCards #-}
module Logging.Handler.RotatingFileHandler
( RotatingFileHandler(..)
) where
import Control.Concurrent.MVar
import Control.Monad
import GHC.Generics
import System.IO
import Text.Format
import Logging.Class
import Logging.Filter
import Logging.Level
import Logging.Prelude
data RotatingFileHandler = RotatingFileHandler { level :: Level
, filterer :: Filterer
, formatter :: Format1
, file :: FilePath
, encoding :: TextEncoding
, maxBytes :: Int
, backupCount :: Int
, stream :: MVar Handle
} deriving (Generic, Eq)
instance Handler RotatingFileHandler where
open RotatingFileHandler{..} = isEmptyMVar stream >>= safeOpen
where
safeOpen True = void $ tryPutMVar stream =<< openLogFile file encoding
safeOpen False = modifyMVar_ stream $ \h -> do
hClose h
openLogFile file encoding
emit handler@RotatingFileHandler{..} rcd = do
let msg = format1 formatter rcd
modifyMVar_ stream $ \stream' -> do
hPutStrLn stream' msg
hFlush stream'
pos <- hTell stream'
rollover (backupCount > 0) (fromEnum pos >= maxBytes) stream'
where
rollover :: Bool -> Bool -> Handle -> IO Handle
rollover True True h = do
hClose h
rotate $ backupCount - 1
tryRenameFile file $ appendBaseName file ".1"
openLogFile file encoding
rollover _ _ h = return h
rotate :: Int -> IO ()
rotate n = when (n > 0) $ do
let src = appendBaseName file $ '.' : (show n)
dest = appendBaseName file $ '.' : (show (n + 1))
tryRenameFile src dest
rotate (n - 1)
close RotatingFileHandler{..} = withMVar stream hClose