packages feed

log-effectful-1.0.1.0: src/Effectful/Log.hs

{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | Logging via 'MonadLog'.
module Effectful.Log
  ( -- * Effect
    Log (..)

    -- ** Handlers
  , runLog

    -- * Re-exports
  , module Log
  ) where

import Data.Aeson.Types
import Data.Text (Text)
import Data.Time.Clock
import Effectful.Dispatch.Dynamic
import Effectful.Reader.Static
import Effectful
import Log

-- | Provide the ability to log messages via 'MonadLog'.
data Log :: Effect where
  LogMessageOp :: LogLevel -> Text -> Value -> Log m ()
  LocalData :: [Pair] -> m a -> Log m a
  LocalDomain :: Text -> m a -> Log m a
  LocalMaxLogLevel :: LogLevel -> m a -> Log m a
  GetLoggerEnv :: Log m LoggerEnv

type instance DispatchOf Log = Dynamic

-- | Run the 'Log' effect.
--
-- /Note:/ this is the @effectful@ version of 'runLogT'.
runLog
  :: IOE :> es
  => Text
  -- ^ Application component name to use.
  -> Logger
  -- ^ The logging back-end to use.
  -> LogLevel
  -- ^ The maximum log level allowed to be logged.
  -> Eff (Log : es) a
  -- ^ The computation to run.
  -> Eff es a
runLog component logger maxLogLevel = reinterpret reader $ \env -> \case
  LogMessageOp level message data_ -> do
    time <- liftIO getCurrentTime
    logEnv <- ask
    liftIO $ logMessageIO logEnv time level message data_
  LocalData data_ action -> localSeqUnlift env $ \unlift -> do
    (`local` unlift action) $ \logEnv -> logEnv { leData = data_ ++ leData logEnv }
  LocalDomain domain action -> localSeqUnlift env $ \unlift -> do
    (`local` unlift action) $ \logEnv -> logEnv { leDomain = leDomain logEnv ++ [domain] }
  LocalMaxLogLevel level action -> localSeqUnlift env $ \unlift -> do
    (`local` unlift action) $ \logEnv -> logEnv { leMaxLogLevel = level }
  GetLoggerEnv -> ask
  where
    reader = runReader LoggerEnv
      { leLogger = logger
      , leComponent = component
      , leDomain = []
      , leData = []
      , leMaxLogLevel = maxLogLevel
      }

-- | Orphan, canonical instance.
instance Log :> es => MonadLog (Eff es) where
  logMessage level message data_ = send $ LogMessageOp level message data_
  localData data_ action = send $ LocalData data_ action
  localDomain domain action = send $ LocalDomain domain action
  localMaxLogLevel level action = send $ LocalMaxLogLevel level action
  getLoggerEnv = send GetLoggerEnv