Blammo-1.1.0.0: src/Blammo/Logging/Logger.hs
module Blammo.Logging.Logger
( Logger
, HasLogger(..)
, newLogger
, flushLogger
, getLoggerReformat
, getLoggerShouldLog
, pushLogStrLn
, flushLogStr
-- * Testing
, newTestLogger
, LoggedMessage(..)
, getLoggedMessages
, getLoggedMessagesLenient
, getLoggedMessagesUnsafe
) where
import Prelude
import Blammo.Logging.LogSettings
import Blammo.Logging.Terminal
import Blammo.Logging.Test hiding (getLoggedMessages)
import qualified Blammo.Logging.Test as LoggedMessages
import Control.Lens (Lens', view)
import Control.Monad (unless)
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Logger.Aeson
import Control.Monad.Reader (MonadReader)
import Data.ByteString (ByteString)
import Data.Either (partitionEithers, rights)
import Data.List (intercalate)
import GHC.Stack (HasCallStack)
import System.IO (stderr, stdout)
import System.Log.FastLogger
( LoggerSet
, defaultBufSize
, newFileLoggerSet
, newStderrLoggerSet
, newStdoutLoggerSet
)
import qualified System.Log.FastLogger as FastLogger (flushLogStr, pushLogStrLn)
import UnliftIO.Exception (throwString)
data Logger = Logger
{ lLoggerSet :: LoggerSet
, lReformat :: LogLevel -> ByteString -> ByteString
, lShouldLog :: LogSource -> LogLevel -> Bool
, lLoggedMessages :: Maybe LoggedMessages
}
getLoggerLoggerSet :: Logger -> LoggerSet
getLoggerLoggerSet = lLoggerSet
getLoggerReformat :: Logger -> LogLevel -> ByteString -> ByteString
getLoggerReformat = lReformat
getLoggerShouldLog :: Logger -> LogSource -> LogLevel -> Bool
getLoggerShouldLog = lShouldLog
pushLogStrLn :: MonadIO m => Logger -> LogStr -> m ()
pushLogStrLn logger str = case lLoggedMessages logger of
Nothing -> liftIO $ FastLogger.pushLogStrLn loggerSet str
Just lm -> appendLogStr lm str
where loggerSet = getLoggerLoggerSet logger
flushLogStr :: MonadIO m => Logger -> m ()
flushLogStr logger = case lLoggedMessages logger of
Nothing -> liftIO $ FastLogger.flushLogStr loggerSet
Just _ -> pure ()
where loggerSet = getLoggerLoggerSet logger
class HasLogger env where
loggerL :: Lens' env Logger
instance HasLogger Logger where
loggerL = id
newLogger :: MonadIO m => LogSettings -> m Logger
newLogger settings = do
(lLoggerSet, useColor) <- liftIO $ case getLogSettingsDestination settings of
LogDestinationStdout ->
(,)
<$> newStdoutLoggerSet defaultBufSize
<*> shouldColorHandle settings stdout
LogDestinationStderr ->
(,)
<$> newStderrLoggerSet defaultBufSize
<*> shouldColorHandle settings stderr
LogDestinationFile path ->
(,) <$> newFileLoggerSet defaultBufSize path <*> shouldColorAuto
settings
(pure False)
let
lReformat = case getLogSettingsFormat settings of
LogFormatJSON -> const id -- Color is ignored
LogFormatTerminal -> reformatTerminal useColor
lShouldLog = shouldLogLevel settings
lLoggedMessages = Nothing
pure $ Logger { .. }
flushLogger :: (MonadIO m, MonadReader env m, HasLogger env) => m ()
flushLogger = do
logger <- view loggerL
flushLogStr logger
-- | Create a 'Logger' that will capture log messages instead of logging them
--
-- See "Blammo.Logging.LoggedMessages" for more details.
--
newTestLogger :: MonadIO m => LogSettings -> m Logger
newTestLogger settings = go <$> newLogger settings <*> newLoggedMessages
where
go logger loggedMessages =
logger { lReformat = const id, lLoggedMessages = Just loggedMessages }
-- | Return the logged messages if 'newTestLogger' was used
--
-- If not, the empty list is returned.
--
getLoggedMessages
:: (MonadIO m, MonadReader env m, HasLogger env)
=> m [Either String LoggedMessage]
getLoggedMessages = do
logger <- view loggerL
maybe (pure []) LoggedMessages.getLoggedMessages $ lLoggedMessages logger
-- | 'getLoggedMessages' but ignore any messages that fail to parse
getLoggedMessagesLenient
:: (MonadIO m, MonadReader env m, HasLogger env) => m [LoggedMessage]
getLoggedMessagesLenient = rights <$> getLoggedMessages
-- | 'getLoggedMessages' but 'throwString' if any messages failed to parse
getLoggedMessagesUnsafe
:: (HasCallStack, MonadIO m, MonadReader env m, HasLogger env)
=> m [LoggedMessage]
getLoggedMessagesUnsafe = do
(failed, succeeded) <- partitionEithers <$> getLoggedMessages
succeeded <$ unless
(null failed)
(throwString
$ intercalate "\n"
$ "Messages were logged that didn't parse as LoggedMessage:"
: failed
)