-- | A logging effect. -- -- There is just one log message type: 'LogMessage' and it is written using 'logMsg' and -- the functions built on top of it. -- -- The 'Logs' effect is tightly coupled with the 'LogWriterReader' effect. -- When using the 'Control.Monad.Trans.ControlMonadBaseControl' instance, the underlying monad of the 'LogWriter', -- that is expected to be present through the respective 'LogWriterReader', is -- constrained to be the base monad itself, e.g. 'IO'. -- -- The log message type is fixed to 'LogMessage', and there is a type class for -- converting to that, call 'ToLogMessage'. -- -- There is a single global 'LogPredicate' that can be used to suppress logs directly -- at the point where they are sent, in the 'logMsg' function. -- -- Note that all logging is eventually done via 'logMsg'; 'logMsg' is the __only__ place where -- log filtering should happen. -- -- Also, 'LogMessage's are evaluated using 'Control.DeepSeq.deepseq', __after__ they pass the 'LogPredicate', also inside 'logMsg'. -- -- Example: -- -- > exampleLogging :: IO () -- > exampleLogging = -- > runLift -- > $ withLogging consoleLogWriter -- > $ do -- > logDebug "test 1.1" -- > logError "test 1.2" -- > censorLogs (prefixLogMessagesWith "NESTED: ") -- > $ do -- > addLogWriter debugTraceLogWriter -- > $ setLogPredicate (\m -> (view lmMessage m) /= "not logged") -- > $ do -- > logInfo "not logged" -- > logMsg "test 2.1" -- > logWarning "test 2.2" -- > logCritical "test 1.3" -- -- == Asynchronous Logging -- -- Logging in a 'Control.Concurrent.Async.withAsync' spawned thread is done using 'withAsyncLogging'. -- -- == 'LogPredicate's -- -- See "Control.Eff.Log.Handler#LogPredicate" module Control.Eff.Log ( -- * Module Re-Exports -- | This module contains the API for __sending__ log messages and for -- handling the messages in the frame work of extensible effects. -- -- It also defines the reader effect to access 'LogWriter's module Control.Eff.Log.Handler -- | The module that contains the 'LogMessage' and 'LogPredicate' definitions. -- -- The log message type corresponds to RFC-5424, including structured data. , module Control.Eff.Log.Message -- | This module only exposes a 'LogWriter' for asynchronous logging; , module Control.Eff.Log.Channel -- | This module defines the 'LogWriter' type, which is used to give -- callback functions for log messages an explicit type. , module Control.Eff.Log.Writer -- * Example Code for Logging , exampleLogging , exampleWithLogging , exampleWithSomeLogging , exampleLogPredicate , exampleLogCapture , exampleAsyncLogging ) where import Control.Eff.Log.Channel import Control.Eff.Log.Handler import Control.Eff.Log.Message import Control.Eff.Log.Writer import Control.Eff import Control.Lens (view, (%~), to) -- * Logging examples -- | Example code for: -- -- * 'withConsoleLogging' -- * 'ioLogWriter' -- * 'printLogMessage' -- * 'logDebug' -- * 'logError' -- * 'prefixLogMessagesWith' -- * 'addLogWriter' -- * 'debugTraceLogWriter' -- * 'setLogPredicate' -- * 'logInfo' -- * 'logMsg' -- * 'logWarning' -- * 'logCritical' -- * 'lmMessage' exampleLogging :: IO () exampleLogging = runLift $ withConsoleLogging "my-app" local7 allLogMessages $ do logDebug "test 1.1" logError "test 1.2" censorLogs (prefixLogMessagesWith "NESTED: ") $ do addLogWriter debugTraceLogWriter $ setLogPredicate (\m -> (view lmMessage m) /= "not logged") $ do logInfo "not logged" logMsg "test 2.1" logWarning "test 2.2" logCritical "test 1.3" -- | Example code for: -- -- * 'withLogging' -- * 'consoleLogWriter' exampleWithLogging :: IO () exampleWithLogging = runLift $ withLogging consoleLogWriter $ logDebug "Oh, hi there" -- | Example code for: -- -- * 'withSomeLogging' -- * 'PureLogWriter' -- * 'logDebug' exampleWithSomeLogging :: () exampleWithSomeLogging = run $ withSomeLogging @PureLogWriter $ logDebug "Oh, hi there" -- | Example code for: -- -- * 'setLogPredicate' -- * 'modifyLogPredicate' -- * 'lmMessageStartsWith' -- * 'lmSeverityIs' -- * 'lmSeverityIsAtLeast' -- * 'includeLogMessages' -- * 'excludeLogMessages' exampleLogPredicate :: IO Int exampleLogPredicate = runLift $ withSomeLogging @IO $ setLogWriter consoleLogWriter $ do logMsg "test" setLogPredicate (lmMessageStartsWith "OMG") (do logMsg "this message will not be logged" logMsg "OMG logged" modifyLogPredicate (\p lm -> p lm || lmSeverityIs errorSeverity lm) $ do logDebug "OMG logged" logInfo "Not logged" logError "Logged" logEmergency "Not Logged" includeLogMessages (lmSeverityIsAtLeast warningSeverity) $ do logInfo "Not logged" logError "Logged" logEmergency "Logged" logWarning "Logged" logDebug "OMG still Logged" excludeLogMessages (lmMessageStartsWith "OMG") $ do logDebug "OMG NOT Logged" logError "OMG ALSO NOT Logged" logEmergency "Still Logged" logWarning "Still Logged" logWarning "Logged" logDebug "OMG still Logged" return 42) -- | Example code for: -- -- * 'runCapturedLogsWriter' -- * 'listLogWriter' -- * 'mappingLogWriter' -- * 'filteringLogWriter' exampleLogCapture :: IO () exampleLogCapture = go >>= putStrLn where go = fmap (unlines . map renderLogMessage . snd) $ runLift $ runCapturedLogsWriter $ withLogging listLogWriter $ addLogWriter (mappingLogWriter (lmMessage %~ ("CAPTURED "++)) listLogWriter) $ addLogWriter (filteringLogWriter testPred (mappingLogWriter (lmMessage %~ ("TRACED "++)) debugTraceLogWriter)) $ do logEmergency "test emergencySeverity 1" logCritical "test criticalSeverity 2" logAlert "test alertSeverity 3" logError "test errorSeverity 4" logWarning "test warningSeverity 5" logInfo "test informationalSeverity 6" logDebug "test debugSeverity 7" testPred = view (lmSeverity . to (<= errorSeverity)) -- | Example code for: -- -- * 'withAsyncLogging' exampleAsyncLogging :: IO () exampleAsyncLogging = runLift $ withSomeLogging @IO $ withAsyncLogging (1000::Int) consoleLogWriter $ do logMsg "test 1" logMsg "test 2" logMsg "test 3"