packages feed

log-warper 1.3.4 → 1.4.0

raw patch · 7 files changed

+126/−93 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- System.Wlog.Handler.Simple: verboseStreamHandler :: Handle -> Severity -> IO (GenericHandler Handle)
- System.Wlog.LoggerConfig: [_lcConsoleOutput] :: LoggerConfig -> Any
- System.Wlog.LoggerConfig: consoleOutB :: LoggerConfig
- System.Wlog.LoggerConfig: lcConsoleOutput :: Lens' LoggerConfig Any
+ System.Wlog.Handler.Simple: defaultHandleAction :: Handle -> Text -> IO ()
+ System.Wlog.LoggerConfig: [_lcConsoleAction] :: LoggerConfig -> Last (Handle -> Text -> IO ())
+ System.Wlog.LoggerConfig: consoleActionB :: (Handle -> Text -> IO ()) -> LoggerConfig
+ System.Wlog.LoggerConfig: customConsoleActionB :: Maybe (Handle -> Text -> IO ()) -> LoggerConfig
+ System.Wlog.LoggerConfig: lcConsoleAction :: Lens' LoggerConfig (Last (Handle -> Text -> IO ()))
+ System.Wlog.LoggerConfig: termSeverityB :: Severity -> LoggerConfig
- System.Wlog.Handler.Simple: streamHandler :: Handle -> Severity -> IO (GenericHandler Handle)
+ System.Wlog.Handler.Simple: streamHandler :: Handle -> (Handle -> Text -> IO ()) -> MVar () -> Severity -> IO (GenericHandler Handle)
- System.Wlog.LoggerConfig: LoggerConfig :: Maybe RotationParameters -> Maybe Severity -> Any -> Any -> Any -> Endo LoggerName -> Maybe FilePath -> LoggerTree -> LoggerConfig
+ System.Wlog.LoggerConfig: LoggerConfig :: Maybe RotationParameters -> Maybe Severity -> Any -> Any -> Last (Handle -> Text -> IO ()) -> Endo LoggerName -> Maybe FilePath -> LoggerTree -> LoggerConfig
- System.Wlog.Wrapper: initTerminalLogging :: MonadIO m => (UTCTime -> Text) -> Bool -> Bool -> Maybe Severity -> m ()
+ System.Wlog.Wrapper: initTerminalLogging :: MonadIO m => (UTCTime -> Text) -> (Handle -> Text -> IO ()) -> Bool -> Bool -> Maybe Severity -> m ()

Files

CHANGES.md view
@@ -1,3 +1,8 @@+1.4.0+=====++* Add ability to specify custom logging action.+ 1.3.4 ==== 
examples/Playground.hs view
@@ -7,11 +7,11 @@ import           Data.Monoid      ((<>)) import           Data.Yaml.Pretty (defConfig, encodePretty) -import           System.Wlog      (CanLog, buildAndSetupYamlLogging, dispatchEvents,-                                   logDebug, logError, logInfo, logNotice, logWarning,-                                   modifyLoggerName, parseLoggerConfig, prefixB,-                                   productionB, releaseAllHandlers, runPureLog,-                                   usingLoggerName)+import           System.Wlog      (CanLog, Severity (Debug), buildAndSetupYamlLogging,+                                   dispatchEvents, logDebug, logError, logInfo, logNotice,+                                   logWarning, modifyLoggerName, parseLoggerConfig,+                                   prefixB, productionB, releaseAllHandlers, runPureLog,+                                   termSeverityB, usingLoggerName)  testLoggerConfigPath :: FilePath testLoggerConfigPath = "logger-config-example.yaml"@@ -46,7 +46,7 @@ main :: IO () main = do     testToJsonConfigOutput-    let config = (productionB <> prefixB "logs")+    let config = (productionB <> prefixB "logs" <> termSeverityB Debug)     bracket_         (buildAndSetupYamlLogging config testLoggerConfigPath)         releaseAllHandlers
log-warper.cabal view
@@ -1,5 +1,5 @@ name:                log-warper-version:             1.3.4+version:             1.4.0 synopsis:            Flexible, configurable, monadic and pretty logging homepage:            https://github.com/serokell/log-warper license:             MIT
src/System/Wlog/Handler/Simple.hs view
@@ -17,10 +17,12 @@ -}  module System.Wlog.Handler.Simple-       ( streamHandler+       ( GenericHandler(..)+       , defaultHandleAction++         -- * Custom handlers        , fileHandler-       , GenericHandler(..)-       , verboseStreamHandler+       , streamHandler        ) where  import           Control.Concurrent      (modifyMVar_, withMVar)@@ -31,18 +33,15 @@ import           System.Directory        (createDirectoryIfMissing) import           System.FilePath         (takeDirectory) import           System.IO               (Handle, IOMode (ReadWriteMode),-                                          SeekMode (SeekFromEnd), hClose,-                                          hFlush, hSeek)+                                          SeekMode (SeekFromEnd), hClose, hFlush, hSeek) import           Universum -import           System.Wlog.Formatter   (LogFormatter, nullFormatter,-                                          simpleLogFormatter)+import           System.Wlog.Formatter   (LogFormatter, nullFormatter) import           System.Wlog.Handler     (LogHandler (..), LogHandlerTag (..)) import           System.Wlog.MemoryQueue (MemoryQueue) import           System.Wlog.MemoryQueue as MQ import           System.Wlog.Severity    (Severity (..)) - -- | A helper data type. data GenericHandler a = GenericHandler     { severity       :: !Severity@@ -64,39 +63,57 @@     emit sh bldr _ = (writeFunc sh) (privData sh) (toText . B.toLazyText $ bldr)     close sh = (closeFunc sh) (privData sh) +-- | Default action which just prints to handle using given message.+defaultHandleAction :: Handle -> Text -> IO ()+defaultHandleAction h message =+    TIO.hPutStrLn h message `catch` handleWriteException+  where+    handleWriteException :: SomeException -> IO ()+    handleWriteException e = do+        let errorMessage = "Error writing log message: "+                        <> show e <> " (original message: " <> message <> ")"+        TIO.hPutStrLn h errorMessage++-- | Creates custom write action and memory queue where write action+-- updates memory queue as well.+createWriteFuncWrapper+    :: (Handle -> Text -> IO ())+    -> MVar ()+    -> IO ( Handle -> Text -> IO ()+          , MVar (MemoryQueue Text)+          )+createWriteFuncWrapper action lock = do+    memoryQueue <- newMVar $ MQ.newMemoryQueue $ 2 * 1024 * 1024 -- 2 MB++    let customWriteFunc :: Handle -> Text -> IO ()+        customWriteFunc hdl msg = withMVar lock $ const $ do+            action hdl msg++            -- Important to force the queue here, else a massive closure will+            -- be retained until the queue is actually used.+            modifyMVar_ memoryQueue $ \mq -> pure $! pushFront msg mq++            hFlush hdl++    return (customWriteFunc, memoryQueue)+ -- | Create a stream log handler. Log messages sent to this handler -- will be sent to the stream used initially. Note that the 'close' -- method will have no effect on stream handlers; it does not actually -- close the underlying stream.-streamHandler :: Handle -> Severity -> IO (GenericHandler Handle)-streamHandler h sev = do-    lock <- newMVar ()-    mq <- newMVar $ MQ.newMemoryQueue $ 2 * 1024 * 1024 -- 2 MB-    let mywritefunc hdl msg = withMVar lock $ const $ do-            writeToHandle hdl msg-            -- Important to force the queue here, else a massive closure will-            -- be retained until the queue is actually used.-            modifyMVar_ mq $ \mq' -> pure $! pushFront msg mq'-            hFlush hdl-    return-        GenericHandler-        { severity = sev-        , formatter = nullFormatter-        , privData = h-        , writeFunc = mywritefunc+streamHandler :: Handle+              -> (Handle -> Text -> IO ())+              -> MVar ()+              -> Severity+              -> IO (GenericHandler Handle)+streamHandler privData writeAction lock severity = do+    (writeFunc, readBackBuffer) <- createWriteFuncWrapper writeAction lock+    return GenericHandler+        { formatter = nullFormatter         , closeFunc = const $ pure ()-        , readBackBuffer = mq-        , ghTag = HandlerOther "GenericHandler/StreamHandler"+        , ghTag     = HandlerOther "GenericHandler/StreamHandler"+        , ..         }-  where-    writeToHandle hdl msg =-        TIO.hPutStrLn hdl msg `catch` (handleWriteException hdl msg)-    handleWriteException :: Handle -> Text -> SomeException -> IO ()-    handleWriteException hdl msg e =-        let msg' =-                "Error writing log message: " <>-                show e <> " (original message: " <> msg <> ")"-        in TIO.hPutStrLn hdl msg'  -- | Create a file log handler.  Log messages sent to this handler -- will be sent to the filename specified, which will be opened in@@ -106,15 +123,9 @@     createDirectoryIfMissing True (takeDirectory fp)     h <- openFile fp ReadWriteMode     hSeek h SeekFromEnd 0-    sh <- streamHandler h sev++    lock <- newMVar ()+    sh <- streamHandler h defaultHandleAction lock sev     pure $ sh { closeFunc = hClose               , ghTag = HandlerFilelike fp               }---- | Like 'streamHandler', but note the priority and logger name along--- with each message.-verboseStreamHandler :: Handle -> Severity -> IO (GenericHandler Handle)-verboseStreamHandler h sev =-    let fmt = simpleLogFormatter "[$loggername/$prio] $msg"-    in do hndlr <- streamHandler h sev-          return $ setFormatter hndlr fmt
src/System/Wlog/Launcher.hs view
@@ -32,6 +32,8 @@        , setupLogging        ) where +import           Universum+ import           Control.Error.Util         ((?:)) import           Control.Exception          (throwIO) import qualified Data.HashMap.Strict        as HM hiding (HashMap)@@ -40,7 +42,6 @@ import           Data.Yaml                  (decodeFileEither) import           System.Directory           (createDirectoryIfMissing) import           System.FilePath            ((</>))-import           Universum  import           System.Wlog.Formatter      (centiUtcTimeF, stdoutFormatter,                                              stdoutFormatterTimeRounded)@@ -64,8 +65,9 @@ setupLogging mTimeFunction LoggerConfig{..} = do     liftIO $ createDirectoryIfMissing True handlerPrefix -    when consoleOutput $+    whenJust consoleAction $ \customTerminalAction ->         initTerminalLogging timeF+                            customTerminalAction                             isShowTime                             isShowTid                             _lcTermSeverity@@ -78,7 +80,7 @@     timeF         = fromMaybe centiUtcTimeF mTimeFunction     isShowTime    = getAny _lcShowTime     isShowTid     = getAny _lcShowTid-    consoleOutput = getAny _lcConsoleOutput+    consoleAction = getLast _lcConsoleAction      handlerFabric :: HandlerFabric     handlerFabric = case _lcRotation of@@ -88,7 +90,7 @@     processLoggers :: MonadIO m => LoggerName -> LoggerTree -> m ()     processLoggers parent LoggerTree{..} = do         -- This prevents logger output to appear in terminal-        unless (parent == mempty && not consoleOutput) $+        unless (parent == mempty && isNothing consoleAction) $             setSeverityMaybe parent _ltSeverity          forM_ _ltFiles $ \HandlerWrap{..} -> liftIO $ do
src/System/Wlog/LoggerConfig.hs view
@@ -33,7 +33,7 @@        , LoggerConfig (..)           -- ** Lenses-       , lcConsoleOutput+       , lcConsoleAction        , lcFilePrefix        , lcMapper        , lcRotation@@ -44,34 +44,37 @@        , zoomLogger           -- ** Builders for 'LoggerConfig'-       , consoleOutB+       , consoleActionB+       , customConsoleActionB        , mapperB        , maybePrefixB        , prefixB        , productionB        , showTidB        , showTimeB+       , termSeverityB        ) where  import           Universum -import           Control.Lens           (at, makeLenses, zoom, _Just)-import           Control.Monad.State    (put)-import           Data.Aeson             (withObject)-import qualified Data.HashMap.Strict    as HM hiding (HashMap)-import           Data.Monoid            (Any (..))-import           Data.Text              (Text)-import qualified Data.Text.Buildable    as Buildable-import           Data.Traversable       (for)-import           Data.Word              (Word64)-import           Data.Yaml              (FromJSON (..), ToJSON (..), Value (Object),-                                         object, (.!=), (.:), (.:?), (.=))-import           Formatting             (bprint, shown)-import           GHC.Generics           (Generic)-import           System.FilePath        (normalise)+import           Control.Lens               (at, makeLenses, zoom, _Just)+import           Control.Monad.State        (put)+import           Data.Aeson                 (withObject)+import qualified Data.HashMap.Strict        as HM hiding (HashMap)+import           Data.Monoid                (Any (..))+import           Data.Text                  (Text)+import qualified Data.Text.Buildable        as Buildable+import           Data.Traversable           (for)+import           Data.Word                  (Word64)+import           Data.Yaml                  (FromJSON (..), ToJSON (..), Value (Object),+                                             object, (.!=), (.:), (.:?), (.=))+import           Formatting                 (bprint, shown)+import           GHC.Generics               (Generic)+import           System.FilePath            (normalise) -import           System.Wlog.LoggerName (LoggerName)-import           System.Wlog.Wrapper    (Severity)+import           System.Wlog.Handler.Simple (defaultHandleAction)+import           System.Wlog.LoggerName     (LoggerName)+import           System.Wlog.Severity       (Severity)  ---------------------------------------------------------------------------- -- Utilites & helpers@@ -199,8 +202,8 @@       -- | Show 'ThreadId' for current logging thread.     , _lcShowTid       :: Any -      -- | @True@ if we should also print output into console.-    , _lcConsoleOutput :: Any+      -- | Specifies action for printing to console.+    , _lcConsoleAction :: Last (Handle -> Text -> IO ())        -- | Defines how to transform logger names in config.     , _lcMapper        :: Endo LoggerName@@ -221,7 +224,7 @@         , _lcTermSeverity  = Nothing         , _lcShowTime      = mempty         , _lcShowTid       = mempty-        , _lcConsoleOutput = mempty+        , _lcConsoleAction = mempty         , _lcMapper        = mempty         , _lcFilePrefix    = Nothing         , _lcTree          = mempty@@ -232,7 +235,7 @@         , _lcTermSeverity  = orCombiner  _lcTermSeverity         , _lcShowTime      = andCombiner _lcShowTime         , _lcShowTid       = andCombiner _lcShowTid-        , _lcConsoleOutput = andCombiner _lcConsoleOutput+        , _lcConsoleAction = andCombiner _lcConsoleAction         , _lcMapper        = andCombiner _lcMapper         , _lcFilePrefix    = orCombiner  _lcFilePrefix         , _lcTree          = andCombiner _lcTree@@ -258,10 +261,12 @@         _lcTermSeverity  <-         o .:? "termSeverity"         _lcShowTime      <- Any <$> o .:? "showTime"    .!= False         _lcShowTid       <- Any <$> o .:? "showTid"     .!= False-        _lcConsoleOutput <- Any <$> o .:? "printOutput" .!= False         _lcFilePrefix    <-         o .:? "filePrefix"         _lcTree          <- parseJSON $ Object $ filterObject topLevelParams o-        let _lcMapper     = mempty++        printConsoleFlag    <- o .:? "printOutput" .!= False+        let _lcConsoleAction = Last $ bool Nothing (Just defaultHandleAction) printConsoleFlag+        let _lcMapper        = mempty         return LoggerConfig{..}  -- | This instances violates @fromJSON . toJSON = identity@ rule but doesn't matter@@ -272,13 +277,18 @@             , "termSeverity" .= _lcTermSeverity             , "showTime"     .= getAny _lcShowTime             , "showTid"      .= getAny _lcShowTid-            , "printOutput"  .= getAny _lcConsoleOutput+            , "printOutput"  .= maybe False (const True) (getLast _lcConsoleAction)             , "filePrefix"   .= _lcFilePrefix             , ("logTree", toJSON _lcTree)             ]-+---------------------------------------------------------------------------- -- Builders for 'LoggerConfig'.+---------------------------------------------------------------------------- +-- | Setup 'lcTermSeverity' to specified severity inside 'LoggerConfig'.+termSeverityB :: Severity -> LoggerConfig+termSeverityB severity = mempty { _lcTermSeverity = Just severity }+ -- | Setup 'lcShowTime' to 'True' inside 'LoggerConfig'. showTimeB :: LoggerConfig showTimeB = mempty { _lcShowTime = Any True }@@ -287,13 +297,16 @@ showTidB :: LoggerConfig showTidB = mempty { _lcShowTid = Any True } +consoleActionB :: (Handle -> Text -> IO ()) -> LoggerConfig+consoleActionB action = mempty { _lcConsoleAction = Last $ Just action }+ -- | Setup 'lcConsoleOutput' inside 'LoggerConfig'.-consoleOutB :: LoggerConfig-consoleOutB = mempty { _lcConsoleOutput = Any True }+customConsoleActionB :: Maybe (Handle -> Text -> IO ()) -> LoggerConfig+customConsoleActionB action = mempty { _lcConsoleAction = Last action }  -- | Adds sensible predefined set of parameters to logger. productionB :: LoggerConfig-productionB = showTimeB <> consoleOutB+productionB = showTimeB <> customConsoleActionB (Just defaultHandleAction)  -- | Setup 'lcMapper' inside 'LoggerConfig'. mapperB :: (LoggerName -> LoggerName) -> LoggerConfig
src/System/Wlog/Wrapper.hs view
@@ -23,7 +23,6 @@  import           Universum -import           Control.Concurrent.MVar    (withMVar) import           Data.Time                  (UTCTime) import           System.IO                  (Handle, stderr, stdout) @@ -38,12 +37,13 @@  -- | Like `streamHandler`, but syncronized using given `MVar` as lock -- (it should be filled before this function call).-streamHandlerWithLock :: MVar () -> Handle -> Severity -> IO (GenericHandler Handle)-streamHandlerWithLock lock h sev = do-    GenericHandler {..} <- streamHandler h sev-    pure-        GenericHandler-        {writeFunc = \a s -> withMVar lock $ const $ writeFunc a s, ..}+streamHandlerWithLock :: (Handle -> Text -> IO ())+                      -> MVar ()+                      -> Handle+                      -> Severity+                      -> IO (GenericHandler Handle)+streamHandlerWithLock customTerminalAction lock handle severity =+    streamHandler handle customTerminalAction lock severity  -- | This function initializes global logging system for terminal output. -- At high level, it sets severity which will be used by all loggers by default,@@ -63,21 +63,23 @@ -- `setSeverity` directly. initTerminalLogging :: MonadIO m                     => (UTCTime -> Text)+                    -> (Handle -> Text -> IO ())                     -> Bool  -- ^ Show time?                     -> Bool  -- ^ Show ThreadId?                     -> Maybe Severity                     -> m () initTerminalLogging     timeF+    customConsoleAction     isShowTime     isShowTid     (fromMaybe Warning -> defaultSeverity)   = liftIO $ do     lock <- liftIO $ newMVar ()     stdoutHandler <- setStdoutFormatter <$>-        streamHandlerWithLock lock stdout defaultSeverity+        streamHandlerWithLock customConsoleAction lock stdout defaultSeverity     stderrHandler <- setStderrFormatter <$>-        streamHandlerWithLock lock stderr Error+        streamHandlerWithLock customConsoleAction lock stderr Error     updateGlobalLogger rootLoggerName $         setHandlers [stderrHandler, stdoutHandler]     updateGlobalLogger rootLoggerName $