simple-logging 0.2.0.2 → 0.2.0.3
raw patch · 3 files changed
+49/−36 lines, 3 filesdep ~simple-effectsPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: simple-effects
API changes (from Hackage documentation)
- Control.Effects.Logging: Logging :: Logging
+ Control.Effects.Logging: instance Control.Effects.Effect Control.Effects.Logging.Logging
+ Control.Effects.Logging: instance GHC.Generics.Generic (Control.Effects.EffMethods Control.Effects.Logging.Logging m)
- Control.Effects.Logging: addCrumbToLogs :: MonadEffect Logging m => Crumb -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: addCrumbToLogs :: MonadEffect Logging m => Crumb -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: addUserToLogs :: MonadEffect Logging m => LogUser -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: addUserToLogs :: MonadEffect Logging m => LogUser -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: collectCrumbs :: MonadEffect Logging m => EffectHandler Logging (StateT [Crumb] m) a -> m a
+ Control.Effects.Logging: collectCrumbs :: MonadEffect Logging m => RuntimeImplemented Logging (StateT [Crumb] m) a -> m a
- Control.Effects.Logging: filterLogs :: MonadEffect Logging m => (Log -> Bool) -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: filterLogs :: MonadEffect Logging m => (Log -> Bool) -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: handleLogging :: Functor m => (Log -> m ()) -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: handleLogging :: Functor m => (Log -> m ()) -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: layerLogs :: (HasCallStack, MonadEffect Logging m) => Context -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: layerLogs :: (HasCallStack, MonadEffect Logging m) => Context -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: logAndThrowsErr :: (MonadEffect Logging m, Throws e m, HasCallStack) => Text -> e -> m a
+ Control.Effects.Logging: logAndThrowsErr :: (MonadEffects '[Logging, Signal e Void] m, HasCallStack) => Text -> e -> m a
- Control.Effects.Logging: logIfDepth :: MonadEffect Logging m => (Int -> Bool) -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: logIfDepth :: MonadEffect Logging m => (Int -> Bool) -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: logIfDepthLessThan :: MonadEffect Logging m => Int -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: logIfDepthLessThan :: MonadEffect Logging m => Int -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: logMessagesToStdout :: MonadIO m => EffectHandler Logging m a -> m a
+ Control.Effects.Logging: logMessagesToStdout :: MonadIO m => RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: logRawToStdout :: MonadIO m => EffectHandler Logging m a -> m a
+ Control.Effects.Logging: logRawToStdout :: MonadIO m => RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: mapLogs :: MonadEffect Logging m => (Log -> m Log) -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: mapLogs :: MonadEffect Logging m => (Log -> m Log) -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: messagesToCrumbs :: (MonadIO m, MonadEffect Logging m) => EffectHandler Logging m a -> m a
+ Control.Effects.Logging: messagesToCrumbs :: (MonadIO m, MonadEffect Logging m) => RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: muteLogs :: Monad m => EffectHandler Logging m a -> m a
+ Control.Effects.Logging: muteLogs :: Monad m => RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: prettyPrintSummary :: MonadIO m => Int -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: prettyPrintSummary :: MonadIO m => Int -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: setDataTo :: MonadEffect Logging m => ByteString -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: setDataTo :: MonadEffect Logging m => ByteString -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: setDataToJsonOf :: (MonadEffect Logging m, ToJSON v) => v -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: setDataToJsonOf :: (MonadEffect Logging m, ToJSON v) => v -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: setDataToShowOf :: (MonadEffect Logging m, Show v) => v -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: setDataToShowOf :: (MonadEffect Logging m, Show v) => v -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: setDataWithSummary :: MonadEffect Logging m => LogData -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: setDataWithSummary :: MonadEffect Logging m => LogData -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: setTimestampToNow :: (MonadEffect Logging m, MonadIO m) => EffectHandler Logging m a -> m a
+ Control.Effects.Logging: setTimestampToNow :: (MonadEffect Logging m, MonadIO m) => RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: witherLogs :: MonadEffect Logging m => (Log -> m (Maybe Log)) -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: witherLogs :: MonadEffect Logging m => (Log -> m (Maybe Log)) -> RuntimeImplemented Logging m a -> m a
- Control.Effects.Logging: writeDataToFiles :: (MonadEffect Logging m, MonadIO m) => FilePath -> EffectHandler Logging m a -> m a
+ Control.Effects.Logging: writeDataToFiles :: (MonadEffect Logging m, MonadIO m) => FilePath -> RuntimeImplemented Logging m a -> m a
Files
- CHANGELOG.md +9/−0
- simple-logging.cabal +2/−2
- src/Control/Effects/Logging.hs +38/−34
CHANGELOG.md view
@@ -1,3 +1,12 @@+# 0.2.0.3 +Updated to simple-effects-0.10.0.0 + +# 0.2.0.2 +Colors in pretty printing +Log data can now hold a summary +Handler for logging all IO exceptions +Handler for writing all log data to the file system + # 0.2.0.1 Fixed wrong pretty printing
simple-logging.cabal view
@@ -1,5 +1,5 @@ name: simple-logging-version: 0.2.0.2+version: 0.2.0.3 synopsis: Logging effect to plug into the simple-effects framework homepage: https://gitlab.com/haskell-hr/logging license: MIT@@ -23,7 +23,7 @@ , bytestring , iso8601-time , text- , simple-effects+ , simple-effects >= 0.10.0.0 , exceptions , mtl , string-conv
src/Control/Effects/Logging.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE TypeFamilies, FlexibleContexts, MultiParamTypeClasses, RankNTypes, ConstraintKinds , RecordWildCards #-} {-# LANGUAGE GADTs, DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE NoMonomorphismRestriction #-} -- | Use this module to add logging to your monad. -- A log is a structured value that can hold information like severity, log message, timestamp, -- callstack, etc. @@ -32,13 +34,20 @@ import Control.Effects.Early import Data.UUID +import GHC.Generics +import Data.Void -- | The logging effect. -data Logging = Logging -data instance Effect Logging method mr where - LoggingMsg :: Log -> Effect Logging 'Logging 'Msg - LoggingRes :: Effect Logging 'Logging 'Res +data Logging +instance Effect Logging where + data EffMethods Logging m = LoggingMethods + { _logEffect :: Log -> m () } + deriving (Generic) +-- | Send a single log into the stream. +logEffect :: MonadEffect Logging m => Log -> m () +LoggingMethods logEffect = effect + -- | Arbitrary piece of text. Logs contain a list of these. newtype Tag = Tag Text deriving (Eq, Ord, Read, Show) @@ -123,17 +132,13 @@ newtype GenericException = GenericException Text deriving (Eq, Ord, Read, Show) instance Exception GenericException --- | Send a single log into the stream. -logEffect :: MonadEffect Logging m => Log -> m () -logEffect = void . effect . LoggingMsg - -- | A generic handler for logs. Since it's polymorphic in 'm' you can choose to emit more logs -- and make it a log transformer instead. -handleLogging :: Functor m => (Log -> m ()) -> EffectHandler Logging m a -> m a -handleLogging f = handleEffect (\(LoggingMsg l) -> LoggingRes <$ f l) +handleLogging :: Functor m => (Log -> m ()) -> RuntimeImplemented Logging m a -> m a +handleLogging f = implement (LoggingMethods f) -- | Add a new context on top of every log that comes from the given computation. -layerLogs :: (HasCallStack, MonadEffect Logging m) => Context -> EffectHandler Logging m a -> m a +layerLogs :: (HasCallStack, MonadEffect Logging m) => Context -> RuntimeImplemented Logging m a -> m a layerLogs ctx = handleLogging (\log' -> logEffect (log' { logContext = ctx : logContext log' })) -- | Get the bottom-most context if it exists. @@ -157,7 +162,7 @@ -- | Log an error and then throw a checked exception. -- Read about checked exceptions in 'Control.Effects.Signal'. -logAndThrowsErr :: (MonadEffect Logging m, Throws e m, HasCallStack) => Text -> e -> m a +logAndThrowsErr :: (MonadEffects '[Logging, Signal e Void] m, HasCallStack) => Text -> e -> m a logAndThrowsErr msg err = logError msg >> throwSignal err -- | Log an error and throw a generic exception containing the text of the error message. @@ -166,40 +171,40 @@ -- | Log a stripped-down version of the logs to the console. -- Only contains the message and the severity. -logMessagesToStdout :: MonadIO m => EffectHandler Logging m a -> m a +logMessagesToStdout :: MonadIO m => RuntimeImplemented Logging m a -> m a logMessagesToStdout = handleLogging (\Log{..} -> putText (pshow logLevel <> ": " <> logMessage)) -- | Log everything to the console. Uses the 'Show' instance for 'Log'. -logRawToStdout :: MonadIO m => EffectHandler Logging m a -> m a +logRawToStdout :: MonadIO m => RuntimeImplemented Logging m a -> m a logRawToStdout = handleLogging print -- | Discard the logs. -muteLogs :: Monad m => EffectHandler Logging m a -> m a +muteLogs :: Monad m => RuntimeImplemented Logging m a -> m a muteLogs = handleLogging (const (return ())) -- | Use the given function to transform and possibly discard logs. -witherLogs :: MonadEffect Logging m => (Log -> m (Maybe Log)) -> EffectHandler Logging m a -> m a +witherLogs :: MonadEffect Logging m => (Log -> m (Maybe Log)) -> RuntimeImplemented Logging m a -> m a witherLogs f = handleLogging $ f >=> maybe (return ()) logEffect -- | Only let through logs that satisfy the given predicate. -filterLogs :: MonadEffect Logging m => (Log -> Bool) -> EffectHandler Logging m a -> m a +filterLogs :: MonadEffect Logging m => (Log -> Bool) -> RuntimeImplemented Logging m a -> m a filterLogs f = witherLogs (\l -> return $ if f l then Just l else Nothing) -- | Transform logs with the given function. -mapLogs :: MonadEffect Logging m => (Log -> m Log) -> EffectHandler Logging m a -> m a +mapLogs :: MonadEffect Logging m => (Log -> m Log) -> RuntimeImplemented Logging m a -> m a mapLogs f = witherLogs ((Just <$>) . f) -- | Filter out logs that are comming from below a certain depth. -logIfDepthLessThan :: MonadEffect Logging m => Int -> EffectHandler Logging m a -> m a +logIfDepthLessThan :: MonadEffect Logging m => Int -> RuntimeImplemented Logging m a -> m a logIfDepthLessThan n = logIfDepth (< n) -- | Filter logs whose depth satisfies the given predicate. -logIfDepth :: MonadEffect Logging m => (Int -> Bool) -> EffectHandler Logging m a -> m a +logIfDepth :: MonadEffect Logging m => (Int -> Bool) -> RuntimeImplemented Logging m a -> m a logIfDepth cond = filterLogs (\Log{..} -> cond (length logContext)) -- | For each log, add it's message to the logs breadcrumb list. This is useful so you don't have -- to manually add crumbs. -messagesToCrumbs :: (MonadIO m, MonadEffect Logging m) => EffectHandler Logging m a -> m a +messagesToCrumbs :: (MonadIO m, MonadEffect Logging m) => RuntimeImplemented Logging m a -> m a messagesToCrumbs = mapLogs $ \l@Log{..} -> do time <- liftIO getCurrentTime let cat = Text.intercalate "." (fmap getContext logContext) @@ -209,7 +214,7 @@ -- If, for example, you're writing a web server, you might want to have this handler over the -- request handler so that if an error occurs you can see all the steps that happened before it, -- during the handling of that request. -collectCrumbs :: MonadEffect Logging m => EffectHandler Logging (StateT [Crumb] m) a -> m a +collectCrumbs :: MonadEffect Logging m => RuntimeImplemented Logging (StateT [Crumb] m) a -> m a collectCrumbs = flip evalStateT [] . mapLogs (\l@Log{..} -> do crumbs <- get let newCrumbs = crumbs ++ logCrumbs @@ -217,37 +222,37 @@ return (l { logCrumbs = newCrumbs })) -- | Add a user to every log. -addUserToLogs :: MonadEffect Logging m => LogUser -> EffectHandler Logging m a -> m a +addUserToLogs :: MonadEffect Logging m => LogUser -> RuntimeImplemented Logging m a -> m a addUserToLogs user = mapLogs (\l -> return (l { logUser = Just user })) -- | Add a crumb to every log. -addCrumbToLogs :: MonadEffect Logging m => Crumb -> EffectHandler Logging m a -> m a +addCrumbToLogs :: MonadEffect Logging m => Crumb -> RuntimeImplemented Logging m a -> m a addCrumbToLogs crumb = mapLogs (\l -> return (l { logCrumbs = logCrumbs l ++ [crumb] })) -- | Attach arbitrary data to every log. Typically you want to use this handler on -- 'logX' functions directly like @setDataWithSummary "some data" (logInfo "some info")@ -setDataWithSummary :: MonadEffect Logging m => LogData -> EffectHandler Logging m a -> m a +setDataWithSummary :: MonadEffect Logging m => LogData -> RuntimeImplemented Logging m a -> m a setDataWithSummary dat = mapLogs (\l -> return (l { logData = dat })) -- | Attach an arbitrary 'ByteString' to every log. Typically you want to use this handler on -- 'logX' functions directly like @setDataTo "some data" (logInfo "some info")@ -setDataTo :: MonadEffect Logging m => ByteString -> EffectHandler Logging m a -> m a +setDataTo :: MonadEffect Logging m => ByteString -> RuntimeImplemented Logging m a -> m a setDataTo bs = setDataWithSummary (LogData bs "") -- | Attach an arbitrary value to every log using it's 'ToJSON' instance. -- Typically you want to use this handler on 'logX' functions directly like -- @setDataToJsonOf 123 (logInfo "some info")@ -setDataToJsonOf :: (MonadEffect Logging m, ToJSON v) => v -> EffectHandler Logging m a -> m a +setDataToJsonOf :: (MonadEffect Logging m, ToJSON v) => v -> RuntimeImplemented Logging m a -> m a setDataToJsonOf = setDataTo . toS . encode -- | Attach an arbitrary value to every log using it's 'Show' instance. -- Typically you want to use this handler on 'logX' functions directly like -- @setDataToShowOf 123 (logInfo "some info")@ -setDataToShowOf :: (MonadEffect Logging m, Show v) => v -> EffectHandler Logging m a -> m a +setDataToShowOf :: (MonadEffect Logging m, Show v) => v -> RuntimeImplemented Logging m a -> m a setDataToShowOf = setDataTo . pshow -- | Add the current time to every log. -setTimestampToNow :: (MonadEffect Logging m, MonadIO m) => EffectHandler Logging m a -> m a +setTimestampToNow :: (MonadEffect Logging m, MonadIO m) => RuntimeImplemented Logging m a -> m a setTimestampToNow = mapLogs $ \l -> do time <- liftIO getCurrentTime return (l { logTimestamp = Just time }) @@ -269,7 +274,7 @@ -- the logs with the path to the files. writeDataToFiles :: ( MonadEffect Logging m, MonadIO m ) - => FilePath -> EffectHandler Logging m a -> m a + => FilePath -> RuntimeImplemented Logging m a -> m a writeDataToFiles path m = do liftIO (createDirectoryIfMissing True path) m & mapLogs ( \l@Log{..} -> do @@ -297,10 +302,9 @@ -- | Print out the logs in rich format. Truncates at the given length. -- Logs will contain: message, timestamp, data, user and the call stack. -prettyPrintSummary :: MonadIO m => Int -> EffectHandler Logging m a -> m a -prettyPrintSummary trunc h = do - lock <- liftIO (newMVar ()) - flip handleLogging h $ \Log{..} -> liftIO $ withMVar lock $ \_ -> do +prettyPrintSummary :: MonadIO m => Int -> RuntimeImplemented Logging m a -> m a +prettyPrintSummary trunc h = + flip handleLogging h $ \Log{..} -> liftIO $ do let callStackSection = manyLines trunc (toS (prettyCallStack logCallStack)) let LogData{..} = logData let dataSection =