{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances, ExistentialQuantification, TypeFamilies, GeneralizedNewtypeDeriving, StandaloneDeriving, MultiParamTypeClasses, UndecidableInstances, FlexibleContexts, Rank2Types, ScopedTypeVariables #-} -- | This is the main module of @heavy-logger@ package. In most cases, you need -- to import this module. You will also need other modules in specific cases. -- All modules that are required always are re-exported by this module. -- -- Example of usage is: -- -- @ -- import System.Log.Heavy -- import System.Log.Heavy.Shortcuts -- import Data.Text.Format.Heavy -- ... -- -- withLoggingT settings $ do -- liftIO $ putStr "Your name? " -- liftIO $ hFlush stdout -- name <- liftIO $ getLine -- info "name was {}" (Single name) -- liftIO $ putStrLn $ "Hello, " ++ name -- @ -- -- Please refer to @examples/@ directory for compiling examples. -- -- There are, in general, following ways to use this package: -- -- * Use @LoggingT@ monad transformer. It can be the simplest, if you already have -- monadic transformers stack of 1-2 transformers and you do not mind to add yet -- another. With @LoggingT@, you do not need to write any adapter instances, since -- @LoggingT@ is already an instance of all required classes. This implementation -- automatically solves all threading-related problems, since in fact it does not -- have any shared state. -- -- * Use @System.Log.Heavy.IO@ module. If you do not have monadic transformers at all, -- and your application works in pure IO, this may be the simplest way. However, -- this is a bit fragile, because you have to be sure that you always call logging -- functions only when logging state is initialized, i.e. within @withLoggingIO@ -- call. This implementation stores required state in thread-local storage. -- -- * Implement required class instances for monadic stack that you already use in -- your application. For example, if you already have something like -- @ReaderT StateT ExceptT IO@, it will be probably better to add a couple of -- fields to StateT's state to track logging state, than change your stack to -- @ReaderT StateT LoggingT ExceptT IO@. If you wish to store logging state in some -- kind of shared storage (global IORef or whatever), then you should think about -- thread-safety by yourself. -- -- When you decided which monadic context you will use, you will call one of -- @withLogging*@ functions to run the entire thing, and inside that you will construct -- instances of @LogMessage@ type and call @logMessage@ or @logMessage'@ function on them -- to actually log a message. You probably will want to use some shortcut functions to -- construct @LogMessage@ instances and log them. There are some provided -- by this package: -- -- * @System.Log.Heavy.Shortcuts@ module exports simple functions, that can be used -- in simple cases, when you do not want to write or check message source. -- -- * @System.Log.Heavy.TH@ module exports TH macros, which correctly fill message -- source and location. -- module System.Log.Heavy ( -- * Reexports module System.Log.Heavy.Types, module System.Log.Heavy.Level, module System.Log.Heavy.LoggingT, module System.Log.Heavy.Backends, module System.Log.Heavy.Backends.Dynamic, -- * Logging functions logMessage, -- * Run actions with logging withLogging, withLoggingF, withLoggingT, -- * Check current settings isLevelEnabledByBackend, isLevelEnabled ) where import Control.Monad.Trans import Control.Monad.Trans.Control import Control.Exception.Lifted (bracket) import qualified Data.Text.Lazy as TL import System.Log.Heavy.Types import System.Log.Heavy.Level import System.Log.Heavy.Util import System.Log.Heavy.LoggingT import System.Log.Heavy.Backends import System.Log.Heavy.Backends.Dynamic -- | Execute actions with logging backend. -- This is mostly an utility function to be used to construct custom -- logging frameworks for custom monad transformer stacks. withLoggingF :: (MonadBaseControl IO m, MonadIO m) => LoggingSettings -- ^ Settings of arbitrary logging backend. -> (forall b. IsLogBackend b => b -> m a) -- ^ Actions to execute with logging backend. -- Note that this type declaration binds argument -- to work with *any* implementation of backend. -> m a withLoggingF (LoggingSettings settings) actions = withLoggingB settings actions -- | Execute actions with logging. -- This function can be useful for monad stacks that store logging backend -- in State-like structure. withLogging :: (MonadBaseControl IO m, MonadIO m, HasLogger m) => LoggingSettings -- ^ Settings of arbitrary logging backend -> m a -- ^ Actions to be executed -> m a withLogging (LoggingSettings settings) actions = bracket (liftIO $ initLogBackend settings) (liftIO . cleanupLogBackend) (\b -> applyBackend b actions) -- | Execute actions with logging. -- This function is most convinient if you use @LoggingT@ as -- @HasLogging@ implementation. withLoggingT :: (MonadBaseControl IO m, MonadIO m) => LoggingSettings -- ^ Settings of arbitrary logging backend -> LoggingT m a -- ^ Actions to be executed -> m a withLoggingT (LoggingSettings settings) actions = withLoggingB settings $ \backend -> let logger = makeLogger backend in runLoggingT actions $ LoggingTState logger (AnyLogBackend backend) [] -- | Check if logging of events of specified level from specified source -- is enabled by backend. -- -- This function assumes that if some events filtering is enabled by the -- backend, it does not depend on message text, only on source and -- severity level. isLevelEnabledByBackend :: forall m. (Monad m, MonadIO m, HasLogBackend AnyLogBackend m) => LogSource -> Level -> m Bool isLevelEnabledByBackend src level = do backend <- getLogBackend :: m AnyLogBackend let msg = LogMessage level src undefined TL.empty () [] liftIO $ wouldWriteMessage backend msg -- | Check if logging of events of specified level from specified source -- is enabled by both context and backend filter. -- -- This function assumes that if some events filtering is enabled by the -- backend, it does not depend on message text, only on source and -- severity level. isLevelEnabled :: forall m. (Monad m, MonadIO m, HasLogBackend AnyLogBackend m, HasLogContext m) => LogSource -> Level -> m Bool isLevelEnabled src level = do let msg = LogMessage level src undefined TL.empty () [] backend <- getLogBackend :: m AnyLogBackend isEnabledByBackend <- liftIO $ wouldWriteMessage backend msg isEnabledByContext <- checkContextFilterM msg return $ isEnabledByContext && isEnabledByBackend