capataz-0.2.1.0: src/Control/Concurrent/Capataz/Util.hs
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Control.Concurrent.Capataz.Util (
buildLogWorkerSpec1
, buildLogWorkerSpec
, buildLogWorkerOptions1
, buildLogWorkerOptions
) where
import Control.Concurrent.Capataz
( ProcessSpec
, WorkerName
, WorkerOptions
, WorkerRestartStrategy (Permanent)
, buildWorkerOptions
, workerRestartStrategyL
, workerSpec
)
import RIO
data LogMsg
= LogMsg
{
lmCallStack :: !CallStack
, lmLogSource :: !LogSource
, lmLogLevel :: !LogLevel
, lmPayload :: !Utf8Builder
}
runLoggerThread :: MonadUnliftIO m => LogOptions -> TBQueue LogMsg -> m a
runLoggerThread logOptions inputQueue = withLogFunc logOptions $ \logFunc ->
runRIO logFunc $ forever $ do
logMsg <- atomically $ readTBQueue inputQueue
let LogMsg { lmLogSource, lmLogLevel, lmPayload } = logMsg
-- TODO: create a ticket for a logGenericWithStack function in rio
logGeneric lmLogSource lmLogLevel lmPayload
-- | Builds a 'ProcessSpec' that spawns a thread that logs messages written with
-- the returned 'LogFunc'. Use this function when your want your logger to be
-- part of a static supervision tree.
--
--
-- __IMPORTANT__ If you use the returned 'LogFunc' to log functions and the
-- 'ProcessSpec' is not used in a supervision tree, your logging won't work and
-- your application will eventually block the current thread when logging.
--
-- A minimal example:
--
-- @
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE NoImplicitPrelude #-}
-- import RIO
-- import Capataz
--
-- main :: IO ()
-- main = do
-- logOptions <- logOptionsHandle stdout True
-- (loggerSpec, logFunc) <- buildLogWorkerSpec logOptions "app-logger" 100 id
-- runRIO logFunc $ do
-- bracket (forkCapataz "application" (set supervisorProcessSpecListL [loggerSpec]))
-- terminateCapataz_ $ \_capataz -> do
-- logInfo "this log message is written by a dedicated supervised thread"
-- threadDelay 1000100
-- @
--
-- @since 0.2.1.0
buildLogWorkerSpec1
:: (MonadUnliftIO m, MonadIO m0)
=> LogOptions -- ^ options for the 'LogFunc' instance
-> WorkerName -- ^ name of the logger worker process
-> Natural -- ^ how many log messages can be in-flight when writer is slow?
-> (WorkerOptions m -> WorkerOptions m) -- ^ worker process modifier
-> m0 (ProcessSpec m, LogFunc)
buildLogWorkerSpec1 logOptions procName bufferSize modOptions = do
inputQueue <- newTBQueueIO bufferSize
let myLogFunc =
mkLogFunc $ \lmCallStack lmLogSource lmLogLevel lmPayload -> atomically
(writeTBQueue
inputQueue
LogMsg {lmCallStack , lmLogSource , lmLogLevel , lmPayload }
)
loggerSpec = workerSpec
procName
(runLoggerThread logOptions inputQueue)
(modOptions . set workerRestartStrategyL Permanent)
return (loggerSpec, myLogFunc)
-- | Deprecated in favour of 'buildLogWorkerSpec1'.
--
-- __IMPORTANT__ Since 0.2.1.0 this function throws a runtime error if
-- the argumet of the type 'Int' is negative.
--
-- @since 0.2.0.0
buildLogWorkerSpec
:: (MonadUnliftIO m, MonadIO m0)
=> LogOptions -- ^ options for the 'LogFunc' instance
-> WorkerName -- ^ name of the logger worker process
-> Int -- ^ how many log messages can be in-flight when writer is slow?
-> (WorkerOptions m -> WorkerOptions m) -- ^ worker process modifier
-> m0 (ProcessSpec m, LogFunc)
buildLogWorkerSpec logOptions procName bufferSize =
buildLogWorkerSpec1 logOptions procName (fromIntegral bufferSize)
{-# DEPRECATED buildLogWorkerSpec "Use buildLogWorkerSpec1 instead" #-}
-- | Builds a 'WorkerOptions' record that spawns a thread that logs messages
-- written with the returned 'LogFunc'. Use this function if you want to build a
-- logger thread dynamically via 'forkWorker'.
--
--
-- __IMPORTANT__ If you use the returned 'LogFunc' to log functions and the
-- 'WorkerOptions' is not used in a 'forkWorker' call, your logging won't work
-- and your application will eventually block the current thread when logging.
--
-- A minimal example:
--
-- @
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE NoImplicitPrelude #-}
-- import RIO
-- import Capataz
--
-- main :: IO ()
-- main = do
-- logOptions <- logOptionsHandle stdout True
-- (loggerOptions, logFunc) <- buildLogWorkerOptions logOptions "app-logger" 100 id
-- runRIO logFunc $ do
-- bracket (forkCapataz "application" id)
-- terminateCapataz_ $ \capataz -> do
-- _workerId <- forkWorker loggerOptions capataz
-- logInfo "this log message is written by a dedicated supervised thread"
-- threadDelay 1000100
-- @
--
-- @since 0.2.1.0
buildLogWorkerOptions1
:: (MonadUnliftIO m, MonadIO m0)
=> LogOptions
-> WorkerName
-> Natural
-> (WorkerOptions m -> WorkerOptions m)
-> m0 (WorkerOptions m, LogFunc)
buildLogWorkerOptions1 logOptions procName bufferSize modOptions = do
inputQueue <- newTBQueueIO bufferSize
let myLogFunc =
mkLogFunc $ \lmCallStack lmLogSource lmLogLevel lmPayload -> atomically
(writeTBQueue
inputQueue
LogMsg {lmCallStack , lmLogSource , lmLogLevel , lmPayload }
)
loggerSpec = buildWorkerOptions
procName
(runLoggerThread logOptions inputQueue)
(modOptions . set workerRestartStrategyL Permanent)
return (loggerSpec, myLogFunc)
-- | Deprecated in favour of 'buildLogWorkerOptions1'.
--
-- __IMPORTANT__ Since 0.2.1.0 this function throws a runtime error if
-- the argumet of the type 'Int' is negative.
--
-- @since 0.2.0.0
buildLogWorkerOptions
:: (MonadUnliftIO m, MonadIO m0)
=> LogOptions
-> WorkerName
-> Int
-> (WorkerOptions m -> WorkerOptions m)
-> m0 (WorkerOptions m, LogFunc)
buildLogWorkerOptions logOptions procName bufferSize =
buildLogWorkerOptions1 logOptions procName (fromIntegral bufferSize)
{-# DEPRECATED buildLogWorkerOptions "Use buildLogWorkerOptions1 instead" #-}