packages feed

monad-effect-logging 0.1.0.0 → 0.3.0.0

raw patch · 8 files changed

+1107/−884 lines, 8 filesdep +optparse-applicativePVP ok

version bump matches the API change (PVP)

Dependencies added: optparse-applicative

API changes (from Hackage documentation)

- Module.Logging: Log :: [LogCat] -> a -> Log a
- Module.Logging: LogMsg :: Maybe Loc -> Maybe LogSource -> a -> LogMsg a
- Module.Logging: [_logContent] :: Log a -> a
- Module.Logging: [_logLoc] :: LogMsg a -> Maybe Loc
- Module.Logging: [_logMsg] :: LogMsg a -> a
- Module.Logging: [_logSource] :: LogMsg a -> Maybe LogSource
- Module.Logging: [_logType] :: Log a -> [LogCat]
- Module.Logging: data Log a
- Module.Logging: data LogMsg a
- Module.Logging: data Logging (m :: Type -> Type) a
- Module.Logging: defaultLoggingFromArgs :: Logger IO LogS -> Maybe (IO ()) -> [String] -> Either Text (ModuleInitData LoggingModule)
- Module.Logging: defaultLoggingFromEnv :: Logger IO LogS -> Maybe (IO ()) -> IO (ModuleInitData LoggingModule)
- Module.Logging: instance (GHC.Internal.Base.Monad m, Data.TypeList.In' c (Module.Logging.Logging m Module.Logging.LogS) mods) => Control.Monad.Logger.MonadLogger (Control.Monad.Effect.EffT' c mods es m)
- Module.Logging: instance (m GHC.Types.~ GHC.Types.IO, Data.TypeList.In' c (Module.Logging.Logging m Module.Logging.LogS) mods) => Control.Monad.Logger.MonadLoggerIO (Control.Monad.Effect.EffT' c mods es m)
- Module.Logging: instance Control.Monad.Effect.Module (Module.Logging.Logging m a)
- Module.Logging: instance Control.Monad.Effect.SystemModule (Module.Logging.Logging m a)
- Module.Logging: instance Control.System.EventLoop c (Module.Logging.Logging m a) mods es
- Module.Logging: instance Control.System.Loadable c (Module.Logging.Logging GHC.Types.IO a) mods ies
- Module.Logging: instance GHC.Internal.Base.Applicative Module.Logging.Log
- Module.Logging: instance GHC.Internal.Base.Functor Module.Logging.Log
- Module.Logging: instance GHC.Internal.Base.Functor Module.Logging.LogMsg
- Module.Logging: instance GHC.Internal.Base.Monad Module.Logging.Log
- Module.Logging: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monoid (Module.Logging.Log a)
- Module.Logging: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monoid (Module.Logging.LogMsg a)
- Module.Logging: instance GHC.Internal.Base.Semigroup a => GHC.Internal.Base.Semigroup (Module.Logging.Log a)
- Module.Logging: instance GHC.Internal.Base.Semigroup a => GHC.Internal.Base.Semigroup (Module.Logging.LogMsg a)
- Module.Logging: localLog :: forall a (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) c. (Monad m, In (Logging m a) mods) => (Log a -> Log a) -> EffT mods es m c -> EffT mods es m c
- Module.Logging: logContent :: forall a1 a2 f. Functor f => (a1 -> f a2) -> Log a1 -> f (Log a2)
- Module.Logging: logLoc :: forall a f. Functor f => (Maybe Loc -> f (Maybe Loc)) -> LogMsg a -> f (LogMsg a)
- Module.Logging: logLog :: forall a (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type). (Monad m, In' c (Logging m a) mods) => Log a -> EffT' c mods es m ()
- Module.Logging: logMsg :: forall a1 a2 f. Functor f => (a1 -> f a2) -> LogMsg a1 -> f (LogMsg a2)
- Module.Logging: logSource :: forall a f. Functor f => (Maybe LogSource -> f (Maybe LogSource)) -> LogMsg a -> f (LogMsg a)
- Module.Logging: logType :: forall a f. Functor f => ([LogCat] -> f [LogCat]) -> Log a -> f (Log a)
- Module.Logging: runLogging :: forall (c :: (Type -> Type) -> [Type] -> Type) (m :: Type -> Type) logS (mods :: [Type]) (es :: [Type]) a. (ConsFDataList c (Logging m logS ': mods), Monad m) => Logger m logS -> EffT' c (Logging m logS ': mods) es m a -> EffT' c mods es m a
- Module.Logging: toLogStrS :: Show a => a -> LogStr
- Module.Logging: type LogData = LogS
- Module.Logging: type LogS = LogMsg LogStr
- Module.Logging.LogB: instance GHC.Internal.Base.Monoid Module.Logging.LogB.LogBuilder
- Module.Logging.LogB: instance GHC.Internal.Base.Semigroup Module.Logging.LogB.LogBuilder
- Module.Logging.LogB: instance GHC.Internal.Data.String.IsString Module.Logging.LogB.LogBuilder
- Module.Logging.LogB: instance GHC.Internal.Data.String.IsString Module.Logging.LogB.LogVal
- Module.Logging.LogB: logIO :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (MonadIO m, In' c (Logging IO LogB) mods, IsLogCat subType) => subType -> LogBuilder -> EffT' c mods es m ()
- Module.Logging.LogB: logLocIO :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (MonadIO m, In' c (Logging IO LogB) mods, IsLogCat subType) => Loc -> subType -> LogBuilder -> EffT' c mods es m ()
- Module.Logging.LogB: logLoc_ :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (Monad m, In' c (Logging m LogB) mods, IsLogCat subType) => Loc -> subType -> LogBuilder -> EffT' c mods es m ()
- Module.Logging.LogB: logShow :: Show a => a -> LogBuilder
- Module.Logging.LogB: logTH :: (IsLogCat subType, Lift subType) => subType -> Q Exp
- Module.Logging.LogB: logTHIO :: (IsLogCat subType, Lift subType) => subType -> Q Exp
- Module.Logging.LogB: log_ :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (Monad m, In' c (Logging m LogB) mods, IsLogCat subType) => subType -> LogBuilder -> EffT' c mods es m ()
- Module.Logging.LogB: logs :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]). (Monad m, In' c (Logging m LogB) mods) => [LogCat] -> LogBuilder -> EffT' c mods es m ()
- Module.Logging.LogB: logsIO :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]). (MonadIO m, In' c (Logging IO LogB) mods) => [LogCat] -> LogBuilder -> EffT' c mods es m ()
- Module.Logging.LogB: renderUsing :: (forall a. Show a => a -> LogStr) -> LogRenderer
- Module.Logging.LogB: toLog :: ToLogStr a => a -> LogBuilder
- Module.Logging.LogB: type LogB = LogMsg LogBuilder
- Module.Logging.LogB: type LogBuilder = LogVals -> LogVals
- Module.Logging.LogB: type LogRenderer = LogBuilder -> LogStr
- Module.Logging.LogB: type LogVals = [LogVal]
- Module.Logging.LogB: type LoggingModuleB = Logging IO LogB
- Module.Logging.LogS: data LogStr
- Module.Logging.LogS: logIO :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (MonadIO m, In' c (Logging IO LogS) mods, IsLogCat subType) => subType -> LogStr -> EffT' c mods es m ()
- Module.Logging.LogS: logLocIO :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (MonadIO m, In' c (Logging IO LogS) mods, IsLogCat subType) => Loc -> subType -> LogStr -> EffT' c mods es m ()
- Module.Logging.LogS: logLoc_ :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (Monad m, In' c (Logging m LogS) mods, IsLogCat subType) => Loc -> subType -> LogStr -> EffT' c mods es m ()
- Module.Logging.LogS: logS :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]). (Monad m, In' c (Logging m LogS) mods) => LogS -> EffT' c mods es m ()
- Module.Logging.LogS: logShow :: Show a => a -> LogStr
- Module.Logging.LogS: logTH :: (IsLogCat subType, Lift subType) => subType -> Q Exp
- Module.Logging.LogS: logTHIO :: (IsLogCat subType, Lift subType) => subType -> Q Exp
- Module.Logging.LogS: log_ :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) subType (es :: [Type]). (Monad m, In' c (Logging m LogS) mods, IsLogCat subType) => subType -> LogStr -> EffT' c mods es m ()
- Module.Logging.LogS: logs :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]). (Monad m, In' c (Logging m LogS) mods) => [LogCat] -> LogStr -> EffT' c mods es m ()
- Module.Logging.LogS: logsIO :: forall (m :: Type -> Type) (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]). (MonadIO m, In' c (Logging IO LogS) mods) => [LogCat] -> LogStr -> EffT' c mods es m ()
- Module.Logging.LogS: toLog :: ToLogStr a => a -> LogStr
- Module.Logging.LogS: type LogS = LogMsg LogStr
- Module.Logging.LogS: type LoggingModule = Logging IO LogS
- Module.Logging.Logger: baseToLogger :: (LogStr -> m ()) -> Logger m LogStr
- Module.Logging.Logger: instance GHC.Internal.Base.Applicative m => GHC.Internal.Base.Monoid (Module.Logging.Logger.LoggerWithCleanup m log)
- Module.Logging.Logger: instance GHC.Internal.Base.Applicative m => GHC.Internal.Base.Semigroup (Module.Logging.Logger.LoggerWithCleanup m log)
- Module.Logging.Logger: logSimple :: LogS -> LogStr
- Module.Logging.Logger: logWithRendering :: forall b (m :: Type -> Type). (b -> LogStr) -> Logger m LogS -> Logger m (LogMsg b)
- Module.Logging.Logger: simpleLogger :: MonadIO m => Timed -> (LogStr -> m ()) -> Logger m LogS
- Module.Logging.Logger: timeLogger :: forall (m :: Type -> Type). MonadIO m => Logger m LogStr -> Logger m LogStr
- Module.Logging.Logger: type Timed = Bool
- Module.Logging.Logger: typedLogger :: forall (m :: Type -> Type). Logger m LogStr -> Logger m LogStr
- Module.Logging.Logger: useBaseLogger :: ((LogStr -> m ()) -> Logger m logS) -> LoggerWithCleanup m LogStr -> LoggerWithCleanup m (Log logS)
- Module.Logging.Logger: useBaseLogger' :: Monad m => (Log logS -> m LogStr) -> LoggerWithCleanup m LogStr -> LoggerWithCleanup m (Log logS)
- Module.Logging.TraceId: runWithTraceId :: forall (mods :: [Type]) (errs :: [Type]) (m :: Type -> Type) a. (Monad m, ConsFDataList (FData :: (Type -> Type) -> [Type] -> Type) (WithTraceId ': mods)) => ModuleRead WithTraceId -> EffT (WithTraceId ': mods) errs m a -> EffT mods errs m a
- Module.Logging.TraceId: withTraceId' :: forall log (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) a. (Monad m, In (Logging m log) mods, NotIn WithTraceId mods, ConsFDataList (FData :: (Type -> Type) -> [Type] -> Type) (WithTraceId ': mods), log ~ LogS) => TraceId -> EffT (WithTraceId ': mods) es m a -> EffT mods es m a
+ Module.Logging: AnsiStyles :: StyleMode
+ Module.Logging: Black :: NamedColor
+ Module.Logging: Blue :: NamedColor
+ Module.Logging: Cyan :: NamedColor
+ Module.Logging: DefaultColor :: Color
+ Module.Logging: DocRenderOptions :: (forall a. Show a => a -> LogStr) -> StyleMode -> DocRenderOptions
+ Module.Logging: Green :: NamedColor
+ Module.Logging: LogEvent :: [LogCat] -> a -> LogEvent a
+ Module.Logging: LogWithSourceMeta :: Maybe Loc -> Maybe LogSource -> a -> LogWithSourceMeta a
+ Module.Logging: Magenta :: NamedColor
+ Module.Logging: Named :: !NamedColor -> Color
+ Module.Logging: NoStyles :: StyleMode
+ Module.Logging: RGB :: !Word8 -> !Word8 -> !Word8 -> Color
+ Module.Logging: Red :: NamedColor
+ Module.Logging: Style :: Maybe Color -> Maybe Color -> Bool -> Style
+ Module.Logging: White :: NamedColor
+ Module.Logging: Yellow :: NamedColor
+ Module.Logging: [_logEventCats] :: LogEvent a -> [LogCat]
+ Module.Logging: [_logEventPayload] :: LogEvent a -> a
+ Module.Logging: [_logMetaDoc] :: LogWithSourceMeta a -> a
+ Module.Logging: [_logMetaLoc] :: LogWithSourceMeta a -> Maybe Loc
+ Module.Logging: [_logMetaSource] :: LogWithSourceMeta a -> Maybe LogSource
+ Module.Logging: [docRenderShow] :: DocRenderOptions -> forall a. Show a => a -> LogStr
+ Module.Logging: [docRenderStyleMode] :: DocRenderOptions -> StyleMode
+ Module.Logging: [styleBg] :: Style -> Maybe Color
+ Module.Logging: [styleBold] :: Style -> Bool
+ Module.Logging: [styleFg] :: Style -> Maybe Color
+ Module.Logging: class ToLog a
+ Module.Logging: data Color
+ Module.Logging: data DocRenderOptions
+ Module.Logging: data LogDoc
+ Module.Logging: data LogEffect (m :: Type -> Type) a
+ Module.Logging: data LogEvent a
+ Module.Logging: data LogWithSourceMeta a
+ Module.Logging: data NamedColor
+ Module.Logging: data SomeShown
+ Module.Logging: data Style
+ Module.Logging: data StyleMode
+ Module.Logging: defaultDocRenderOptions :: DocRenderOptions
+ Module.Logging: defaultStyle :: Style
+ Module.Logging: emitLogEvent :: forall a (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type). (Monad m, In' c (LogEffect m a) mods) => LogEvent (LogWithSourceMeta a) -> EffT' c mods es m ()
+ Module.Logging: instance (GHC.Internal.Base.Monad m, Data.TypeList.In' c (Module.Logging.LogEffect m Module.Logging.LogDoc) mods) => Control.Monad.Logger.MonadLogger (Control.Monad.Effect.EffT' c mods es m)
+ Module.Logging: instance (m GHC.Types.~ GHC.Types.IO, Data.TypeList.In' c (Module.Logging.LogEffect m Module.Logging.LogDoc) mods) => Control.Monad.Logger.MonadLoggerIO (Control.Monad.Effect.EffT' c mods es m)
+ Module.Logging: instance Control.Monad.Effect.Module (Module.Logging.LogEffect m a)
+ Module.Logging: instance Control.Monad.Effect.SystemModule (Module.Logging.LogEffect m a)
+ Module.Logging: instance Control.System.EventLoop c (Module.Logging.LogEffect m a) mods es
+ Module.Logging: instance Control.System.Loadable c (Module.Logging.LogEffect GHC.Types.IO a) mods ies
+ Module.Logging: instance GHC.Classes.Eq Module.Logging.Color
+ Module.Logging: instance GHC.Classes.Eq Module.Logging.NamedColor
+ Module.Logging: instance GHC.Classes.Eq Module.Logging.Style
+ Module.Logging: instance GHC.Classes.Eq Module.Logging.StyleMode
+ Module.Logging: instance GHC.Internal.Base.Applicative Module.Logging.LogEvent
+ Module.Logging: instance GHC.Internal.Base.Functor Module.Logging.LogEvent
+ Module.Logging: instance GHC.Internal.Base.Functor Module.Logging.LogWithSourceMeta
+ Module.Logging: instance GHC.Internal.Base.Monad Module.Logging.LogEvent
+ Module.Logging: instance GHC.Internal.Base.Monoid Module.Logging.LogDoc
+ Module.Logging: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monoid (Module.Logging.LogEvent a)
+ Module.Logging: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monoid (Module.Logging.LogWithSourceMeta a)
+ Module.Logging: instance GHC.Internal.Base.Semigroup Module.Logging.LogDoc
+ Module.Logging: instance GHC.Internal.Base.Semigroup a => GHC.Internal.Base.Semigroup (Module.Logging.LogEvent a)
+ Module.Logging: instance GHC.Internal.Base.Semigroup a => GHC.Internal.Base.Semigroup (Module.Logging.LogWithSourceMeta a)
+ Module.Logging: instance GHC.Internal.Data.String.IsString Module.Logging.LogDoc
+ Module.Logging: instance GHC.Internal.Show.Show Module.Logging.Color
+ Module.Logging: instance GHC.Internal.Show.Show Module.Logging.NamedColor
+ Module.Logging: instance GHC.Internal.Show.Show Module.Logging.Style
+ Module.Logging: instance GHC.Internal.Show.Show Module.Logging.StyleMode
+ Module.Logging: instance Module.Logging.IsLogColor (GHC.Internal.Word.Word8, GHC.Internal.Word.Word8, GHC.Internal.Word.Word8)
+ Module.Logging: instance Module.Logging.IsLogColor Module.Logging.Color
+ Module.Logging: instance Module.Logging.IsLogColor Module.Logging.NamedColor
+ Module.Logging: instance Module.Logging.ToLog Data.Text.Internal.Text
+ Module.Logging: instance Module.Logging.ToLog GHC.Internal.Base.String
+ Module.Logging: instance Module.Logging.ToLog Module.Logging.LogDoc
+ Module.Logging: liftLogger :: (m () -> n ()) -> Logger m a -> Logger n a
+ Module.Logging: localLogEvent :: forall a (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) c. (Monad m, In (LogEffect m a) mods) => (LogEvent (LogWithSourceMeta a) -> LogEvent (LogWithSourceMeta a)) -> EffT mods es m c -> EffT mods es m c
+ Module.Logging: logBg :: IsLogColor color => color -> LogDoc -> LogDoc
+ Module.Logging: logBold :: LogDoc -> LogDoc
+ Module.Logging: logEventCats :: forall a f. Functor f => ([LogCat] -> f [LogCat]) -> LogEvent a -> f (LogEvent a)
+ Module.Logging: logEventPayload :: forall a1 a2 f. Functor f => (a1 -> f a2) -> LogEvent a1 -> f (LogEvent a2)
+ Module.Logging: logFg :: IsLogColor color => color -> LogDoc -> LogDoc
+ Module.Logging: logIO :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) cat. (MonadIO m, In' c (LogEffect IO LogDoc) mods, IsLogCat cat) => cat -> LogDoc -> EffT' c mods es m ()
+ Module.Logging: logLocIO :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) cat. (MonadIO m, In' c (LogEffect IO LogDoc) mods, IsLogCat cat) => Loc -> cat -> LogDoc -> EffT' c mods es m ()
+ Module.Logging: logLoc_ :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) cat. (Monad m, In' c (LogEffect m LogDoc) mods, IsLogCat cat) => Loc -> cat -> LogDoc -> EffT' c mods es m ()
+ Module.Logging: logMetaDoc :: forall a1 a2 f. Functor f => (a1 -> f a2) -> LogWithSourceMeta a1 -> f (LogWithSourceMeta a2)
+ Module.Logging: logMetaLoc :: forall a f. Functor f => (Maybe Loc -> f (Maybe Loc)) -> LogWithSourceMeta a -> f (LogWithSourceMeta a)
+ Module.Logging: logMetaSource :: forall a f. Functor f => (Maybe LogSource -> f (Maybe LogSource)) -> LogWithSourceMeta a -> f (LogWithSourceMeta a)
+ Module.Logging: logRaw :: LogStr -> LogDoc
+ Module.Logging: logShow :: Show a => a -> LogDoc
+ Module.Logging: logTH :: (IsLogCat cat, Lift cat) => cat -> Q Exp
+ Module.Logging: logTHIO :: (IsLogCat cat, Lift cat) => cat -> Q Exp
+ Module.Logging: logTypeName :: IsLogCat cat => cat -> LogStr
+ Module.Logging: log_ :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) cat. (Monad m, In' c (LogEffect m LogDoc) mods, IsLogCat cat) => cat -> LogDoc -> EffT' c mods es m ()
+ Module.Logging: logs :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type). (Monad m, In' c (LogEffect m LogDoc) mods) => [LogCat] -> LogDoc -> EffT' c mods es m ()
+ Module.Logging: logsIO :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type). (MonadIO m, In' c (LogEffect IO LogDoc) mods) => [LogCat] -> LogDoc -> EffT' c mods es m ()
+ Module.Logging: renderLogDoc :: DocRenderOptions -> LogDoc -> LogStr
+ Module.Logging: runLogEffect :: forall (c :: (Type -> Type) -> [Type] -> Type) (m :: Type -> Type) a (mods :: [Type]) (es :: [Type]) b. (ConsFDataList c (LogEffect m a ': mods), Monad m) => Logger m (LogWithSourceMeta a) -> EffT' c (LogEffect m a ': mods) es m b -> EffT' c mods es m b
+ Module.Logging: someLogCatDisplay :: LogCat -> LogDoc
+ Module.Logging: toLog :: ToLog a => a -> LogDoc
+ Module.Logging: type Logging = LogEffect
+ Module.Logging.Logger: LogCatChunk :: LogOrderControl
+ Module.Logging.Logger: LogDocChunk :: LogOrderControl
+ Module.Logging.Logger: LogLocChunk :: LogOrderControl
+ Module.Logging.Logger: LogSrcChunk :: LogOrderControl
+ Module.Logging.Logger: LogTimeChunk :: LogOrderControl
+ Module.Logging.Logger: LoggerOptions :: DocRenderOptions -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe [LogOrderControl] -> LoggerOptions
+ Module.Logging.Logger: [loggerAppendNewline] :: LoggerOptions -> Bool
+ Module.Logging.Logger: [loggerDocRenderOptions] :: LoggerOptions -> DocRenderOptions
+ Module.Logging.Logger: [loggerIncludeCats] :: LoggerOptions -> Bool
+ Module.Logging.Logger: [loggerIncludeLoc] :: LoggerOptions -> Bool
+ Module.Logging.Logger: [loggerIncludeSource] :: LoggerOptions -> Bool
+ Module.Logging.Logger: [loggerIncludeTime] :: LoggerOptions -> Bool
+ Module.Logging.Logger: [loggerJsonFormat] :: LoggerOptions -> Bool
+ Module.Logging.Logger: [loggerOrderControl] :: LoggerOptions -> Maybe [LogOrderControl]
+ Module.Logging.Logger: buildLoggerStyle :: LoggerStyle -> LoggerOptions
+ Module.Logging.Logger: createFileLoggerWith :: MonadIO m => Integer -> Int -> FilePath -> m (LoggerWithCleanup IO LogStr)
+ Module.Logging.Logger: data LogOrderControl
+ Module.Logging.Logger: data LoggerOptions
+ Module.Logging.Logger: defaultLoggerStyle :: LoggerOptions
+ Module.Logging.Logger: defaultLoggingFromArgs :: LoggerWithCleanup IO (LogEvent (LogWithSourceMeta LogDoc)) -> [String] -> Either Text (ModuleInitData LoggingModule)
+ Module.Logging.Logger: defaultLoggingFromEnv :: LoggerWithCleanup IO (LogEvent (LogWithSourceMeta LogDoc)) -> IO (ModuleInitData LoggingModule)
+ Module.Logging.Logger: defaultLoggingOptParser :: forall (m :: Type -> Type). Applicative m => LoggerWithCleanup m (LogEvent (LogWithSourceMeta LogDoc)) -> Parser (ModuleInitData (LogEffect m LogDoc))
+ Module.Logging.Logger: instance GHC.Classes.Eq Module.Logging.Logger.LogOrderControl
+ Module.Logging.Logger: instance GHC.Classes.Ord Module.Logging.Logger.LogOrderControl
+ Module.Logging.Logger: instance GHC.Internal.Base.Applicative m => GHC.Internal.Base.Monoid (Module.Logging.Logger.LoggerWithCleanup m a)
+ Module.Logging.Logger: instance GHC.Internal.Base.Applicative m => GHC.Internal.Base.Semigroup (Module.Logging.Logger.LoggerWithCleanup m a)
+ Module.Logging.Logger: instance GHC.Internal.Show.Show Module.Logging.Logger.LogOrderControl
+ Module.Logging.Logger: loggerFromRenderer :: MonadIO m => LoggerOptions -> (LogStr -> m ()) -> Logger m (LogWithSourceMeta LogDoc)
+ Module.Logging.Logger: loggerJson :: LoggerStyle
+ Module.Logging.Logger: loggerNoCats :: LoggerStyle
+ Module.Logging.Logger: loggerNoLoc :: LoggerStyle
+ Module.Logging.Logger: loggerNoNewline :: LoggerStyle
+ Module.Logging.Logger: loggerNoSource :: LoggerStyle
+ Module.Logging.Logger: loggerNoStyle :: LoggerStyle
+ Module.Logging.Logger: loggerNoTime :: LoggerStyle
+ Module.Logging.Logger: loggerOrder :: [LogOrderControl] -> LoggerStyle
+ Module.Logging.Logger: loggerShowWith :: (forall a. Show a => a -> LogStr) -> LoggerStyle
+ Module.Logging.Logger: loggerUseAnsi :: LoggerStyle
+ Module.Logging.Logger: makeLoggerFromBase :: forall (m :: Type -> Type). MonadIO m => LoggerOptions -> LoggerWithCleanup m LogStr -> LoggerWithCleanup m (LogEvent (LogWithSourceMeta LogDoc))
+ Module.Logging.Logger: renderLogEvent :: LoggerOptions -> LogEvent (LogWithSourceMeta LogDoc) -> IO LogStr
+ Module.Logging.Logger: type LoggerStyle = LoggerOptions -> LoggerOptions
+ Module.Logging.TraceId: newTraceId :: ModuleRead TraceIdGen -> IO TraceId
+ Module.Logging.TraceId: traceId :: ModuleRead WithTraceId -> TraceId
- Module.Logging: Logger :: (Log a -> m ()) -> Logger (m :: Type -> Type) a
+ Module.Logging: Logger :: (LogEvent a -> m ()) -> Logger (m :: Type -> Type) a
- Module.Logging: [LogCat] :: forall sub. IsLogCat sub => sub -> LogCat
+ Module.Logging: [LogCat] :: forall cat. IsLogCat cat => cat -> LogCat
- Module.Logging: [_runLogger] :: Logger (m :: Type -> Type) a -> Log a -> m ()
+ Module.Logging: [_runLogger] :: Logger (m :: Type -> Type) a -> LogEvent a -> m ()
- Module.Logging: class Typeable sub => IsLogCat sub
+ Module.Logging: class Typeable cat => IsLogCat cat
- Module.Logging: effAddLogCat :: forall a (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) b. (Monad m, In' c (Logging m a) mods) => LogCat -> EffT' c mods es m b -> EffT' c mods es m b
+ Module.Logging: effAddLogCat :: forall a (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) b. (Monad m, In' c (LogEffect m a) mods) => LogCat -> EffT' c mods es m b -> EffT' c mods es m b
- Module.Logging: effAddLogCat' :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) b. (Monad m, In' c (Logging m LogS) mods) => LogCat -> EffT' c mods es m b -> EffT' c mods es m b
+ Module.Logging: effAddLogCat' :: forall (c :: (Type -> Type) -> [Type] -> Type) (mods :: [Type]) (es :: [Type]) (m :: Type -> Type) b. (Monad m, In' c (LogEffect m LogDoc) mods) => LogCat -> EffT' c mods es m b -> EffT' c mods es m b
- Module.Logging: isLogCat :: IsLogCat sub => Predicate LogCat
+ Module.Logging: isLogCat :: IsLogCat cat => Predicate LogCat
- Module.Logging: isLogSubType :: IsLogCat sub => Predicate sub -> Predicate LogCat
+ Module.Logging: isLogSubType :: IsLogCat cat => Predicate cat -> Predicate LogCat
- Module.Logging: localLogger :: forall a (c :: (Type -> Type) -> [Type] -> Type) (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) b. (Monad m, In' c (Logging m a) mods) => (Logger m a -> Logger m a) -> EffT' c mods es m b -> EffT' c mods es m b
+ Module.Logging: localLogger :: forall a (c :: (Type -> Type) -> [Type] -> Type) (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) b. (Monad m, In' c (LogEffect m a) mods) => (Logger m (LogWithSourceMeta a) -> Logger m (LogWithSourceMeta a)) -> EffT' c mods es m b -> EffT' c mods es m b
- Module.Logging: logTypeDisplay :: IsLogCat sub => sub -> LogStr
+ Module.Logging: logTypeDisplay :: IsLogCat cat => LogStr -> LogDoc
- Module.Logging: monadLoggerAdapter :: Logger m LogS -> Loc -> LogSource -> LogLevel -> LogStr -> m ()
+ Module.Logging: monadLoggerAdapter :: Logger m (LogWithSourceMeta LogDoc) -> Loc -> LogSource -> LogLevel -> LogStr -> m ()
- Module.Logging: runLogger :: forall m1 a1 m2 a2 p f. (Profunctor p, Functor f) => p (Log a1 -> m1 ()) (f (Log a2 -> m2 ())) -> p (Logger m1 a1) (f (Logger m2 a2))
+ Module.Logging: runLogger :: Logger m a -> LogEvent a -> m ()
- Module.Logging: severity :: IsLogCat sub => sub -> Maybe LogSeverity
+ Module.Logging: severity :: IsLogCat cat => cat -> Maybe LogSeverity
- Module.Logging: type LoggingModule = Logging IO LogS
+ Module.Logging: type LoggingModule = LogEffect IO LogDoc
- Module.Logging: withLiftLogger :: forall m n (c :: (Type -> Type) -> [Type] -> Type) a (mods :: [Type]) (es :: [Type]) b. (Monad m, In' c (Logging m a) (Logging m a ': mods), ConsFDataList c (Logging n a ': mods), ConsFDataList c (Logging m a ': mods)) => (forall x. () => m x -> n x) -> EffT' c (Logging n a ': mods) es m b -> EffT' c (Logging m a ': mods) es m b
+ Module.Logging: withLiftLogger :: forall m n (c :: (Type -> Type) -> [Type] -> Type) a (mods :: [Type]) (es :: [Type]) b. (Monad m, In' c (LogEffect m a) (LogEffect m a ': mods), ConsFDataList c (LogEffect n a ': mods), ConsFDataList c (LogEffect m a ': mods)) => (forall x. () => m x -> n x) -> EffT' c (LogEffect n a ': mods) es m b -> EffT' c (LogEffect m a ': mods) es m b
- Module.Logging.Logger: LoggerWithCleanup :: (log -> m ()) -> m () -> LoggerWithCleanup (m :: Type -> Type) log
+ Module.Logging.Logger: LoggerWithCleanup :: (a -> m ()) -> m () -> LoggerWithCleanup (m :: Type -> Type) a
- Module.Logging.Logger: [baseLogFunc] :: LoggerWithCleanup (m :: Type -> Type) log -> log -> m ()
+ Module.Logging.Logger: [baseLogFunc] :: LoggerWithCleanup (m :: Type -> Type) a -> a -> m ()
- Module.Logging.Logger: [cleanUpFunc] :: LoggerWithCleanup (m :: Type -> Type) log -> m ()
+ Module.Logging.Logger: [cleanUpFunc] :: LoggerWithCleanup (m :: Type -> Type) a -> m ()
- Module.Logging.Logger: data LoggerWithCleanup (m :: Type -> Type) log
+ Module.Logging.Logger: data LoggerWithCleanup (m :: Type -> Type) a
- Module.Logging.Logger: liftBaseLogger :: (m () -> n ()) -> LoggerWithCleanup m log -> LoggerWithCleanup n log
+ Module.Logging.Logger: liftBaseLogger :: (m () -> n ()) -> LoggerWithCleanup m a -> LoggerWithCleanup n a
- Module.Logging.Logger: withBaseLogger :: forall (c :: (Type -> Type) -> [Type] -> Type) m logS (mods :: [Type]) (es :: [Type]) a. (ConsFDataList c (Logging m (LogMsg logS) ': mods), Monad m, MonadMask m) => m (LoggerWithCleanup m LogStr) -> ((LogStr -> m ()) -> Logger m (LogMsg logS)) -> EffT' c (Logging m (LogMsg logS) ': mods) es m a -> EffT' c mods es m a
+ Module.Logging.Logger: withBaseLogger :: forall (c :: (Type -> Type) -> [Type] -> Type) m (mods :: [Type]) (es :: [Type]) a. (ConsFDataList c (LogEffect m LogDoc ': mods), MonadIO m, MonadMask m) => m (LoggerWithCleanup m LogStr) -> LoggerOptions -> EffT' c (LogEffect m LogDoc ': mods) es m a -> EffT' c mods es m a
- Module.Logging.Logger: withBaseLoggerIO :: IO (LoggerWithCleanup IO LogStr) -> ((LogStr -> IO ()) -> Logger IO logS) -> (Logger IO logS -> IO a) -> IO a
+ Module.Logging.Logger: withBaseLoggerIO :: IO (LoggerWithCleanup IO LogStr) -> LoggerOptions -> (Logger IO (LogWithSourceMeta LogDoc) -> IO a) -> IO a
- Module.Logging.Logger: withLoggerCleanup :: forall (c :: (Type -> Type) -> [Type] -> Type) (m :: Type -> Type) logS (mods :: [Type]) (es :: [Type]) a. (ConsFDataList c (Logging m logS ': mods), Monad m, MonadMask m) => LoggerWithCleanup m (Log logS) -> EffT' c (Logging m logS ': mods) es m a -> EffT' c mods es m a
+ Module.Logging.Logger: withLoggerCleanup :: forall (c :: (Type -> Type) -> [Type] -> Type) (m :: Type -> Type) a (mods :: [Type]) (es :: [Type]) b. (ConsFDataList c (LogEffect m a ': mods), Monad m, MonadMask m) => LoggerWithCleanup m (LogEvent (LogWithSourceMeta a)) -> EffT' c (LogEffect m a ': mods) es m b -> EffT' c mods es m b
- Module.Logging.TraceId: withNewTraceId :: forall log (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) a. (MonadIO m, In TraceIdGen mods, In (Logging m log) mods, NotIn WithTraceId mods, ConsFDataList (FData :: (Type -> Type) -> [Type] -> Type) (WithTraceId ': mods)) => EffT (WithTraceId ': mods) es m a -> EffT mods es m a
+ Module.Logging.TraceId: withNewTraceId :: forall doc (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) a. (MonadIO m, In TraceIdGen mods, In (LogEffect m doc) mods, NotIn WithTraceId mods, ConsFDataList (FData :: (Type -> Type) -> [Type] -> Type) (WithTraceId ': mods)) => EffT (WithTraceId ': mods) es m a -> EffT mods es m a
- Module.Logging.TraceId: withTraceId :: forall log (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) a. (Monad m, In (Logging m log) mods, NotIn WithTraceId mods, ConsFDataList (FData :: (Type -> Type) -> [Type] -> Type) (WithTraceId ': mods)) => TraceId -> EffT (WithTraceId ': mods) es m a -> EffT mods es m a
+ Module.Logging.TraceId: withTraceId :: forall doc (m :: Type -> Type) (mods :: [Type]) (es :: [Type]) a. (Monad m, In (LogEffect m doc) mods, NotIn WithTraceId mods, ConsFDataList (FData :: (Type -> Type) -> [Type] -> Type) (WithTraceId ': mods)) => TraceId -> EffT (WithTraceId ': mods) es m a -> EffT mods es m a

Files

README.md view
@@ -1,227 +1,153 @@ # Flexible Logging with Monad-Effect -`monad-effect-logging` is a flexible logging system utilizing the `monad-effect` effect system for Haskell, it gives you very fine control over the logging behavior.--Main features include:+`monad-effect-logging` is a pure structured logging library for the `monad-effect` ecosystem. -* (Optional) Separation of log generation and rendering+The current API is centered on one unified message payload `LogDoc`. -* Pure logging+## Highlights -* Extensible log categories+* You build one `LogDoc` value and decide at the boundary how to render it: -* Compatible with `monad-logger`+  - plain text+  - ANSI colored text+  - different `Show` strategies+  - custom logger pipelines+  +* Color stays semantic until rendering time. A file logger can ignore color while a console logger can emit ANSI codes from the exact same log event. -* `TraceId` support+* Excellent `TraceId` support -## Type Definition+* Open log categories, extensible -```haskell-import qualified Control.Monad.Logger as ML+* Easy JSON logging by tagging `loggerJson` -data Logging m a+This enables you to easily add JSON logging to your files while keeping the console output colorful and readable: you can write no-color json logging to a file while use `pSho` and color constructors to display stuff on the screen at the same time. -instance Module (Logging m (a :: Type)) where-  newtype ModuleRead  (Logging m a) = LoggingRead-    { logging :: Logger m a-    }-  data    ModuleState (Logging m a) = LoggingState+## Core types -data Log a = Log-  { _logType    :: [LogCat]-  , _logContent :: a-  } deriving (Functor)+- `LogEvent` for the event envelope+- `LogWithSourceMeta` for source-location metadata+- `LogDoc` for the structured log message+- `Logger` for the sink+- `LogEffect` for the installed effect -data LogMsg a = LogMsg-  { _logLoc    :: Maybe ML.Loc-  , _logSource :: Maybe ML.LogSource-  , _logMsg    :: a+```haskell+data LogEvent a = LogEvent+  { logEventCats :: [LogCat]+  , logEventPayload :: a   }-``` -A `Logger m a` is just a function that takes a `Log a` and produces an effect in `m ()`.+data LogWithSourceMeta a = LogWithSourceMeta+  { logMetaLoc :: Maybe Loc+  , logMetaSource :: Maybe LogSource+  , logMetaDoc :: a+  } -```haskell-type Logger :: (Type -> Type) -> Type -> Type newtype Logger m a = Logger-  { _runLogger :: Log a -> m ()+  { runLogger :: LogEvent a -> m ()   }-``` -## Features+data LogEffect m a -I developed it by using it and changing it to meet my needs. It solves the following problems:+data LogDoc+``` -### Separation Of Logs and Rendering+The default installed logging effect is: -Sometimes I want to write logs to different files, or sending logs to stdout and files at the same time. I wish I can use the `pretty-simple` pretty print with color in the terminal, but no color in the log files.+```haskell+type Logging = LogEffect IO LogDoc+``` -This means we have to separate log generation and log rendering, we can do exactly that: we provide two default Loggings styles in `Module.Logging.LogS` which uses `Logger m (LogMsg LogStr)` and `Module.Logging.LogB` which uses `Logger m (LogMsg LogBuilder)` (allows you to separate log generation and rendering).+## Building messages -See `Module.Logging.LogB` for relevant functions and types. Here is an example:+String literals work through `IsString`, and deferred values use `logShow`.  ```haskell-import Control.Monad.Effect-import Module.RS import Module.Logging-import Module.Logging.LogB-import Module.Logging.Logger-import Text.Pretty.Simple (pShow, pShowNoColor) -runApp :: EffT '[RModule ProxyState, RModule ProxySettings, LoggingModuleB] NoError IO () -> IO ()-runApp app = do-  opts :: ProxyOptions Unwrapped <- unwrapRecord "Haskell Proxy Server" -- read command line options-  case optionsToSettings opts of-    Nothing -> putStrLn "Invalid options provided."-    Just settings -> do-      baseStdLogger <- useBaseLogger (logWithRendering (renderUsing (toLogStr . pShow)) . simpleLogger True)-        <$> createSimpleConcurrentStdoutBaseLogger-      baseFileLogger <- useBaseLogger (logWithRendering (renderUsing (toLogStr . pShowNoColor)) . simpleLogger True)-        <$> createFileLogger "proxy.log"-      let logger = baseStdLogger <> baseFileLogger :: LoggerWithCleanup IO LogB-      runEffT00 $ withLoggerCleanup logger $ do-        state <- initializeState settings-        runRModule settings $ runRModule state app--main :: IO ()-main = runApp $ do-  activeConnections <- liftIO $ newTVarIO M.empty-  $(logTH Info) "Launching OwO"-  settings <- askR @ProxySettings-  $(logTH Info) $ "Options:" <> logShow settings-  traceId <- liftIO $ newTVarIO (0 :: Word64)-  -- | A Socket is a communication endpoint.-  bracketEffT (liftIO $ socket AF_INET Stream defaultProtocol)-    (\s -> do-      liftIO $ close s-      $(logTH Info) "Main Socket Closed."-      threadIds <- liftIO $ M.elems <$> readTVarIO activeConnections-      forM_ threadIds $ \t -> do-        liftIO $ killThread t-        $(logTH Info) $ "Killed thread: " <> logShow t-    )-    $ \sock -> do-    -- ... Rest of the code+example :: (Monad m, In (LogEffect m LogDoc) mods) => EffT mods es m ()+example = do+  $(logTH Info) $ "starting request " <> logShow (42 :: Int)+  $(logTH Warn) $ logFg Yellow "slow query: " <> toLog ("SELECT ..." :: String) ``` -Those `logShow` does not immediately show anything, they are only rendered when the log is actually written to the output, using the specified rendering function.--### Pure Logging+Useful helpers: -Logging typically works in `IO`, but sometimes I need a complex pure function state machine that outputs diagnostics. It would be great if I don't need to change anything inside the function, using the same logging utilities. The parametrized monad field of the logging effect allows you to use a pure monad (for example writer or state monads) to collect the logs, without needing to change anything inside the function (only need to change how you run it).+- `logRaw`+- `logShow`+- `logFg`+- `logBg`+- `logBold`+- `toLog` -```haskell-eventHandler-  :: (Monad pureMonad)-  => InputEvent-  -> EffT-      [ Logging pureMonad LogData -- ^ We will use logging to generate diagnostics-      , EventState                -- ^ We need to read and update the state-      ]-      [ ErrorText "not-allowed"-      ]-      pureMonad-      [OutputCommands]      -- ^ Output commands from the event module-```+Literal strings can be directly typed in using `IsString` class, no need to convert using helpers. -### Extensible Log Categories+## Rendering and base loggers -You can easily define your own log categories, not just `Debug`, `Info`, `Warn`, `Error`. A log message can have multiple categories, you can add category at anytime using `effAddLogCat`, these types can be used to filter logs later.+Most applications should use one options-based helper from `Module.Logging.Logger`:  ```haskell--- | An exsitential type that wraps all logging categories, it is easy to define a new instance-data LogCat where-  LogCat :: forall sub. IsLogCat sub => sub -> LogCat+import Module.Logging+import Module.Logging.Logger --- | So every module can have its own logging category type, for example--- Database module can have `data Database` used as a log type------ and have a subtype--- @--- data DatabaseSubType = ConnectionPool | Query | Migration | Cursor deriving (Show, Eq)--- @------ you can then write instance------ @--- instance IsLogCat DatabaseSubType where---   severity _    = Nothing---   logTypeDisplay _ = "DB"--- @-class Typeable sub => IsLogCat (sub :: Type) where-  severity :: sub -> Maybe LogSeverity-  severity _ = Nothing-  {-# INLINE severity #-}-  -- | This is used for display only-  logTypeDisplay :: sub -> ML.LogStr-  {-# MINIMAL logTypeDisplay #-}+runApp :: EffT '[LogEffect IO LogDoc] NoError IO () -> IO ()+runApp app = do+  stdoutBase <- createSimpleConcurrentStdoutBaseLogger+  fileBase <- createFileLogger "app.log"++  let stdoutLogger = makeLoggerFromBase ( buildLoggerStyle loggerUseAnsi                ) stdoutBase+  let fileLogger   = makeLoggerFromBase ( buildLoggerStyle (loggerJson . loggerNoStyle) ) fileBase++  runEffT00 $ withLoggerCleanup (stdoutLogger <> fileLogger) app ``` -Here is an example of defining your own log category `ProxyLog`:+Here each style is a builder function `LoggerOptions -> LoggerOptions`, and the `buildLoggerStyle` function is just a composition of them on the `defaultLoggerStyle`. -```haskell-import Module.Logging as L-import Language.Haskell.TH.Syntax (Lift)+For custom pipelines, use the lower-level building blocks: -data ProxyLog = Bytes | Logic deriving (Show, Eq, Lift)-instance IsLogCat ProxyLog where-  severity Bytes = severity L.Debug-  severity Logic = severity L.Info-  logTypeDisplay Bytes = "BYTES"-  logTypeDisplay Logic = "LOGIC"+- `renderLogEvent`+- `loggerFromRenderer`+- your own `Logger`++Styles compose as normal functions:++```haskell+verboseConsole :: LoggerOptions+verboseConsole =+  buildLoggerStyle+    $ loggerUseAnsi+    . loggerOrder [LogTimeChunk, LogCatChunk, LogLocChunk, LogDocChunk] ``` -The `Lift` class is only necessary if you want to use them inside `logTH` template haskell utilities, otherwise you can remove it.+## Categories -To use them, using functions in `Module.Logging.LogS` or `Module.Logging.LogB`, under corresponding logging context:+Categories are open and extensible:  ```haskell-import Module.Logging-import Module.Logging.LogS+data ProxyLog = Bytes | Logic deriving (Lift)+-- The `Lift` class is only necessary if you want to use them inside `logTH` template haskell+-- utilities, otherwise you can remove it. -example :: (In (Logging m LogS) mods, Monad m) => EffT mods es m ()-example = do-  sendBytesToClient 1024-  $(logTH Bytes) "Sent 1024 bytes to client"-  processUserLogin-  $(logTH Logic) "User logged in successfully"+instance IsLogCat ProxyLog where+  severity Bytes = severity Debug+  severity Logic = severity Info+  logTypeName Bytes = "BYTES"+  logTypeName Logic = "LOGIC" ``` -### Compatible With `monad-logger`+You can add local categories with `effAddLogCat`, and filter them with the existing combinators. -You can use all utilities from `monad-logger` inside the `Logging m LogS` effect here since we implement the `MonadLogger` and `MonadLoggerIO` class for it. Although we recommend using the logging utilities provided in `Module.Logging.LogS` and `Module.Logging.LogB`.+## `MonadLogger` compatibility -```haskell-botInstanceToModule :: BotInstance -> EffT '[LoggingModule] NoError IO BotModules-botInstanceToModule bot@(BotInstance runFlag identityFlags commandFlags mode proxyFlags logFlags watchDogFlags _) = do-    $(logInfo) $ "\n### Starting bot instance: " <> tshow bot-    $(logInfo) $ "Running mode: "   <> tshow mode-    $(logInfo) $ "Running flags: "  <> tshow runFlag-    $(logInfo) $ "Identity flags: " <> tshow identityFlags-    $(logInfo) $ "Command flags: "  <> tshow commandFlags-    $(logInfo) $ "Proxy flags: "    <> tshow proxyFlags-    $(logInfo) $ "Log flags: "      <> tshow logFlags-    $(logInfo) $ "WatchDog flags: " <> tshow watchDogFlags-    let mGlobalSysMsg = listToMaybe [ pack sysMsg | UseSysMsg sysMsg <- identityFlags ]-        withDefault def [] = def-        withDefault _ xs = xs-        botModules = BotModules-          { canUseGroupCommands   = withDefault (identifier <$> allGroupCommands)   (coerce commandFlags)-          , canUsePrivateCommands = withDefault (identifier <$> allPrivateCommands) (coerce commandFlags)-          , nameOfBot = BotName $ listToMaybe [ nameBot | UseName nameBot <- identityFlags ]-          , botId = fromMaybe 0 $ listToMaybe [ idBot | UseId idBot <- identityFlags ]-          , globalSysMsg = mGlobalSysMsg-          , proxyTChans = []-          , logFile = [ logFile | LogFlag logFile <- logFlags ]-          , botInstance = bot-          }-    return botModules-```+`MonadLogger` and `MonadLoggerIO` are implemented for `LogEffect m LogDoc`. -### `TraceId` Support+Incoming `monad-logger` messages are wrapped as `logRaw`, so compatibility fits directly into the unified payload model. -In complex applications, it is often useful to trace the flow of a request or operation across multiple log entries. Use `withTraceId` to attach a `TraceId` to a block of codes, all logger inside that block will automatically include the `TraceId` in their log categories.+## `TraceId` -We also included some optional trace id generation utilities.+This library also provides a super convenient `TraceId` mechanism that can attach scoped trace IDs to log events.+`TraceId` is just a log category. Use `withTraceId` or one of the provided generators from `Module.Logging.TraceId`. -See `Module.Logging.TraceId` for relevant functions and types.+## Status++This release presents the library around `LogDoc`, options-based logger construction, open categories, and boundary-driven rendering.
monad-effect-logging.cabal view
@@ -9,7 +9,7 @@ -- PVP summary:     +-+------- breaking API changes --                  | | +----- non-breaking API additions --                  | | | +--- code changes with no API change-version:            0.1.0.0+version:            0.3.0.0  -- A short (one-line) description of the package. synopsis: A flexible logging system utilizing the `monad-effect` effect system.@@ -54,8 +54,6 @@      -- Modules exported by the library.     exposed-modules: Module.Logging-                   , Module.Logging.LogS-                   , Module.Logging.LogB                    , Module.Logging.Logger                    , Module.Logging.TraceId     ghc-options: -fexpose-all-unfoldings@@ -79,6 +77,7 @@                  , template-haskell >= 2.20 && < 2.24                  , primitive < 0.10                  , clock < 0.9+                 , optparse-applicative < 0.20      -- Directories containing source files.     hs-source-dirs:   src@@ -94,3 +93,4 @@                       , LambdaCase                       , TypeFamilies                       , TypeApplications+                      , TypeAbstractions
src/Module/Logging.hs view
@@ -1,33 +1,59 @@-{-# LANGUAGE TemplateHaskell, UndecidableInstances, AllowAmbiguousTypes, DeriveLift, OverloadedRecordDot #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeAbstractions #-}+{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -Wno-orphans #-}--- | We want a logger that supports open categories, open levels, open severities etc.--- so that we can filter on different levels for different categories------ finally we will also provide an interface for MonadLogger for compatibility+ module Module.Logging-  ( -- * Definitions+  ( -- * Core Types     LogSeverity   , IsLogCat(..)   , LogCat(..)-  , Log(..), logType, logContent-  , LogMsg(..), logLoc, logSource, logMsg-  , Logger(..), runLogger+  , someSeverity+  , someLogCatName+  , someLogCatDisplay+    -- * Log Event Model+  , LogEvent(..)+  , logEventCats+  , logEventPayload+  , LogWithSourceMeta(..)+  , logMetaLoc+  , logMetaSource+  , logMetaDoc+  , Logger(..)+  , runLogger+  , LogEffect   , Logging   , LoggingModule-  -- * Default log categories+    -- * Structured Log Documents+  , LogDoc+  , SomeShown+  , NamedColor(..)+  , Color(..)+  , Style(..)+  , defaultStyle+  , StyleMode(..)+  , DocRenderOptions(..)+  , defaultDocRenderOptions+  , renderLogDoc+  , logRaw+  , logShow+  , logFg+  , logBg+  , logBold+  , ToLog(..)+    -- * Default Log Categories   , Debug(..)   , Info(..)   , Warn(..)   , Error(..)   , Other(..)-  -- * Default log implementation-  , toLogStrS-  , logLog-  , LogS-  , LogData-  -- * Combinators+    -- * Logger Combinators   , localLogger-  , localLog+  , localLogEvent   , addLogCat   , effAddLogCat   , effAddLogCat'@@ -39,402 +65,627 @@   , isLogCat   , isLogSubType   , isLogCatName-  , someSeverity-  , someLogCatName-  -- * Running and initializing-  , runLogging+  , liftLogger+    -- * Logging Operations+  , emitLogEvent+  , log_+  , logLoc_+  , logs+  , logTH+  , logLocIO+  , logIO+  , logsIO+  , logTHIO+    -- * Running and Initialization+  , runLogEffect   , withLiftLogger-  -- * Other optional utilities   , defaultStringToLogSeverity-  , defaultLoggingFromEnv-  , defaultLoggingFromArgs+    -- * Compatibility   , monadLoggerAdapter   , mlLogLevelToLogCat--  -- * Re-export+    -- * Re-exports   , ModuleRead(..)   , ModuleState(..)   , ModuleInitData(..)   , ModuleEvent(..)-  -- * Re-export contravariant functors   , module Data.Functor.Contravariant   ) where  import Control.Applicative import Control.Lens-import Control.System import Control.Monad import Control.Monad.Effect-import Control.Monad.Logger (Loc(..))+import Control.System import Data.Fixed import Data.Functor.Contravariant import Data.Kind+import Data.List (intercalate) import Data.Maybe+import Data.String (IsString(..)) import Data.Text (Text) import Data.Typeable-import qualified Control.Monad.Logger as ML--import System.Environment+import Data.Word import Text.Read (readMaybe)-+import qualified Control.Monad.Logger as ML+import qualified Language.Haskell.TH as TH import qualified Language.Haskell.TH.Syntax as TH --- | so user can interpolate between levels easily------ by default, Debug = 1, Info = 2, Warn = 3, Error = 4--- you can use anything in between, with a precision of 1 decimal place--- for example, 2.5 is between Info and Warn type LogSeverity = Fixed E1 --- | So every module can have its own logging category type, for example--- Database module can have `data Database` used as a log type------ and have a subtype--- @--- data DatabaseSubType = ConnectionPool | Query | Migration | Cursor deriving (Show, Eq)--- @------ you can then write instance------ @--- instance IsLogCat DatabaseSubType where---   severity _    = Nothing---   logTypeDisplay _ = "DB"--- @-class Typeable sub => IsLogCat (sub :: Type) where-  severity :: sub -> Maybe LogSeverity+class Typeable cat => IsLogCat (cat :: Type) where+  severity :: cat -> Maybe LogSeverity   severity _ = Nothing-  {-# INLINE severity #-}-  -- | This is used for display only-  logTypeDisplay :: sub -> ML.LogStr-  {-# MINIMAL logTypeDisplay #-} -instance IsLogCat Text where-  logTypeDisplay = ML.toLogStr+  logTypeName :: cat -> ML.LogStr+  {-# MINIMAL logTypeName #-}++  logTypeDisplay :: ML.LogStr -> LogDoc+  logTypeDisplay = DocRaw   {-# INLINE logTypeDisplay #-} --- | An exsitential type that wraps all logging categories, it is easy to define a new instance+instance IsLogCat Text where+  logTypeName = ML.toLogStr+ data LogCat where-  LogCat :: forall sub. IsLogCat sub => sub -> LogCat+  LogCat :: forall cat. IsLogCat cat => cat -> LogCat  someSeverity :: LogCat -> Maybe LogSeverity-someSeverity (LogCat @a subType) = severity @a subType-{-# INLINE someSeverity #-}+someSeverity (LogCat @cat x) = severity @cat x  someLogCatName :: LogCat -> ML.LogStr-someLogCatName (LogCat @a subType) = logTypeDisplay @a subType-{-# INLINE someLogCatName #-}+someLogCatName (LogCat @cat x) = logTypeName @cat x -data Log a = Log-  { _logType    :: [LogCat]-  , _logContent :: a-  } deriving (Functor)+someLogCatDisplay :: LogCat -> LogDoc+someLogCatDisplay (LogCat @cat x) = logTypeDisplay @cat (logTypeName @cat x) -type LogS = LogMsg ML.LogStr+-- | Carries several log categories and a payload.+data LogEvent a = LogEvent+  { _logEventCats    :: [LogCat]+  , _logEventPayload :: a+  }+  deriving (Functor) -type LogData = LogS-{-# DEPRECATED LogData "Use LogS instead" #-}+data LogWithSourceMeta a = LogWithSourceMeta+  { _logMetaLoc    :: Maybe ML.Loc+  , _logMetaSource :: Maybe ML.LogSource+  , _logMetaDoc    :: a+  }+  deriving (Functor) -data LogMsg a = LogMsg-  { _logLoc    :: Maybe ML.Loc-  , _logSource :: Maybe ML.LogSource-  , _logMsg    :: a+data SomeShown where+  SomeShown :: Show a => a -> SomeShown++data NamedColor+  = Black+  | Red+  | Green+  | Yellow+  | Blue+  | Magenta+  | Cyan+  | White+  deriving (Eq, Show)++data Color+  = DefaultColor+  | Named !NamedColor+  | RGB   !Word8 !Word8 !Word8+  deriving (Eq, Show)++class IsLogColor c where+  toLogColor :: c -> Color++instance IsLogColor Color where+  toLogColor = id++instance IsLogColor NamedColor where+  toLogColor = Named++instance IsLogColor (Word8, Word8, Word8) where+  toLogColor (r, g, b) = RGB r g b++class ToLog a where+  toLog :: a -> LogDoc++instance ToLog LogDoc where+  toLog = id++instance ToLog Text where+  toLog = DocRaw . ML.toLogStr++instance ToLog String where+  toLog = DocRaw . ML.toLogStr++data Style = Style+  { styleFg   :: Maybe Color+  , styleBg   :: Maybe Color+  , styleBold :: Bool   }+  deriving (Eq, Show) -makeLenses ''Log-makeLenses ''LogMsg-makeLenses ''Loc+defaultStyle :: Style+defaultStyle =+  Style+    { styleFg   = Nothing+    , styleBg   = Nothing+    , styleBold = False+    } -instance Functor LogMsg where-  fmap f logMsg' = logMsg' { _logMsg = f (_logMsg logMsg') }-  {-# INLINE fmap #-}+data LogDoc+  = DocEmpty+  | DocRaw    ML.LogStr+  | DocShown  SomeShown+  | DocStyled Style  LogDoc+  | DocAppend LogDoc LogDoc -instance Semigroup a => Semigroup (LogMsg a) where-  l1 <> l2 = LogMsg-    { _logLoc = l1 ^. logLoc <|> l2 ^. logLoc-    , _logSource = l1 ^. logSource <|> l2 ^. logSource-    , _logMsg = l1 ^. logMsg <> l2 ^. logMsg+data StyleMode+  = NoStyles+  | AnsiStyles+  deriving (Eq, Show)++data DocRenderOptions = DocRenderOptions+  { docRenderShow      :: forall a. Show a => a -> ML.LogStr+  , docRenderStyleMode :: StyleMode+  }++defaultDocRenderOptions :: DocRenderOptions+defaultDocRenderOptions =+  DocRenderOptions+    { docRenderShow      = ML.toLogStr . show+    , docRenderStyleMode = NoStyles     }-  {-# INLINE (<>) #-} -instance Monoid a => Monoid (LogMsg a) where-  mempty = LogMsg Nothing Nothing mempty-  {-# INLINE mempty #-}+makeLenses ''LogEvent+makeLenses ''LogWithSourceMeta --- | Some default log types, you can easily define your own-data    Debug = Debug  deriving TH.Lift-data    Info  = Info   deriving TH.Lift-data    Warn  = Warn   deriving TH.Lift-data    Error = Error  deriving TH.Lift-newtype Other = Other Text  deriving TH.Lift+instance Semigroup a => Semigroup (LogEvent a) where+  LogEvent catsA payloadA <> LogEvent catsB payloadB =+    LogEvent (catsA <> catsB) (payloadA <> payloadB) -instance IsLogCat Debug where severity _ = Just 1; logTypeDisplay _ = "DEBUG"-instance IsLogCat Info  where severity _ = Just 2; logTypeDisplay _ = "INFO"-instance IsLogCat Warn  where severity _ = Just 3; logTypeDisplay _ = "WARN"-instance IsLogCat Error where severity _ = Just 4; logTypeDisplay _ = "ERROR"-instance IsLogCat Other where-  severity _ = Just 2-  logTypeDisplay (Other t) = "OTHER:" <> ML.toLogStr t+instance Monoid a => Monoid (LogEvent a) where+  mempty = LogEvent [] mempty -instance Semigroup a => Semigroup (Log a) where-  Log t1 c1 <> Log t2 c2 = Log (t1 <> t2) (c1 <> c2)-  {-# INLINE (<>) #-}+instance Applicative LogEvent where+  pure = LogEvent []+  LogEvent catsF f <*> LogEvent catsA a = LogEvent (catsF <> catsA) (f a) -instance Monoid a => Monoid (Log a) where-  mempty = Log [] mempty-  {-# INLINE mempty #-}+instance Monad LogEvent where+  LogEvent catsA a >>= f =+    let LogEvent catsB b = f a+     in LogEvent (catsA <> catsB) b -instance Applicative Log where-  pure = Log []-  {-# INLINE pure #-}-  Log t1 f <*> Log t2 a = Log (t1 <> t2) (f a)-  {-# INLINE (<*>) #-}+instance Semigroup a => Semigroup (LogWithSourceMeta a) where+  metaA <> metaB =+    LogWithSourceMeta+      { _logMetaLoc    = _logMetaLoc    metaA <|> _logMetaLoc    metaB+      , _logMetaSource = _logMetaSource metaA <|> _logMetaSource metaB+      , _logMetaDoc    = _logMetaDoc    metaA <>  _logMetaDoc    metaB+      } -instance Monad Log where-  (Log t a) >>= f =-    let Log t' a' = f a-    in Log (t <> t') a'-  {-# INLINE (>>=) #-}+instance Monoid a => Monoid (LogWithSourceMeta a) where+  mempty = LogWithSourceMeta Nothing Nothing mempty +instance IsString LogDoc where+  fromString = DocRaw . ML.toLogStr++instance Semigroup LogDoc where+  (<>) = DocAppend++instance Monoid LogDoc where+  mempty = DocEmpty++data Debug    = Debug      deriving TH.Lift+data Info     = Info       deriving TH.Lift+data Warn     = Warn       deriving TH.Lift+data Error    = Error      deriving TH.Lift+newtype Other = Other Text deriving TH.Lift++instance IsLogCat Debug where+  severity _ = Just 1+  logTypeName _ = "DEBUG"+  logTypeDisplay = logFg Blue . DocRaw++instance IsLogCat Info where+  severity _ = Just 2+  logTypeName _ = "INFO"+  logTypeDisplay = logFg Green . DocRaw++instance IsLogCat Warn where+  severity _ = Just 3+  logTypeName _ = "WARN"+  logTypeDisplay = logFg Yellow . DocRaw++instance IsLogCat Error where+  severity _ = Just 4+  logTypeName _ = "ERROR"+  logTypeDisplay = logFg Red . DocRaw++instance IsLogCat Other where+  severity _ = Just 2+  logTypeName (Other t) = "OTHER:" <> ML.toLogStr t+ type Logger :: (Type -> Type) -> Type -> Type newtype Logger m a = Logger-  { _runLogger :: Log a -> m ()+  { _runLogger :: LogEvent a -> m ()   } -liftLogger :: (m () -> n ()) -> Logger m b -> Logger n b-liftLogger f (Logger g) = Logger (f . g)-{-# INLINE liftLogger #-}+runLogger :: Logger m a -> LogEvent a -> m ()+runLogger = _runLogger -makeLenses ''Logger+liftLogger :: (m () -> n ()) -> Logger m a -> Logger n a+liftLogger nat (Logger f) = Logger (nat . f)  instance Applicative m => Semigroup (Logger m a) where-  Logger f <> Logger g = Logger $ \log' -> f log' *> g log'-  {-# INLINE (<>) #-}+  Logger f <> Logger g = Logger $ \entry -> f entry *> g entry  instance Applicative m => Monoid (Logger m a) where   mempty = Logger $ const $ pure ()-  {-# INLINE mempty #-}  instance Contravariant (Logger m) where   contramap f (Logger g) = Logger (g . fmap f)-  {-# INLINE contramap #-} ------------------------------------------------------------------------------------ $ Some combinators for logger filtering------ You can use these combinators to filter logs based on their types and severities.--- Example:------ @--- localLogger---   ( anyLogCat (severityThat $ Predicate (>= 1))---   . excludeLogCat (isLogCat @Database)---   ) $ do---     ...--- @+logRaw :: ML.LogStr -> LogDoc+logRaw = DocRaw --- | Locally modify the logger-localLogger :: forall a c m mods es b. (Monad m, In' c (Logging m a) mods) => (Logger m a -> Logger m a) -> EffT' c mods es m b -> EffT' c mods es m b-localLogger f = localModule (\(LoggingRead logger) -> LoggingRead (f logger))-{-# INLINE localLogger #-}+logShow :: Show a => a -> LogDoc+logShow = DocShown . SomeShown --- | Locally modify the log-localLog :: forall a m mods es c. (Monad m, Logging m a `In` mods) => (Log a -> Log a) -> EffT mods es m c -> EffT mods es m c-localLog f = localLogger $ over runLogger (. f)-{-# INLINE localLog #-}+logFg :: IsLogColor color => color -> LogDoc -> LogDoc+logFg color = DocStyled defaultStyle {styleFg = Just $ toLogColor color} --- | Add a log category to the log--- @--- localLogger (addLogCat $ LogCat ConnectionPool) $ do---   ...--- @+logBg :: IsLogColor color => color -> LogDoc -> LogDoc+logBg color = DocStyled defaultStyle {styleBg = Just $ toLogColor color}++logBold :: LogDoc -> LogDoc+logBold = DocStyled defaultStyle {styleBold = True}++renderLogDoc :: DocRenderOptions -> LogDoc -> ML.LogStr+renderLogDoc opts = case docRenderStyleMode opts of+  NoStyles -> goPlain+  AnsiStyles -> goAnsi defaultStyle+  where+    goPlain DocEmpty                 = mempty+    goPlain (DocRaw str)             = str+    goPlain (DocShown (SomeShown x)) = docRenderShow opts x+    goPlain (DocStyled _ doc)        = goPlain doc+    goPlain (DocAppend a b)          = goPlain a <> goPlain b++    goAnsi _       DocEmpty                 = mempty+    goAnsi _       (DocRaw str)             = str+    goAnsi _       (DocShown (SomeShown x)) = docRenderShow opts x+    goAnsi current (DocAppend a b)          = goAnsi current a <> goAnsi current b+    goAnsi current (DocStyled style doc)    =+      let merged = mergeStyle current style+       in ansiForStyle merged <> goAnsi merged doc <> ansiForStyle current++mergeStyle :: Style -> Style -> Style+mergeStyle outer inner =+  Style+    { styleFg   = styleFg   inner <|> styleFg   outer+    , styleBg   = styleBg   inner <|> styleBg   outer+    , styleBold = styleBold outer ||  styleBold inner+    }++ansiForStyle :: Style -> ML.LogStr+ansiForStyle style =+  ML.toLogStr $ "\ESC[" <> mconcat (intercalate [";"] (pure <$> codes)) <> "m"+  where+    codes =+      let baseCodes =+            concat+              [ maybe [] colorToFgCodes (styleFg style)+              , maybe [] colorToBgCodes (styleBg style)+              , ["1" | styleBold style]+              ]+       in if null baseCodes then ["0"] else baseCodes++colorToFgCodes :: Color -> [ML.LogStr]+colorToFgCodes DefaultColor  = ["39"]+colorToFgCodes (Named color) = [lshow $ namedColorCode color]+colorToFgCodes (RGB r g b)   = ["38", "2", lshow r, lshow g, lshow b]++colorToBgCodes :: Color -> [ML.LogStr]+colorToBgCodes DefaultColor  = ["49"]+colorToBgCodes (Named color) = [lshow $ namedColorCode color + 10]+colorToBgCodes (RGB r g b)   = ["48", "2", lshow r, lshow g, lshow b]++lshow :: Show a => a -> ML.LogStr+lshow = ML.toLogStr . show+{-# INLINE lshow #-}++namedColorCode :: NamedColor -> Int+namedColorCode = \case+  Black   -> 30+  Red     -> 31+  Green   -> 32+  Yellow  -> 33+  Blue    -> 34+  Magenta -> 35+  Cyan    -> 36+  White   -> 37++localLogger+  :: forall a c m mods es b.+     (Monad m, In' c (LogEffect m a) mods)+  => (Logger m (LogWithSourceMeta a) -> Logger m (LogWithSourceMeta a))+  -> EffT' c mods es m b+  -> EffT' c mods es m b+localLogger f =+  localModule (\(LogEffectRead logger) -> LogEffectRead (f logger))++localLogEvent+  :: forall a m mods es c.+     (Monad m, LogEffect m a `In` mods)+  => (LogEvent (LogWithSourceMeta a) -> LogEvent (LogWithSourceMeta a))+  -> EffT mods es m c+  -> EffT mods es m c+localLogEvent f = localLogger $ \(Logger g) -> Logger (g . f)+ addLogCat :: LogCat -> Logger m a -> Logger m a-addLogCat t = over runLogger (. over logType (t:))-{-# INLINE addLogCat #-}+addLogCat cat (Logger g) =+  Logger $ \entry -> g entry {_logEventCats = cat : _logEventCats entry} --- | Add a log category to the log in EffT--- @--- effAddLogCat @LogS (LogCat ConnectionPool) $ do---   ...--- @-effAddLogCat :: forall a c mods es m b. (Monad m, In' c (Logging m a) mods) => LogCat -> EffT' c mods es m b -> EffT' c mods es m b-effAddLogCat logCat = localLogger @a (addLogCat logCat)-{-# INLINE effAddLogCat #-}+effAddLogCat+  :: forall a c mods es m b.+     (Monad m, In' c (LogEffect m a) mods)+  => LogCat+  -> EffT' c mods es m b+  -> EffT' c mods es m b+effAddLogCat cat = localLogger @a (addLogCat cat) --- | Add a log category to the log in EffT (defaulting to In (Logging m LogS) mods)--- @--- effAddLogCat' (LogCat ConnectionPool) $ do---   ...--- @-effAddLogCat' :: forall c mods es m b. (Monad m, In' c (Logging m LogS) mods) => LogCat -> EffT' c mods es m b -> EffT' c mods es m b-effAddLogCat' logCat = localLogger @LogS (addLogCat logCat)-{-# INLINE effAddLogCat' #-}+effAddLogCat'+  :: forall c mods es m b.+     (Monad m, In' c (LogEffect m LogDoc) mods)+  => LogCat+  -> EffT' c mods es m b+  -> EffT' c mods es m b+effAddLogCat' = effAddLogCat @LogDoc  filterLogCats :: Applicative m => Predicate [LogCat] -> Logger m a -> Logger m a-filterLogCats p (Logger logger) = Logger $ \log' -> when (p.getPredicate $ log' ^. logType) $ logger log'-{-# INLINE filterLogCats #-}+filterLogCats p (Logger logger) =+  Logger $ \entry ->+    when (getPredicate p $ _logEventCats entry) $+      logger entry  anyLogCat :: Applicative m => Predicate LogCat -> Logger m a -> Logger m a-anyLogCat p = filterLogCats (Predicate $ any p.getPredicate)-{-# INLINE anyLogCat #-}+anyLogCat p = filterLogCats (Predicate $ any (getPredicate p))  excludeLogCat :: Applicative m => Predicate LogCat -> Logger m a -> Logger m a-excludeLogCat p = filterLogCats (Predicate $ not . any p.getPredicate)-{-# INLINE excludeLogCat #-}+excludeLogCat p = filterLogCats (Predicate $ not . any (getPredicate p))  severityThat :: Predicate LogSeverity -> Predicate LogCat severityThat = contramap (fromMaybe 0 . someSeverity)-{-# INLINE severityThat #-}  noSeverity :: Predicate LogCat noSeverity = Predicate (isNothing . someSeverity)-{-# INLINE noSeverity #-} --- | use type applications to check if a log type is present-isLogCat :: forall sub. IsLogCat sub => Predicate LogCat-isLogCat = Predicate $ \(LogCat (_ :: sub')) -> case eqT @sub @sub' of-  Just Refl -> True-  Nothing   -> False-{-# INLINE isLogCat #-}+-- | Use with type applications+isLogCat :: forall cat. IsLogCat cat => Predicate LogCat+isLogCat =+  Predicate $ \(LogCat (_ :: cat')) -> case eqT @cat @cat' of+    Just Refl -> True+    Nothing -> False -isLogSubType :: forall sub. IsLogCat sub => Predicate sub -> Predicate LogCat-isLogSubType p = Predicate $ \(LogCat (subType :: sub')) -> case eqT @sub @sub' of-  Just Refl -> p.getPredicate subType-  Nothing   -> False-{-# INLINE isLogSubType #-}+isLogSubType :: forall cat. IsLogCat cat => Predicate cat -> Predicate LogCat+isLogSubType p =+  Predicate $ \(LogCat (x :: cat')) -> case eqT @cat @cat' of+    Just Refl -> getPredicate p x+    Nothing -> False  isLogCatName :: ML.ToLogStr n => n -> Predicate LogCat-isLogCatName name = Predicate $ \logCat -> someLogCatName logCat == ML.toLogStr name-{-# INLINE isLogCatName #-}+isLogCatName name =+  Predicate $ \logCat -> someLogCatName logCat == ML.toLogStr name --- | The Logging m module type, a module `Logging m a` provides logging capabilities for logs of type `a`-type Logging :: (Type -> Type) -> Type -> Type-data Logging m a+type LogEffect :: (Type -> Type) -> Type -> Type+data LogEffect m a -type LoggingModule = Logging IO LogS -- standard logging module+type Logging = LogEffect +type LoggingModule = LogEffect IO LogDoc+ withLiftLogger   :: forall m n c a mods es b.-  ( Monad m-  , In' c (Logging m a) (Logging m a : mods)-  , ConsFDataList c (Logging n a : mods)-  , ConsFDataList c (Logging m a : mods)-  )-  => (forall x. m x -> n x) -> EffT' c (Logging n a : mods) es m b -> EffT' c (Logging m a : mods) es m b+     ( Monad m+     , In' c (LogEffect m a) (LogEffect m a : mods)+     , ConsFDataList c (LogEffect n a : mods)+     , ConsFDataList c (LogEffect m a : mods)+     )+  => (forall x. m x -> n x)+  -> EffT' c (LogEffect n a : mods) es m b+  -> EffT' c (LogEffect m a : mods) es m b withLiftLogger lifter act = do-  LoggingRead logger <- askModule @(Logging m a)-  let logger' = liftLogger lifter logger-  embedMods $ runEffTOuter_ (LoggingRead logger') LoggingState act+  LogEffectRead logger <- askModule @(LogEffect m a)+  embedMods $ runEffTOuter_ (LogEffectRead $ liftLogger lifter logger) LogEffectState act -instance Module (Logging m (a :: Type)) where-  newtype ModuleRead  (Logging m a) = LoggingRead-    { logging :: Logger m a+instance Module (LogEffect m a) where+  newtype ModuleRead (LogEffect m a) = LogEffectRead+    { logging :: Logger m (LogWithSourceMeta a)     }-  data    ModuleState (Logging m a) = LoggingState+  data ModuleState (LogEffect m a) = LogEffectState -runLogging-  :: (ConsFDataList c (Logging m logS : mods), Monad m) => Logger m logS-  -> EffT' c (Logging m logS : mods) es m a-  -> EffT' c mods es m a-runLogging logger = runEffTOuter_ (LoggingRead logger) LoggingState-{-# INLINE runLogging #-}+runLogEffect+  :: (ConsFDataList c (LogEffect m a : mods), Monad m)+  => Logger m (LogWithSourceMeta a)+  -> EffT' c (LogEffect m a : mods) es m b+  -> EffT' c mods es m b+runLogEffect logger = runEffTOuter_ (LogEffectRead logger) LogEffectState -instance SystemModule (Logging m a) where-  data    ModuleInitData (Logging m a) = LoggingInitData-    { loggerInitLogger   :: Logger IO a-    , loggerInitSeverity :: Maybe LogSeverity-    , loggerInitCleanup  :: Maybe (IO ())+instance SystemModule (LogEffect m a) where+  data ModuleInitData (LogEffect m a) = LogEffectInitData+    { loggerInitBase       :: Logger m (LogWithSourceMeta a)+    , loggerInitCleanup    :: Maybe (m ())+    , loggerInitTransform  :: Logger m (LogWithSourceMeta a) -> Logger m (LogWithSourceMeta a)+    , loggerInitSeverity   :: Maybe LogSeverity     }-  data    ModuleEvent    (Logging m a) = LoggingEvent+  data ModuleEvent (LogEffect m a) = LogEffectEvent -instance Loadable c (Logging IO a) mods ies where-  withModule (LoggingInitData logger mSev Nothing) act = case mSev of-    Nothing -> runEffTOuter_ (LoggingRead logger) LoggingState act-    Just s -> runEffTOuter_ (LoggingRead $ anyLogCat (severityThat $ Predicate (>= s)) logger) LoggingState act-  withModule (LoggingInitData logger mSev (Just clean)) act = bracketEffT (return ()) (\_ -> liftIO clean) (\_ -> case mSev of-      Nothing -> runEffTOuter_ (LoggingRead logger) LoggingState act-      Just s -> runEffTOuter_ (LoggingRead $ anyLogCat (severityThat $ Predicate (>= s)) logger) LoggingState act-    )-  {-# INLINE withModule #-}+instance Loadable c (LogEffect IO a) mods ies where+  withModule initData act =+    let transformedLogger = loggerInitTransform initData (loggerInitBase initData)+        baseLogger = case loggerInitSeverity initData of+          Nothing -> transformedLogger+          Just sev ->+            anyLogCat (severityThat $ Predicate (>= sev)) transformedLogger+        runAction = runEffTOuter_ (LogEffectRead baseLogger) LogEffectState act+     in case loggerInitCleanup initData of+          Nothing -> runAction+          Just cleanup -> bracketEffT (pure ()) (\_ -> liftIO cleanup) (const runAction) -instance EventLoop c (Logging m a) mods es+instance EventLoop c (LogEffect m a) mods es --- | Maps 'Debug', 'Info', 'Warn', 'Error' to 1, 2, 3, 4 respectively--- and also accepts numbers between 0 and 10 with a precision of 1 decimal place+emitLogEvent+  :: forall a c mods es m.+     (Monad m, In' c (LogEffect m a) mods)+  => LogEvent (LogWithSourceMeta a)+  -> EffT' c mods es m ()+emitLogEvent entry = do+  action <- asksModule @(LogEffect m a) (runLogger . logging)+  lift $ action entry++logLoc_+  :: forall c mods es m cat.+     (Monad m, In' c (LogEffect m LogDoc) mods, IsLogCat cat)+  => ML.Loc+  -> cat+  -> LogDoc+  -> EffT' c mods es m ()+logLoc_ loc cat doc =+  emitLogEvent $+    LogEvent+      { _logEventCats = [LogCat cat]+      , _logEventPayload =+          LogWithSourceMeta+            { _logMetaLoc    = Just loc+            , _logMetaSource = Nothing+            , _logMetaDoc    = doc+            }+      }++log_+  :: forall c mods es m cat.+     (Monad m, In' c (LogEffect m LogDoc) mods, IsLogCat cat)+  => cat+  -> LogDoc+  -> EffT' c mods es m ()+log_ cat doc =+  emitLogEvent $+    LogEvent+      { _logEventCats = [LogCat cat]+      , _logEventPayload =+          LogWithSourceMeta+            { _logMetaLoc    = Nothing+            , _logMetaSource = Nothing+            , _logMetaDoc    = doc+            }+      }++logs+  :: forall c mods es m.+     (Monad m, In' c (LogEffect m LogDoc) mods)+  => [LogCat]+  -> LogDoc+  -> EffT' c mods es m ()+logs cats doc =+  emitLogEvent $+    LogEvent+      { _logEventCats = cats+      , _logEventPayload =+          LogWithSourceMeta+            { _logMetaLoc    = Nothing+            , _logMetaSource = Nothing+            , _logMetaDoc    = doc+            }+      }++logTH :: (IsLogCat cat, TH.Lift cat) => cat -> TH.Q TH.Exp+logTH cat = [| logLoc_ $(TH.qLocation >>= TH.lift) $(TH.lift cat) |]++logLocIO+  :: forall c mods es m cat.+     (MonadIO m, In' c (LogEffect IO LogDoc) mods, IsLogCat cat)+  => ML.Loc+  -> cat+  -> LogDoc+  -> EffT' c mods es m ()+logLocIO loc cat = baseTransform liftIO . logLoc_ loc cat++logIO+  :: forall c mods es m cat.+     (MonadIO m, In' c (LogEffect IO LogDoc) mods, IsLogCat cat)+  => cat+  -> LogDoc+  -> EffT' c mods es m ()+logIO cat = baseTransform liftIO . log_ cat++logsIO+  :: forall c mods es m.+     (MonadIO m, In' c (LogEffect IO LogDoc) mods)+  => [LogCat]+  -> LogDoc+  -> EffT' c mods es m ()+logsIO cats = baseTransform liftIO . logs cats++logTHIO :: (IsLogCat cat, TH.Lift cat) => cat -> TH.Q TH.Exp+logTHIO cat = [| baseTransform liftIO . logLoc_ @LogDoc $(TH.qLocation >>= TH.lift) $(TH.lift cat) |]+ defaultStringToLogSeverity :: String -> Either Text LogSeverity defaultStringToLogSeverity = \case   "Debug" -> Right 1   "Info"  -> Right 2   "Warn"  -> Right 3   "Error" -> Right 4-  other   -> maybe (Left "Invalid LogLevel, must be one of 'Debug', 'Info', 'Warn', 'Error', or a number between 0 and 10 with a precision of 1 decimal place") Right-    $ readMaybe other-{-# INLINABLE defaultStringToLogSeverity #-}---- | Load a log level from environment variable LOG_LEVEL,-defaultLoggingFromEnv :: Logger IO LogS -> Maybe (IO ()) -> IO (ModuleInitData LoggingModule)-defaultLoggingFromEnv logger mcl = do-  mLogLevel <- liftIO $ (readMaybe =<<) <$> lookupEnv "LOG_LEVEL"-  return $ LoggingInitData logger mLogLevel mcl-{-# INLINABLE defaultLoggingFromEnv #-}---- | Load an argument --log-level <level> from command line arguments,--- if none is provided, it will log everything (Maybe LogSeverity = Nothing)-defaultLoggingFromArgs :: Logger IO LogS -> Maybe (IO ()) -> [String] -> Either Text (ModuleInitData LoggingModule)-defaultLoggingFromArgs logger mcl []         = Right $ LoggingInitData logger Nothing mcl-defaultLoggingFromArgs logger mcl args@(_:_) = do-  level    <- maybe (Right Nothing) (fmap Just) $ detectFlag "--log-level" defaultStringToLogSeverity args-  types    <- sequence $ detectAllFlags "--log-type"    (\case "" -> Left "Empty log type"; s -> Right s) args-  nonTypes <- sequence $ detectAllFlags "--no-log-type" (\case "" -> Left "Empty log type"; s -> Right s) args-  let logger' = foldr ($) logger (  [ anyLogCat     (isLogCatName name) | name <- types ]-                                 <> [ excludeLogCat (isLogCatName name) | name <- nonTypes ]-                                 )-  return $ LoggingInitData logger' level mcl-{-# INLINABLE defaultLoggingFromArgs #-}+  other ->+    maybe+      (Left "Invalid LogLevel, must be one of 'Debug', 'Info', 'Warn', 'Error', or a number between 0 and 10 with a precision of 1 decimal place")+      Right+      (readMaybe other) -monadLoggerAdapter :: Logger m LogS -> ML.Loc -> ML.LogSource -> ML.LogLevel -> ML.LogStr -> m ()-monadLoggerAdapter logger loc src lev msg = _runLogger logger Log-  { _logType = [mlLogLevelToLogCat lev]-  , _logContent = LogMsg-      { _logLoc    = Just loc-      , _logSource = Just src-      , _logMsg    = msg+monadLoggerAdapter+  :: Logger m (LogWithSourceMeta LogDoc)+  -> ML.Loc+  -> ML.LogSource+  -> ML.LogLevel+  -> ML.LogStr+  -> m ()+monadLoggerAdapter logger loc src lev msg =+  runLogger logger $+    LogEvent+      { _logEventCats = [mlLogLevelToLogCat lev]+      , _logEventPayload =+          LogWithSourceMeta+            { _logMetaLoc    = Just loc+            , _logMetaSource = Just src+            , _logMetaDoc    = logRaw msg+            }       }-  }-{-# INLINE monadLoggerAdapter #-} --- | This provides an interface to MonadLogger-instance (Monad m, In' c (Logging m LogS) mods) => ML.MonadLogger (EffT' c mods es m) where-  monadLoggerLog loc logsource loglevel msg = do-    LoggingRead logger <- queryModule @(Logging m LogS)-    lift $ monadLoggerAdapter logger loc logsource loglevel (ML.toLogStr msg)-  {-# INLINE monadLoggerLog #-}+instance (Monad m, In' c (LogEffect m LogDoc) mods) => ML.MonadLogger (EffT' c mods es m) where+  monadLoggerLog loc src lev msg = do+    LogEffectRead logger <- queryModule @(LogEffect m LogDoc)+    lift $ monadLoggerAdapter logger loc src lev (ML.toLogStr msg) -instance (m ~ IO, In' c (Logging m LogS) mods) => ML.MonadLoggerIO (EffT' c mods es m) where-  askLoggerIO = queriesModule @(Logging m LogS) $ (\f loc src lev str -> f-    $ Log [mlLogLevelToLogCat lev]-    $ LogMsg-      { _logLoc    = Just loc-      , _logSource = Just src-      , _logMsg    = str-      }-    ) . _runLogger . logging-  {-# INLINE askLoggerIO #-}+instance (m ~ IO, In' c (LogEffect m LogDoc) mods) => ML.MonadLoggerIO (EffT' c mods es m) where+  askLoggerIO =+    queriesModule @(LogEffect m LogDoc) $+      (\f loc src lev str ->+         f $+           LogEvent+             { _logEventCats = [mlLogLevelToLogCat lev]+             , _logEventPayload =+                 LogWithSourceMeta+                   { _logMetaLoc    = Just loc+                   , _logMetaSource = Just src+                   , _logMetaDoc    = logRaw str+                   }+             }+      )+        . runLogger+        . logging --- | A compatibility function that works for the old MonadLogger instances mlLogLevelToLogCat :: ML.LogLevel -> LogCat-mlLogLevelToLogCat ML.LevelDebug     = LogCat Debug-mlLogLevelToLogCat ML.LevelInfo      = LogCat Info-mlLogLevelToLogCat ML.LevelWarn      = LogCat Warn-mlLogLevelToLogCat ML.LevelError     = LogCat Error-mlLogLevelToLogCat (ML.LevelOther t) = LogCat (Other t)-{-# INLINE mlLogLevelToLogCat #-}--logLog :: forall a c mods es m. (Monad m, In' c (Logging m a) mods) => Log a -> EffT' c mods es m ()-logLog logd = asksModule @(Logging m a) (_runLogger . logging) >>= (\action -> lift $ action logd)-{-# INLINABLE logLog #-}---- | Apply 'show' to convert a value to LogStr-toLogStrS :: Show a => a -> ML.LogStr-toLogStrS = ML.toLogStr . show-{-# INLINE toLogStrS #-}+mlLogLevelToLogCat = \case+  ML.LevelDebug   -> LogCat Debug+  ML.LevelInfo    -> LogCat Info+  ML.LevelWarn    -> LogCat Warn+  ML.LevelError   -> LogCat Error+  ML.LevelOther t -> LogCat (Other t)
− src/Module/Logging/LogB.hs
@@ -1,114 +0,0 @@--- | Module      : Module.Logging.LogVal---   Description : Log value builders and renderers. This module provides a way to build log messages separately from rendering them, allowing for flexible logging rendering strategies (e.g. with console colors, JSON formatting, etc.).-{-# LANGUAGE TemplateHaskell #-}-module Module.Logging.LogB-  ( -- * Log value builders and renderers-    LogVals-  , LogBuilder-  , LogRenderer-  , toLog-  , logShow-  , renderUsing--  -- * Logging module types-  , LogB-  , LoggingModuleB-  -- * General logging utilities-  , log_-  , logLoc_-  , logs-  , logTH-  -- * MonadIO specific versions-  , logLocIO-  , logIO-  , logsIO-  , logTHIO-  ) where--import Data.String (IsString(..))-import qualified Control.Monad.Logger as ML-import qualified Language.Haskell.TH as TH-import qualified Language.Haskell.TH.Syntax as TH-import Module.Logging-import Control.Monad.Effect-import Control.Lens---- | Note on LogStr from monad-logger:---   It is just Data.ByteString.Builder.Builder, but with extra length information for buffering.-data LogVal where-  LogValStr :: ML.LogStr   -> LogVal-  LogShow   :: Show a => a -> LogVal--type LogVals = [LogVal]--type LogBuilder   = LogVals    -> LogVals-type LogRenderer  = LogBuilder -> ML.LogStr--instance IsString LogVal where-  fromString = LogValStr . fromString-  {-# INLINE fromString #-}--instance IsString LogBuilder where-  fromString s = (LogValStr (fromString s) :)-  {-# INLINE fromString #-}-instance {-# OVERLAPPING #-} Semigroup LogBuilder where-  (<>) = (.)-  {-# INLINE (<>) #-}-instance {-# OVERLAPPING #-} Monoid LogBuilder where-  mempty = id-  {-# INLINE mempty #-}---- | Use this function together with OverloadedStrings and <> to log your messages-logShow :: Show a => a -> LogBuilder-logShow v = (LogShow v :)-{-# INLINE logShow #-}--renderUsing :: (forall a. Show a => a -> ML.LogStr) -> LogRenderer-renderUsing func vals = mconcat @ML.LogStr $ map renderVal (vals [])-  where renderVal (LogValStr ls) = ls-        renderVal (LogShow   v ) = func v--type LogB = LogMsg LogBuilder--type LoggingModuleB = Logging IO LogB--toLog :: (ML.ToLogStr a) => a -> LogBuilder-toLog = (:) . LogValStr . ML.toLogStr-{-# INLINE toLog #-}--logLoc_ :: (Monad m, In' c (Logging m LogB) mods, IsLogCat subType) => ML.Loc -> subType -> LogBuilder -> EffT' c mods es m ()-logLoc_ src subTypeType msg = logLog (Log [LogCat subTypeType] (mempty @LogB & logMsg .~ msg & logLoc ?~ src))-{-# INLINE logLoc_ #-}---- | Simple logging function, provide one log type and a LogStr message-log_ :: (Monad m, In' c (Logging m LogB) mods, IsLogCat subType) => subType -> LogBuilder -> EffT' c mods es m ()-log_ subTypeType msg = logLog (Log [LogCat subTypeType] (mempty @LogB & logMsg .~ msg))-{-# INLINE log_ #-}---- | Log with multiple log types (wrapped in existantial constructor LogCat)-logs :: (Monad m, In' c (Logging m LogB) mods) => [LogCat] -> LogBuilder -> EffT' c mods es m ()-logs logTypes msg = logLog (Log logTypes (mempty @LogB & logMsg .~ msg))-{-# INLINE logs #-}---- | Template Haskell helper with location info-logTH :: (IsLogCat subType, TH.Lift subType) => subType -> TH.Q TH.Exp-logTH subType = [| logLoc_ $(TH.qLocation >>= TH.lift) $(TH.lift subType) |]------------ Monad IO specific versions ------------ -- | Useful when you are in a MonadIO but with a Logging IO _ type module-logLocIO :: (MonadIO m, In' c (Logging IO LogB) mods, IsLogCat subType) => ML.Loc -> subType -> LogBuilder -> EffT' c mods es m ()-logLocIO src subTypeType = baseTransform liftIO . logLoc_ src subTypeType-{-# INLINE logLocIO #-}--logIO :: (MonadIO m, In' c (Logging IO LogB) mods, IsLogCat subType) => subType -> LogBuilder -> EffT' c mods es m ()-logIO subTypeType = baseTransform liftIO . log_ subTypeType-{-# INLINE logIO #-}--logsIO :: (MonadIO m, In' c (Logging IO LogB) mods) => [LogCat] -> LogBuilder -> EffT' c mods es m ()-logsIO logTypes = baseTransform liftIO . logs logTypes-{-# INLINE logsIO #-}---- | Template Haskell helper with location info, with m=IO-logTHIO :: (IsLogCat subType, TH.Lift subType) => subType -> TH.Q TH.Exp-logTHIO subType = [| baseTransform liftIO . logLoc_ $(TH.qLocation >>= TH.lift) $(TH.lift subType) |]
− src/Module/Logging/LogS.hs
@@ -1,78 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-module Module.Logging.LogS-  (-  -- * Log value builders and renderers-    toLog-  , logShow-  -- * Logging module types-  , LogS-  , LoggingModule-  -- * General logging utilities-  , log_-  , logLoc_-  , logs-  , logTH-  , logS-  -- * MonadIO specific versions-  , logLocIO-  , logIO-  , logsIO-  , logTHIO-  -- * Re-export-  , ML.LogStr-  ) where--import qualified Control.Monad.Logger as ML-import qualified Language.Haskell.TH as TH-import qualified Language.Haskell.TH.Syntax as TH-import Module.Logging-import Control.Monad.Effect-import Control.Lens--toLog :: (ML.ToLogStr a) => a -> ML.LogStr-toLog = ML.toLogStr-{-# INLINE toLog #-}--logShow :: Show a => a -> ML.LogStr-logShow = ML.toLogStr . show-{-# INLINE logShow #-}--logS :: (Monad m, In' c (Logging m LogS) mods) => LogS -> EffT' c mods es m ()-logS logd = logLog (Log [] logd)-{-# INLINE logS #-}--logLoc_ :: (Monad m, In' c (Logging m LogS) mods, IsLogCat subType) => ML.Loc -> subType -> ML.LogStr -> EffT' c mods es m ()-logLoc_ src subTypeType msg = logLog (Log [LogCat subTypeType] (mempty @LogS & logMsg .~ msg & logLoc ?~ src))-{-# INLINE logLoc_ #-}---- | Simple logging function, provide one log type and a LogStr message-log_ :: (Monad m, In' c (Logging m LogS) mods, IsLogCat subType) => subType -> ML.LogStr -> EffT' c mods es m ()-log_ subTypeType msg = logLog (Log [LogCat subTypeType] (mempty @LogS & logMsg .~ msg))-{-# INLINE log_ #-}---- | Log with multiple log types (wrapped in existantial constructor LogCat)-logs :: (Monad m, In' c (Logging m LogS) mods) => [LogCat] -> ML.LogStr -> EffT' c mods es m ()-logs logTypes msg = logLog (Log logTypes (mempty @LogS & logMsg .~ msg))-{-# INLINE logs #-}---- | Template Haskell helper with location info-logTH :: (IsLogCat subType, TH.Lift subType) => subType -> TH.Q TH.Exp-logTH subType = [| logLoc_ $(TH.qLocation >>= TH.lift) $(TH.lift subType) |]------------ Monad IO specific versions ------------logLocIO :: (MonadIO m, In' c (Logging IO LogS) mods, IsLogCat subType) => ML.Loc -> subType -> ML.LogStr -> EffT' c mods es m ()-logLocIO src subTypeType = baseTransform liftIO . logLoc_ src subTypeType-{-# INLINE logLocIO #-}--logIO :: (MonadIO m, In' c (Logging IO LogS) mods, IsLogCat subType) => subType -> ML.LogStr -> EffT' c mods es m ()-logIO subTypeType = baseTransform liftIO . log_ subTypeType-{-# INLINE logIO #-}--logsIO :: (MonadIO m, In' c (Logging IO LogS) mods) => [LogCat] -> ML.LogStr -> EffT' c mods es m ()-logsIO logTypes = baseTransform liftIO . logs logTypes-{-# INLINE logsIO #-}---- | Template Haskell helper with location info, with MonadIO-logTHIO :: (IsLogCat subType, TH.Lift subType) => subType -> TH.Q TH.Exp-logTHIO subType = [| baseTransform liftIO . logLoc_ @IO $(TH.qLocation >>= TH.lift) $(TH.lift subType) |]
src/Module/Logging/Logger.hs view
@@ -1,200 +1,439 @@-{-# LANGUAGE RecordWildCards, DuplicateRecordFields #-}--- | Some simple combinators to build your logger+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ApplicativeDo         #-}+{-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE OverloadedRecordDot   #-}+ module Module.Logging.Logger-  ( module Module.Logging.Logger-  -- * Re-exporting fast-logger+  ( -- * Logger Lifecycle+    LoggerWithCleanup(..)+  , liftBaseLogger+    -- * Logger Options+  , LoggerOptions(..)+  , LogOrderControl(..)+  , LoggerStyle+  , defaultLoggerStyle+  , buildLoggerStyle+  , loggerUseAnsi+  , loggerNoStyle+  , loggerShowWith+  , loggerNoTime+  , loggerNoCats+  , loggerNoLoc+  , loggerNoSource+  , loggerNoNewline+  , loggerJson+  , loggerOrder+    -- * Base Loggers+  , createFastBaseLogger+  , createStdoutBaseLogger+  , createSimpleStdoutBaseLogger+  , createSimpleConcurrentStdoutBaseLogger+  , createStderrBaseLogger+  , createFileLogger+  , createFileLoggerWith+    -- * Rendering and Composition+  , renderLogEvent+  , loggerFromRenderer+  , makeLoggerFromBase+  , withLoggerCleanup+  , withBaseLogger+  , withBaseLoggerIO+    -- * Helpers+  , defaultLoggingFromEnv+  , defaultLoggingFromArgs+  , defaultLoggingOptParser+    -- * Re-exporting fast-logger   , module System.Log.FastLogger   ) where -import Control.Monad import Control.Concurrent import Control.Concurrent.STM+import Control.Exception (bracket)+import Control.Lens ((^.))+import Control.Monad import Control.Monad.Effect+import Control.System (detectFlag, detectAllFlags)+import Data.Text (Text) import Data.Time.Clock import Module.Logging+import System.Environment (lookupEnv) import System.Log.FastLogger-import System.Log.FastLogger.Internal (LogStr (..))+import System.Log.FastLogger.Internal (LogStr(..))+import Text.Read (readMaybe) import qualified Control.Monad.Logger as ML import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as BL-import Data.List (foldl1')-import Control.Exception (bracket)+import qualified Options.Applicative as O+import Data.Maybe+import Data.List (intersperse)+import Control.Arrow+import Data.Word+import Data.ByteString (ByteString)+import qualified Data.ByteString as B --- | Logger with a cleanup function-data LoggerWithCleanup m log = LoggerWithCleanup-  { baseLogFunc :: log -> m ()+data LoggerWithCleanup m a = LoggerWithCleanup+  { baseLogFunc :: a -> m ()   , cleanUpFunc :: m ()   } -useBaseLogger :: ((LogStr -> m ()) -> Logger m logS) -> LoggerWithCleanup m LogStr -> LoggerWithCleanup m (Log logS)-useBaseLogger makeLogger (LoggerWithCleanup logFunc cleanUp) =-  LoggerWithCleanup (_runLogger $ makeLogger logFunc) cleanUp-{-# INLINE useBaseLogger #-}+data LogOrderControl+  = LogTimeChunk+  | LogCatChunk+  | LogLocChunk+  | LogSrcChunk+  | LogDocChunk+  deriving (Eq, Ord, Show) --- | Primitive version of 'useBaseLogger'-useBaseLogger' :: Monad m => (Log logS -> m LogStr) -> LoggerWithCleanup m LogStr -> LoggerWithCleanup m (Log logS)-useBaseLogger' makeLogger (LoggerWithCleanup logFunc cleanUp) =-  LoggerWithCleanup (makeLogger >=> logFunc) cleanUp-{-# INLINE useBaseLogger' #-}+data LoggerOptions = LoggerOptions+  { loggerDocRenderOptions :: DocRenderOptions+  , loggerIncludeTime      :: Bool+  , loggerIncludeCats      :: Bool+  , loggerIncludeLoc       :: Bool+  , loggerIncludeSource    :: Bool+  , loggerAppendNewline    :: Bool+  , loggerJsonFormat       :: Bool+  , loggerOrderControl     :: Maybe [LogOrderControl]+  } -liftBaseLogger :: (m () -> n ()) -> LoggerWithCleanup m log -> LoggerWithCleanup n log-liftBaseLogger nat (LoggerWithCleanup f c) = LoggerWithCleanup (nat . f) (nat c)-{-# INLINE liftBaseLogger #-}+type LoggerStyle = LoggerOptions -> LoggerOptions -instance Applicative m => Semigroup (LoggerWithCleanup m log) where-  (LoggerWithCleanup l1 c1) <> (LoggerWithCleanup l2 c2) = LoggerWithCleanup (\l -> l1 l *> l2 l) (c1 *> c2)-  {-# INLINE (<>) #-}-instance Applicative m => Monoid (LoggerWithCleanup m log) where+defaultLoggerStyle :: LoggerOptions+defaultLoggerStyle =+  LoggerOptions+    { loggerDocRenderOptions = defaultDocRenderOptions+    , loggerIncludeTime      = True+    , loggerIncludeCats      = True+    , loggerIncludeLoc       = True+    , loggerIncludeSource    = True+    , loggerAppendNewline    = True+    , loggerJsonFormat       = False+    , loggerOrderControl     = Nothing+    }++buildLoggerStyle :: LoggerStyle -> LoggerOptions+buildLoggerStyle style = style defaultLoggerStyle++loggerUseAnsi :: LoggerStyle+loggerUseAnsi opts =+  opts+    { loggerDocRenderOptions =+        (loggerDocRenderOptions opts) { docRenderStyleMode = AnsiStyles }+    }++loggerNoStyle :: LoggerStyle+loggerNoStyle opts =+  opts+    { loggerDocRenderOptions =+        (loggerDocRenderOptions opts) { docRenderStyleMode = NoStyles }+    }++loggerShowWith :: (forall a. Show a => a -> ML.LogStr) -> LoggerStyle+loggerShowWith renderShown opts =+  opts+    { loggerDocRenderOptions =+        (loggerDocRenderOptions opts) { docRenderShow = renderShown }+    }++loggerNoTime :: LoggerStyle+loggerNoTime opts = opts {loggerIncludeTime = False}++loggerNoCats :: LoggerStyle+loggerNoCats opts = opts {loggerIncludeCats = False}++loggerNoLoc :: LoggerStyle+loggerNoLoc opts = opts {loggerIncludeLoc = False}++loggerNoSource :: LoggerStyle+loggerNoSource opts = opts {loggerIncludeSource = False}++loggerNoNewline :: LoggerStyle+loggerNoNewline opts = opts {loggerAppendNewline = False}++loggerJson :: LoggerStyle+loggerJson opts = opts {loggerJsonFormat = True}++loggerOrder :: [LogOrderControl] -> LoggerStyle+loggerOrder chunks opts = opts {loggerOrderControl = Just chunks}++liftBaseLogger :: (m () -> n ()) -> LoggerWithCleanup m a -> LoggerWithCleanup n a+liftBaseLogger nat (LoggerWithCleanup f cleanup) =+  LoggerWithCleanup (nat . f) (nat cleanup)++instance Applicative m => Semigroup (LoggerWithCleanup m a) where+  LoggerWithCleanup logA cleanA <> LoggerWithCleanup logB cleanB =+    LoggerWithCleanup (\entry -> logA entry *> logB entry) (cleanA *> cleanB)++instance Applicative m => Monoid (LoggerWithCleanup m a) where   mempty = LoggerWithCleanup (const $ pure ()) (pure ())-  {-# INLINE mempty #-} --- | It doesn't mean it is really fast, called 'FastBaseLogger' because it is imported from fast-logger createFastBaseLogger :: MonadIO m => LogType -> m (LoggerWithCleanup IO LogStr)-createFastBaseLogger logT = liftIO $ uncurry LoggerWithCleanup <$> newFastLogger logT+createFastBaseLogger logType =+  liftIO $ uncurry LoggerWithCleanup <$> newFastLogger logType --- | Uses the logger from fast-logger with default buffer-size createStdoutBaseLogger :: MonadIO m => m (LoggerWithCleanup IO LogStr) createStdoutBaseLogger = createFastBaseLogger (LogStdout defaultBufSize) --- | A very simple logger that just prints to stdout **without buffering**.--- Inlines a putStr function. Suitable for simple applications createSimpleStdoutBaseLogger :: MonadIO m => m (LoggerWithCleanup IO LogStr)-createSimpleStdoutBaseLogger = liftIO $ do-  let logFunc (LogStr _ builder) = BL.putStr (BB.toLazyByteString builder)-  return $ LoggerWithCleanup logFunc (return ())-{-# INLINE createSimpleStdoutBaseLogger #-}+createSimpleStdoutBaseLogger =+  liftIO $ do+    let logFunc (LogStr _ builder) = BL.putStr (BB.toLazyByteString builder)+    pure $ LoggerWithCleanup logFunc (pure ()) --- | A very simple concurrent logger that just prints to stdout **without buffering**.--- A cleanUp function is provided to make sure all logs are printed before exiting. createSimpleConcurrentStdoutBaseLogger :: MonadIO m => m (LoggerWithCleanup IO LogStr)-createSimpleConcurrentStdoutBaseLogger = liftIO $ do-  queue <- newTQueueIO-  counter <- newTVarIO (0 :: Int)-  -- ^ the caller increments this when logging atomically-  -- logger checks this to see if it should exit-  let logFunc (LogStr _ builder) = do-        atomically $ do-          writeTQueue queue builder-          modifyTVar' counter (+1)-      rawLogFunc builder = BL.putStr (BB.toLazyByteString builder)-      atomicLogFunc queue' = do-        logStr <- atomically $ do-          logStr <- readTQueue queue'-          modifyTVar' counter (subtract 1)-          return logStr-        rawLogFunc logStr-  let cleanUpFunc = do-        remQ <- atomically $ do-          r <- readTVar counter-          if r == 0-            then return Nothing-            else do-              b <- flushTQueue queue-              writeTVar counter 0-              return (Just b)-        forM_ remQ (mapM_ rawLogFunc)-  _ <- forkIO $ forever $ atomicLogFunc queue-  return $ LoggerWithCleanup logFunc cleanUpFunc+createSimpleConcurrentStdoutBaseLogger =+  liftIO $ do+    queue <- newTQueueIO+    counter <- newTVarIO (0 :: Int)+    let logFunc (LogStr _ builder) =+          atomically $ do+            writeTQueue queue builder+            modifyTVar' counter (+ 1)+        rawLogFunc builder = BL.putStr (BB.toLazyByteString builder)+        atomicLogFunc queue' = do+          builder <- atomically $ do+            next <- readTQueue queue'+            modifyTVar' counter (subtract 1)+            pure next+          rawLogFunc builder+        cleanUpFunc tid = do+          killThread tid+          remaining <- atomically $ do+            count <- readTVar counter+            if count == 0+              then pure Nothing+              else do+                builders <- flushTQueue queue+                writeTVar counter 0+                pure (Just builders)+          forM_ remaining (mapM_ rawLogFunc)+    tid <- forkIO $ forever $ atomicLogFunc queue+    pure $ LoggerWithCleanup logFunc (cleanUpFunc tid) --- | Uses the logger from fast-logger with default buffer-size createStderrBaseLogger :: MonadIO m => m (LoggerWithCleanup IO LogStr) createStderrBaseLogger = createFastBaseLogger (LogStderr defaultBufSize) --- | Uses the logger from fast-logger with default buffer-size createFileLogger :: MonadIO m => FilePath -> m (LoggerWithCleanup IO LogStr)-createFileLogger fp = createFastBaseLogger (LogFile (FileLogSpec fp (512 * 1024 * 1024) 3) defaultBufSize)+createFileLogger fp =+  createFastBaseLogger (LogFile (FileLogSpec fp (256 * 1024 * 1024) 2) defaultBufSize) -type Timed = Bool--- | this formats the logging data and sends it to the provided function-simpleLogger :: MonadIO m => Timed -> (LogStr -> m ()) -> Logger m LogS-simpleLogger time-  = contramap logSimple-  . contramap (<> "\n")-  . (if time then timeLogger else id)-  . typedLogger-  . baseToLogger+createFileLoggerWith :: MonadIO m => Integer -> Int -> FilePath -> m (LoggerWithCleanup IO LogStr)+createFileLoggerWith size count fp =+  createFastBaseLogger (LogFile (FileLogSpec fp size count) defaultBufSize) --- | Render the inner data type into LogStr and pass to the provided logger accepting LogStr.--- @--- logWithRendering renderB = contramap $ fmap renderB--- @------ Example:--- @--- logWithRendering someRenderFunc (simpleLogger False baseLogFunc) :: Logger m (LogMsg b)--- @-logWithRendering :: (b -> LogStr) -> Logger m LogS -> Logger m (LogMsg b)-logWithRendering renderB = contramap $ fmap renderB-{-# INLINE logWithRendering #-}+renderLogEvent :: LoggerOptions -> LogEvent (LogWithSourceMeta LogDoc) -> IO ML.LogStr+renderLogEvent LoggerOptions {..} entry = do+  let meta = entry ^. logEventPayload+      docChunk = renderLogDoc loggerDocRenderOptions (meta ^. logMetaDoc)+      catChunk =+        if loggerIncludeCats && not (null (entry ^. logEventCats))+          then squareBracket $ logSepList (map (renderLogDoc loggerDocRenderOptions . someLogCatDisplay) (entry ^. logEventCats))+          else emptyField+      locChunk =+        if loggerIncludeLoc+          then maybe emptyField renderLoc (meta ^. logMetaLoc)+          else emptyField+      srcChunk =+        if loggerIncludeSource+          then maybe emptyField ML.toLogStr (meta ^. logMetaSource)+          else emptyField+      suffix = if loggerAppendNewline then "\n" else mempty+      orderControl = fromMaybe [LogTimeChunk, LogCatChunk, LogLocChunk, LogSrcChunk, LogDocChunk] loggerOrderControl --- | simply apply the provided function to the log string-baseToLogger :: (LogStr -> m ()) -> Logger m LogStr-baseToLogger baseIO = Logger $ \(Log _ str) -> baseIO str-{-# INLINE baseToLogger #-}+      logSepList x = mconcat (intersperse sep $ map quote x) --- | Modifies LogStr: add the types of the log to the log string on the left-typedLogger :: Logger m LogStr -> Logger m LogStr-typedLogger (Logger logFunc) = Logger $ \(Log types logStr) -> do-  let typeNames = map someLogCatName types-  let logLine-        | null typeNames = logStr-        | otherwise = "[" <> foldl1' (\x y -> x <> "|" <> y) typeNames <> "] " <> logStr-  logFunc $ Log types logLine-{-# INLINE typedLogger #-}+      emptyField+        | loggerJsonFormat = "null"+        | otherwise        = mempty --- | add the current time to the log string on the left-timeLogger :: MonadIO m => Logger m LogStr -> Logger m LogStr-timeLogger (Logger logger) = Logger $ \(Log types logStr) -> do-  time <- liftIO getCurrentTime-  let timeStr = toLogStr (show time)-  logger $ Log types (timeStr <> "|" <> logStr)-{-# INLINE timeLogger #-}+      quote x = "\"" <> x <> "\"" --- | format the log data into a simple log string, utilizing log types, and location info (if any)-logSimple :: LogS -> LogStr-logSimple LogMsg {..}-    =  maybe "" ((<> ",") . toLogStr . ML.loc_filename) _logLoc-    <> maybe "" ((<> "-") . displayPos . ML.loc_start) _logLoc-    <> maybe "" ((<> "|") . displayPos . ML.loc_end) _logLoc-    <> maybe "" ((<> "|") . toLogStr) _logSource-    <> _logMsg-  where displayPos (l, c) = toLogStr (show l <> ":" <> show c)-{-# INLINE logSimple #-}+      quoteAndEscape x = quote (escape x) --- | Bracket pattern, runs the action with the provided logger and cleans up afterwards+      escape :: ML.LogStr -> ML.LogStr+      escape+        =   fromLogStr+        >>> replace+              [ (wordQuote    , B.pack [wordBackslash, wordQuote     ])+              , (wordBackslash, B.pack [wordBackslash, wordBackslash ])+              , (wordNewline  , B.pack [wordBackslash, wordN         ])+              , (wordTab      , B.pack [wordBackslash, wordT         ])+              , (wordCarriage , B.pack [wordBackslash, wordR         ])+              ]+        >>> map toLogStr+        >>> mconcat+        where+          wordQuote     = 34  :: Word8+          wordBackslash = 92  :: Word8+          wordNewline   = 10  :: Word8+          wordTab       = 9   :: Word8+          wordCarriage  = 13  :: Word8+          wordN         = 110 :: Word8+          wordT         = 116 :: Word8+          wordR         = 114 :: Word8++      replace :: [(Word8, ByteString)] -> ByteString -> [ByteString]+      replace works input =+        let (prefix, work) = B.break (\c -> any (\(w, _) -> w == c) works) input+        in case B.uncons work of+          Nothing     -> [prefix]+          Just (h, t) ->+            prefix+            : case lookup h works of+                Nothing -> B.singleton h+                Just r  -> r+            : replace works t++      squareBracket x = "[" <> x <> "]"++      bigBracket x = "{" <> x <> "}"++      ifJson :: (a -> a) -> a -> a+      ifJson f x = if loggerJsonFormat then f x else x++      sep = if loggerJsonFormat then "," else "|"++      renderLoc loc =+        objectLike+          [ ("file" , ifJson quote $ ML.toLogStr (ML.loc_filename loc))+          , ("start", ifJson quote $ displayPos (ML.loc_start loc))+          , ("end"  , ifJson quote $ displayPos (ML.loc_end loc))+          ]++      objectLike :: [(ML.LogStr, ML.LogStr)] -> ML.LogStr+      objectLike  = ifJson bigBracket+                  . mconcat+                  . intersperse sep+                  . map (\(k, v) -> ifJson ((quote k <> ":") <>) v)++      displayPos (line, col) = ML.toLogStr (show line <> ":" <> show col)++  timeChunk <-+    if loggerIncludeTime+      then ML.toLogStr . show <$> getCurrentTime+      else pure mempty+  pure $ objectLike (map (\case+           LogTimeChunk -> ("time", ifJson quote timeChunk)+           LogCatChunk  -> ("type", catChunk)+           LogLocChunk  -> ("loc" , locChunk)+           LogSrcChunk  -> ("src" , srcChunk)+           LogDocChunk  -> ("doc" , ifJson quoteAndEscape docChunk)+         ) orderControl) <> suffix++loggerFromRenderer+  :: MonadIO m+  => LoggerOptions+  -> (ML.LogStr -> m ())+  -> Logger m (LogWithSourceMeta LogDoc)+loggerFromRenderer opts sink =+  Logger $ \entry -> do+    rendered <- liftIO $ renderLogEvent opts entry+    sink rendered++makeLoggerFromBase+  :: MonadIO m+  => LoggerOptions+  -> LoggerWithCleanup m ML.LogStr+  -> LoggerWithCleanup m (LogEvent (LogWithSourceMeta LogDoc))+makeLoggerFromBase opts LoggerWithCleanup {..} =+  LoggerWithCleanup+    { baseLogFunc = runLogger (loggerFromRenderer opts baseLogFunc)+    , cleanUpFunc = cleanUpFunc+    }+ withLoggerCleanup-  :: (ConsFDataList c (Logging m logS : mods), Monad m, MonadMask m)-  => LoggerWithCleanup m (Log logS) -- ^ specify a logger with cleanup function-  -> EffT' c (Logging m logS : mods) es m a-  -> EffT' c mods es m a-withLoggerCleanup (LoggerWithCleanup logger cleanUp) action = bracketEffT-  (return ())-  (\_ -> lift cleanUp)-  (\_ -> runLogging (Logger logger) action)-{-# INLINE withLoggerCleanup #-}+  :: (ConsFDataList c (LogEffect m a : mods), Monad m, MonadMask m)+  => LoggerWithCleanup m (LogEvent (LogWithSourceMeta a))+  -> EffT' c (LogEffect m a : mods) es m b+  -> EffT' c mods es m b+withLoggerCleanup (LoggerWithCleanup logger cleanup) action =+  bracketEffT+    (pure ())+    (\_ -> lift cleanup)+    (\_ -> runLogEffect (Logger logger) action) --- $ Bracket pattern--- | This function is used to create a logger in a scoped manner.--- It takes care of creating and cleaning up the base logger. withBaseLogger-  :: (ConsFDataList c (Logging m (LogMsg logS) : mods), Monad m, MonadMask m)-  => m (LoggerWithCleanup m LogStr)               -- ^ specify a base logger-  -> ((LogStr -> m ()) -> Logger m (LogMsg logS)) -- ^ specify how to format the log data using the base logger-  -> EffT' c (Logging m (LogMsg logS) : mods) es m a+  :: (ConsFDataList c (LogEffect m LogDoc : mods), MonadIO m, MonadMask m)+  => m (LoggerWithCleanup m ML.LogStr)+  -> LoggerOptions+  -> EffT' c (LogEffect m LogDoc : mods) es m a   -> EffT' c mods es m a-withBaseLogger createBaseLogger makeLogger action = bracketEffT-  (lift createBaseLogger)-  (\LoggerWithCleanup {cleanUpFunc} -> lift cleanUpFunc)-  (\LoggerWithCleanup {baseLogFunc} -> runLogging (makeLogger baseLogFunc) action)-{-# INLINE withBaseLogger #-}+withBaseLogger createBaseLogger opts action =+  bracketEffT+    (lift createBaseLogger)+    (\LoggerWithCleanup {cleanUpFunc} -> lift cleanUpFunc)+    (\baseLogger ->+       let logger = Logger (baseLogFunc (makeLoggerFromBase opts baseLogger))+        in runLogEffect logger action+    )  withBaseLoggerIO-  :: IO (LoggerWithCleanup IO LogStr)                    -- ^ specify a base logger-  -> ((LogStr -> IO ()) -> Logger IO logS) -- ^ specify how to log logS using the base logger-  -> (Logger IO logS -> IO a)  -- ^ action to run with the logger+  :: IO (LoggerWithCleanup IO ML.LogStr)+  -> LoggerOptions+  -> (Logger IO (LogWithSourceMeta LogDoc) -> IO a)   -> IO a-withBaseLoggerIO createBaseLogger makeLogger action = bracket-  (liftIO createBaseLogger)-  (\LoggerWithCleanup {cleanUpFunc} -> liftIO cleanUpFunc)-  (\LoggerWithCleanup {baseLogFunc} -> action (makeLogger baseLogFunc))-{-# INLINE withBaseLoggerIO #-}+withBaseLoggerIO createBaseLogger opts action =+  bracket+    createBaseLogger+    cleanUpFunc+    (\baseLogger -> action $ loggerFromRenderer opts baseLogger.baseLogFunc)++----++defaultLoggingFromEnv+  :: LoggerWithCleanup IO (LogEvent (LogWithSourceMeta LogDoc))+  -> IO (ModuleInitData LoggingModule)+defaultLoggingFromEnv (LoggerWithCleanup logger cleanup) = do+  mLevel <- (readMaybe =<<) <$> lookupEnv "LOG_LEVEL"+  pure $ LogEffectInitData (Logger logger) (Just cleanup) id mLevel++defaultLoggingFromArgs+  :: LoggerWithCleanup IO (LogEvent (LogWithSourceMeta LogDoc))+  -> [String]+  -> Either Text (ModuleInitData LoggingModule)+defaultLoggingFromArgs (LoggerWithCleanup logger cleanup) [] =+  Right $ LogEffectInitData (Logger logger) (Just cleanup) id Nothing+defaultLoggingFromArgs (LoggerWithCleanup logger cleanup) args = do+  level <- maybe (Right Nothing) (fmap Just) $ detectFlag "--log-level" defaultStringToLogSeverity args+  types <- sequence $ detectAllFlags "--log-type" (\case "" -> Left "Empty log type"; s -> Right s) args+  nonTypes <- sequence $ detectAllFlags "--no-log-type" (\case "" -> Left "Empty log type"; s -> Right s) args+  let transform =+        foldr+          (.)+          id+          ( [anyLogCat (isLogCatName name) | name <- types]+              <> [excludeLogCat (isLogCatName name) | name <- nonTypes]+          )+  pure $ LogEffectInitData (Logger logger) (Just cleanup) transform level++defaultLoggingOptParser+  :: Applicative m+  => LoggerWithCleanup m (LogEvent (LogWithSourceMeta LogDoc))+  -> O.Parser (ModuleInitData (LogEffect m LogDoc))+defaultLoggingOptParser (LoggerWithCleanup logger cleanup) = do+  level <-+    O.optional $+      O.option O.auto+        ( O.long "log-level"+            <> O.metavar "LEVEL"+            <> O.help "Log level, one of 'Debug', 'Info', 'Warn', 'Error', or a number between 0 and 10 with a precision of 1 decimal place"+        )+  types :: [String] <-+    O.many $+      O.option O.str+        ( O.long "log-type"+            <> O.metavar "TYPE"+            <> O.help "Log type, can be specified multiple times"+        )+  nonTypes :: [String] <-+    O.many $+      O.option O.str+        ( O.long "no-log-type"+            <> O.metavar "TYPE"+            <> O.help "Log type to exclude, can be specified multiple times"+        )+  pure $+    LogEffectInitData+      (Logger logger)+      (Just cleanup)+      (foldr (.) id $ [anyLogCat (isLogCatName name) | name <- types] <> [excludeLogCat (isLogCatName name) | name <- nonTypes])+      level
src/Module/Logging/TraceId.hs view
@@ -1,27 +1,45 @@-{-# LANGUAGE AllowAmbiguousTypes, QuasiQuotes, DeriveLift #-}--- | This module provides functionality for handling trace IDs in logging.------  A trace Id is a unique identifier used to trace and correlate log entries across different parts of a system.---  It is particularly useful in systems for tracking requests as they propagate through various services.-module Module.Logging.TraceId where+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE QuasiQuotes #-} +module Module.Logging.TraceId+  ( -- * Types+    TraceId(..)+    -- * Modules+  , TraceIdGen+  , newTraceId+  , WithTraceId+  , traceId+    -- * Attaching Trace IDs+  , withTraceId+  , withNewTraceId+  , runWithTraceIdIn+    -- * Trace ID Generators+  , withRandomTraceIdGen+  , withTimeTraceIdGen+  , withCountingTraceIdGen+  , withStartTimeCountingTraceIdGen+  , runTraceIdGen+  , runTraceIdGenIn+  , TraceIdGenRead+  , WithTraceIdRead+  ) where+ import Control.Concurrent.STM import Control.Monad.Effect-import Control.Monad.Logger+import Control.Monad.Logger (toLogStr)+import Data.Aeson (FromJSON, ToJSON) import Data.Time.Clock.POSIX (getPOSIXTime) import Data.TypeList import Data.Word-import Data.Aeson (FromJSON, ToJSON) import Module.Logging-import Module.RS.QQ import Module.Logging.TraceId.XorShiftRNG+import Module.RS.QQ -newtype TraceId = TraceId { unTraceId :: Word64 }+newtype TraceId = TraceId {unTraceId :: Word64}   deriving newtype (Eq, Ord, Show, FromJSON, ToJSON)  instance IsLogCat TraceId where-  logTypeDisplay (TraceId tid) = "TID=" <> toLogStr tid-  {-# INLINE logTypeDisplay #-}+  logTypeName (TraceId tid) = "TID=" <> toLogStr tid  [makeRModule__| TraceIdGen@@ -33,86 +51,67 @@   traceId :: !TraceId |] --- | Assign the provided traceId to the logging context withTraceId-  :: forall log m mods es a.+  :: forall doc m mods es a.      ( Monad m-     , Logging m log `In`    mods-     , WithTraceId   `NotIn` mods+     , LogEffect m doc `In` mods+     , WithTraceId `NotIn` mods      , ConsFDataList FData (WithTraceId : mods)      )-  => TraceId -> EffT (WithTraceId : mods) es m a -> EffT mods es m a-withTraceId tid = effAddLogCat @log (LogCat tid) . runWithTraceId (WithTraceIdRead tid)-{-# INLINE withTraceId #-}---- | Specialized for log = LogS-withTraceId'-  :: forall log m mods es a.-     ( Monad m-     , Logging m log `In`    mods-     , WithTraceId   `NotIn` mods-     , ConsFDataList FData (WithTraceId : mods)-     , log ~ LogS-     )-  => TraceId -> EffT (WithTraceId : mods) es m a -> EffT mods es m a-withTraceId' = withTraceId @log-{-# INLINE withTraceId' #-}+  => TraceId+  -> EffT (WithTraceId : mods) es m a+  -> EffT mods es m a+withTraceId tid =+  effAddLogCat @doc (LogCat tid) . runWithTraceId (WithTraceIdRead tid) --- | Assign new traceId using the provided TraceIdGen module withNewTraceId-  :: forall log m mods es a.+  :: forall doc m mods es a.      ( MonadIO m-     , TraceIdGen     `In`    mods-     , Logging m log  `In`    mods-     , WithTraceId    `NotIn` mods-     , ConsFDataList  FData   (WithTraceId : mods)+     , TraceIdGen `In` mods+     , LogEffect m doc `In` mods+     , WithTraceId `NotIn` mods+     , ConsFDataList FData (WithTraceId : mods)      )-  => EffT (WithTraceId : mods) es m a -> EffT mods es m a+  => EffT (WithTraceId : mods) es m a+  -> EffT mods es m a withNewTraceId act = do-  newTidIO <- asksModule newTraceId-  newTrace <- liftIO     newTidIO-  withTraceId @log newTrace act-{-# INLINE withNewTraceId #-}+  mkTraceId <- asksModule newTraceId+  tid <- liftIO mkTraceId+  withTraceId @doc tid act --- | Using a global XorShift random number generator for traceId withRandomTraceIdGen   :: (MonadIO m, ConsFDataList FData (TraceIdGen : mods))-  => EffT (TraceIdGen : mods) es m a -> EffT mods es m a+  => EffT (TraceIdGen : mods) es m a+  -> EffT mods es m a withRandomTraceIdGen act = do   rng <- liftIO newRNG   runTraceIdGen (TraceIdGenRead $ TraceId <$> uniformWord64FromRNG rng) act-{-# INLINE withRandomTraceIdGen #-} --- | Using current time in microsecond precision for traceId withTimeTraceIdGen   :: (MonadIO m, ConsFDataList FData (TraceIdGen : mods))-  => EffT (TraceIdGen : mods) es m a -> EffT mods es m a-withTimeTraceIdGen act = do-  runTraceIdGen (TraceIdGenRead $ TraceId . floor . (*1000_000) <$> getPOSIXTime) act-{-# INLINE withTimeTraceIdGen #-}+  => EffT (TraceIdGen : mods) es m a+  -> EffT mods es m a+withTimeTraceIdGen act =+  runTraceIdGen (TraceIdGenRead $ TraceId . floor . (* 1000_000) <$> getPOSIXTime) act --- | Using a simple counting number for traceId, starting from the provided number withCountingTraceIdGen   :: (MonadIO m, ConsFDataList FData (TraceIdGen : mods))-  => Word64  -- ^ starting count, e.g. you can use microsecond unix time+  => Word64   -> EffT (TraceIdGen : mods) es m a   -> EffT mods es m a withCountingTraceIdGen startCount act = do   counter <- liftIO $ newTVarIO startCount-  let getNewTid = atomically $ do-        tid <- readTVar counter-        let !newTid = tid + 1-        writeTVar counter newTid-        return $ TraceId tid-  runTraceIdGen (TraceIdGenRead getNewTid) act-{-# INLINE withCountingTraceIdGen #-}+  let nextTraceId = atomically $ do+        current <- readTVar counter+        let newValue = current + 1+        writeTVar counter newValue+        pure $ TraceId current+  runTraceIdGen (TraceIdGenRead nextTraceId) act --- | Using current time in microsecond precision as the starting point for a counting traceId generator withStartTimeCountingTraceIdGen   :: (MonadIO m, ConsFDataList FData (TraceIdGen : mods))   => EffT (TraceIdGen : mods) es m a   -> EffT mods es m a withStartTimeCountingTraceIdGen act = do-  startTime <- liftIO $ floor . (*1000_000) <$> getPOSIXTime+  startTime <- liftIO $ floor . (* 1000_000) <$> getPOSIXTime   withCountingTraceIdGen startTime act-{-# INLINE withStartTimeCountingTraceIdGen #-}
src/Module/Logging/TraceId/XorShiftRNG.hs view
@@ -39,7 +39,7 @@   in  z2 `xor` (z2 `shiftR` 31) {-# INLINE mix64 #-} --- ----------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- -- | Turn one seed into @n@ independent seeds suitable for different threads. -- --   *   Deterministic: same parent → same list.