{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Text.LoggerThread ( withLoggerThread , Logger ) where import Control.Concurrent.STM.TQueue import Control.Exception.Safe import Control.Monad.STM (atomically) import Data.Text import Protolude hiding (bracket) import System.Log.FastLogger type Logger = Text -> IO () toLog :: TQueue Text -> Text -> IO () toLog chan s = atomically (writeTQueue chan (s <> singleton '\n')) withLoggerThread :: (Logger -> IO ()) -> IO () withLoggerThread mainThread = do logChannel <- atomically newTQueue bracket (async (loggerThread logChannel)) (\loggerThreadAsync -> do putText "after the main thread, stop the loggerThread" -- dummy text to unblock the logger channel threadDelay (1000 * 1000) cancel loggerThreadAsync putText "cancelled the loggerThread" wait loggerThreadAsync) (\loggerThreadAsync -> do link loggerThreadAsync withException (mainThread (toLog logChannel)) (\e -> do putText "exception in main thread" (print :: SomeException -> IO ()) e putText "main thread exiting")) -- 1 MiB = 1 mebibyte = 1,0242 bytes = 1,048,576 bytes -- 100 MiB = 1,048,576 * 100 bytes loggerThread :: TQueue Text -> IO () loggerThread chan = withFastLogger (LogStderr 1048576) (\f -> withException (readAndLog chan f) (\e -> do putText "exception in logger thread" (print :: SomeException -> IO ()) e shutdownLogging chan f)) shutdownLogging :: TQueue Text -> (LogStr -> IO a) -> IO () shutdownLogging chan f = do putText "in shutdownLogging" maybeValue <- atomically (tryReadTQueue chan) case maybeValue of Just t -> (f . (toLogStr :: Text -> LogStr)) t >> shutdownLogging chan f Nothing -> putText "LoggerThread stopped reading, exiting" >> return () readAndLog :: TQueue Text -> (LogStr -> IO a) -> IO () readAndLog chan f = atomically (readTQueue chan) >>= f . (toLogStr :: Text -> LogStr) >> readAndLog chan f -- log rotation is a system function, not an application function -- should not be doing this here -- not sure how to use this. If I use it readAndLog, it gives this -- message -- GetMarketsServer: /home/j/var/betfair//log/aping-20160820-betfair.log: openFile: resource busy (file is locked) -- checkAndRotate :: FileLogSpec -> IO () -- checkAndRotate spec = -- do size <- getFileSize (log_file spec) -- when (size > log_file_size spec) -- (rotate spec) -- getFileSize -- :: BasicPrelude.FilePath -> IO Integer -- getFileSize path = withFile path ReadMode hFileSize