packages feed

log4hs 0.1.0.0 → 0.2.0.0

raw patch · 17 files changed

+146/−281 lines, 17 filesdep +vformatPVP ok

version bump matches the API change (PVP)

Dependencies added: vformat

API changes (from Hackage documentation)

- Logging.Aeson: instance Data.Aeson.Types.FromJSON.FromJSON (Data.Map.Internal.Map GHC.Base.String Logging.Types.Formatter.Formatter -> GHC.Types.IO Logging.Types.Class.Handler.SomeHandler)
- Logging.Aeson: instance Data.Aeson.Types.FromJSON.FromJSON Logging.Types.Formatter.Formatter
- Logging.Types: Formatter :: String -> String -> Formatter
- Logging.Types: [$sel:packagename:LogRecord] :: LogRecord -> String
- Logging.Types: [datefmt] :: Formatter -> String
- Logging.Types: [fmt] :: Formatter -> String
- Logging.Types: class Formattable a
- Logging.Types: data Formatter
- Logging.Types: flush :: Handler a => a -> IO ()
- Logging.Types: format :: Formattable a => a -> LogRecord -> String
- Logging.Types: formatTime :: Formattable a => a -> LogRecord -> String
+ Logging.Aeson: instance Data.Aeson.Types.FromJSON.FromJSON (Data.Map.Internal.Map GHC.Base.String Text.Format.Format.Format1 -> GHC.Types.IO Logging.Types.Class.Handler.SomeHandler)
+ Logging.Aeson: instance Data.Aeson.Types.FromJSON.FromJSON Text.Format.Format.Format1
+ Logging.Types: [$sel:asctime:LogRecord] :: LogRecord -> ZonedTime
+ Logging.Types: [$sel:msecs:LogRecord] :: LogRecord -> Integer
+ Logging.Types: [$sel:pathname:LogRecord] :: LogRecord -> String
+ Logging.Types: [$sel:pkgname:LogRecord] :: LogRecord -> String
+ Logging.Types: [$sel:utctime:LogRecord] :: LogRecord -> UTCTime
+ Logging.Utils: modifyBaseName :: FilePath -> (String -> String) -> FilePath
+ Logging.Utils: rotateFile :: FilePath -> FilePath -> IO ()
- Logging.Types: FileHandler :: IORef Handle -> FilePath -> TextEncoding -> Level -> Filterer -> Formatter -> FileHandler
+ Logging.Types: FileHandler :: Level -> Filterer -> Format1 -> FilePath -> TextEncoding -> IORef Handle -> FileHandler
- Logging.Types: LogRecord :: Logger -> Level -> String -> String -> String -> String -> Int -> ZonedTime -> LogRecord
+ Logging.Types: LogRecord :: Logger -> Level -> String -> String -> String -> String -> String -> Int -> ZonedTime -> UTCTime -> Double -> Integer -> LogRecord
- Logging.Types: StreamHandler :: Handle -> Level -> Filterer -> Formatter -> StreamHandler
+ Logging.Types: StreamHandler :: Level -> Filterer -> Format1 -> Handle -> StreamHandler
- Logging.Types: [$sel:created:LogRecord] :: LogRecord -> ZonedTime
+ Logging.Types: [$sel:created:LogRecord] :: LogRecord -> Double
- Logging.Types: [$sel:formatter:FileHandler] :: FileHandler -> Formatter
+ Logging.Types: [$sel:formatter:FileHandler] :: FileHandler -> Format1
- Logging.Types: [$sel:formatter:StreamHandler] :: StreamHandler -> Formatter
+ Logging.Types: [$sel:formatter:StreamHandler] :: StreamHandler -> Format1
- Logging.Types: class (HasType Level a, HasType Filterer a, HasType Formatter a, Typeable a, Eq a) => Handler a
+ Logging.Types: class (HasType Level a, HasType Filterer a, HasType Format1 a, Typeable a, Eq a) => Handler a

Files

bench/Main.hs view
@@ -104,9 +104,8 @@       }     },     "formatters": {-      "simple": "%(message)s",-      "normal": "%(asctime)s - %(level)s - %(logger)s] %(message)s",-      "full": "%(asctime)s - %(level)s - %(logger)s - %(pathname)s/%(filename)s:%(lineno)d] %(message)s"+      "simple": "{message}",+      "normal": "{asctime:%Y-%m-%dT%H:%M:%S%6Q%z} - {level} - {logger}] {message}",+      "full": "{asctime:%Y-%m-%dT%H:%M:%S%6Q%z} - {level} - {logger} - {pathname}:{lineno}] {message}"     }   }|]-
log4hs.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: a647fb460a908fafb35b6663c643cc259815b41a600dce95f217343328a72f6c+-- hash: 8d633b37fcc55dd17512ea7e3f2351a1e6b89b9fb459303951dd3039c1bb8bed  name:           log4hs-version:        0.1.0.0+version:        0.2.0.0 synopsis:       A python logging style log library description:    Please see the http://hackage.haskell.org/package/log4hs category:       logging@@ -32,10 +32,8 @@       Logging.TH       Logging.Types.Class       Logging.Types.Class.Filterable-      Logging.Types.Class.Formattable       Logging.Types.Class.Handler       Logging.Types.Filter-      Logging.Types.Formatter       Logging.Types.Handlers       Logging.Types.Handlers.FileHandler       Logging.Types.Handlers.StreamHandler@@ -60,6 +58,7 @@     , template-haskell >=2.0 && <3.0     , text >=1.2 && <2.0     , time >=1.4 && <2.0+    , vformat >=0.9.1 && <1.0   default-language: Haskell2010  test-suite log4hs-test@@ -91,6 +90,7 @@     , template-haskell >=2.0 && <3.0     , text >=1.2 && <2.0     , time >=1.4 && <2.0+    , vformat >=0.9.1 && <1.0   default-language: Haskell2010  benchmark log4hs-bench@@ -116,4 +116,5 @@     , template-haskell >=2.0 && <3.0     , text >=1.2 && <2.0     , time >=1.4 && <2.0+    , vformat >=0.9.1 && <1.0   default-language: Haskell2010
src/Logging/Aeson.hs view
@@ -1,3 +1,5 @@+{-# OPTIONS_HADDOCK prune, ignore-exports #-}+ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE OverloadedStrings     #-}@@ -19,9 +21,11 @@ import           Data.Generics.Product.Typed import           Data.IORef import qualified Data.Map.Lazy               as M+import           Data.String import           System.Directory import           System.FilePath import           System.IO+import           Text.Format  import           Logging.Internal import           Logging.Types@@ -38,7 +42,7 @@   {     \"loggers\": {\"root\": {}, \"MyLogger\": {}},     \"handlers\": {\"console\": {}, \"file\": {}},-    \"formatters\": {\"default\": {}, \"simple\": {}},+    \"formatters\": {\"default\": "format", \"simple\": "format"},     \"disabled\": false,     \"catchUncaughtException\": true   }@@ -51,24 +55,16 @@  So as handlers, sinks may share same handlers. -__Examples of 'Formatter' json__--@-  -- a standard format-  {-    \"fmt\": \"%(message)s\",-    \"datefmt\": \"%Y-%m-%dT%H:%M:%S\"-  }+__Examples of Formatter json__ -  -- missing field will use default value-  {-    \"fmt": "%(message)s\",-    \"datefmt\": \"%Y-%m-%dT%H:%M:%S\"-  }+See 'Text.Format' of vformat package for more information about formatting. -  -- it works as well, just a string-  \"%(message)s\" @+  "{message}"+  "{logger} {level}: {message}"+  "{logger:<20.20s} {level:<8s}: {message}"+  "{asctime:%Y-%m-%dT%H:%M:%S%6Q%z} - {level} - {logger}] {message}"+@  __Examples of 'Handler' json__ @@ -130,19 +126,16 @@   parseJSON v = Filter <$> parseJSON v  -instance FromJSON Formatter where-  parseJSON (Object v) = Formatter <$> v .:? "fmt" .!= (fmt def)-                                   <*> v .:? "datefmt" .!= (datefmt def)-  parseJSON (String v) = (\fmt -> def {fmt = fmt}) <$> parseJSON (String v)-  parseJSON invalid = typeMismatch "Object" invalid+instance FromJSON Format1 where+  parseJSON v = fromString <$> parseJSON v   instance FromJSON StreamHandler where   parseJSON = withObject "StreamHandler" $ \v ->-      StreamHandler <$> (parseStream <$> (v .:? "stream" .!= "stderr"))-                    <*> v .:? "level" .!= def+      StreamHandler <$> v .:? "level" .!= def                     <*> v .:? "filterer" .!= []-                    <*> v .:? "formatter" .!= def+                    <*> v .:? "formatter" .!= "{message}"+                    <*> (parseStream <$> (v .:? "stream" .!= "stderr"))     where       parseStream :: String -> Handle       parseStream "stderr" = stderr@@ -152,15 +145,15 @@  instance FromJSON (IO FileHandler) where   parseJSON = withObject "FileHandler" $ \v -> do-    file <- v .:? "file" .!= "log4hs.log"-    encoding <- v .:? "encoding" .!= (show utf8)     level <- v .:? "level" .!= def     filterer <- v .:? "filterer" .!= []-    formatter <- v .:? "formatter" .!= def+    formatter <- v .:? "formatter" .!= "{message}"+    file <- v .:? "file" .!= "log4hs.log"+    encoding <- v .:? "encoding" .!= (show utf8)     return $ do       stream <- newIORef undefined       encoding' <- mkTextEncoding encoding-      return $ FileHandler stream file encoding' level filterer formatter+      return $ FileHandler level filterer formatter file encoding' stream   instance FromJSON (IO SomeHandler) where@@ -175,12 +168,12 @@         return $ toHandler <$> (hdlIo :: IO FileHandler)  -instance FromJSON (M.Map String Formatter -> IO SomeHandler) where+instance FromJSON (M.Map String Format1 -> IO SomeHandler) where   parseJSON = withObject "Handler" $ \v -> do     hdlio <- parseJSON (Object v)-    key <- v .:? "formatter" .!= ""+    key <- v .:? "formatter" .!= "{message}"     return $ \fs -> hdlio >>= \hdl -> return $-      set (typed @Formatter) (M.findWithDefault def key fs) hdl+      set (typed @Format1) (M.findWithDefault "" key fs) hdl   instance FromJSON (Sink) where@@ -202,7 +195,7 @@                              }  -type Formatters = M.Map String Formatter+type Formatters = M.Map String Format1 type HandlersMakerIO = M.Map String (Formatters -> IO SomeHandler) type SinksMaker = M.Map String (String -> M.Map String SomeHandler -> Sink) 
src/Logging/Internal.hs view
@@ -22,13 +22,16 @@ import           Data.List                   (dropWhileEnd, group) import           Data.Map.Lazy               (elems, (!?)) import           Data.Time.Clock+import           Data.Time.Clock.POSIX import           Data.Time.LocalTime import           GHC.Conc                    (setUncaughtExceptionHandler) import           Prelude                     hiding (filter, log)+import           System.FilePath import           System.IO                   (stderr, stdout) import           System.IO.Unsafe            (unsafePerformIO)  import           Logging.Types+import           Logging.Utils  {-# NOINLINE _mgr #-} _mgr :: IORef Manager@@ -72,12 +75,18 @@      => Logger -> Level -> String -> (String, String, String, Int) -> m () log logger level message location = liftIO $ do     mgr@Manager{..} <- readIORef _mgr-    created <- getZonedTime+    asctime <- getZonedTime -    let (file, package, modulename, lineno) = location+    let (pathname, pkgname, modulename, lineno) = location+        filename = takeFileName pathname+        utctime = zonedTimeToUTC asctime+        diffTime = utcTimeToPOSIXSeconds utctime+        created = timestamp diffTime+        msecs = microseconds diffTime      when (not disabled) $ process logger mgr $-      LogRecord logger level message file package modulename lineno created+      LogRecord logger level message pathname filename pkgname modulename+                lineno asctime utctime created msecs   where     process :: Logger -> Manager -> LogRecord -> IO ()     process logger mgr rcd =@@ -115,11 +124,11 @@  -- |A 'StreamHandler' bound to 'stderr' stderrHandler :: StreamHandler-stderrHandler = StreamHandler stderr def [] def+stderrHandler = StreamHandler def [] "" stderr  -- |A 'StreamHandler' bound to 'stdout' stdoutHandler :: StreamHandler-stdoutHandler = StreamHandler stdout def [] def+stdoutHandler = StreamHandler def [] "" stdout  {-# NOINLINE defaultRoot #-} -- |Default root sink which is used by 'jsonToManager' when __root__ is missed.
src/Logging/Types.hs view
@@ -2,7 +2,6 @@   ( module Logging.Types.Logger   , module Logging.Types.Level   , module Logging.Types.Filter-  , module Logging.Types.Formatter   , module Logging.Types.Record   , module Logging.Types.Handlers   , module Logging.Types.Sink@@ -12,7 +11,6 @@  import           Logging.Types.Class import           Logging.Types.Filter-import           Logging.Types.Formatter import           Logging.Types.Handlers import           Logging.Types.Level import           Logging.Types.Logger
src/Logging/Types/Class.hs view
@@ -1,10 +1,8 @@ module Logging.Types.Class   ( module Logging.Types.Class.Filterable-  , module Logging.Types.Class.Formattable   , module Logging.Types.Class.Handler   ) where   import           Logging.Types.Class.Filterable-import           Logging.Types.Class.Formattable import           Logging.Types.Class.Handler
− src/Logging/Types/Class/Formattable.hs
@@ -1,9 +0,0 @@-module Logging.Types.Class.Formattable ( Formattable(..) ) where--import           Logging.Types.Record----- |A class represents a common trait of formatting 'LogRecord' as 'String'.-class Formattable a where-  format :: a -> LogRecord -> String-  formatTime :: a -> LogRecord -> String
src/Logging/Types/Class/Handler.hs view
@@ -11,10 +11,10 @@ import           Data.Generics.Product.Typed import           Data.Typeable import           Prelude                        hiding (filter)+import           Text.Format  import           Logging.Types.Class.Filterable import           Logging.Types.Filter-import           Logging.Types.Formatter import           Logging.Types.Level import           Logging.Types.Record @@ -32,9 +32,9 @@   getTyped (SomeHandler h) = view (typed @Filterer) h   setTyped v (SomeHandler h) = SomeHandler $ set (typed @Filterer) v h -instance {-# OVERLAPPING #-} HasType Formatter SomeHandler where-  getTyped (SomeHandler h) = view (typed @Formatter) h-  setTyped v (SomeHandler h) = SomeHandler $ set (typed @Formatter) v h+instance {-# OVERLAPPING #-} HasType Format1 SomeHandler where+  getTyped (SomeHandler h) = view (typed @Format1) h+  setTyped v (SomeHandler h) = SomeHandler $ set (typed @Format1) v h  instance Eq SomeHandler where   (SomeHandler h1) == h2 = Just h1 == fromHandler h2@@ -42,7 +42,6 @@ instance Handler SomeHandler where   open (SomeHandler h) = open h   emit (SomeHandler h) = emit h-  flush (SomeHandler h) = flush h   close (SomeHandler h) = close h   handle (SomeHandler h) = handle h   fromHandler = Just . id@@ -55,7 +54,7 @@ -- handle operations. class ( HasType Level a       , HasType Filterer a-      , HasType Formatter a+      , HasType Format1 a       , Typeable a       , Eq a       ) => Handler a where@@ -63,9 +62,6 @@   open _ = return ()    emit :: a -> LogRecord -> IO ()--  flush :: a -> IO ()-  flush _ = return ()    close :: a -> IO ()   close _ = return ()
− src/Logging/Types/Formatter.hs
@@ -1,79 +0,0 @@-{-# LANGUAGE RecordWildCards #-}--module Logging.Types.Formatter ( Formatter(..) ) where--import           Data.Default-import qualified Data.Time.Format                as TF-import           System.FilePath-import           Text.Printf--import           Logging.Types.Class.Formattable-import           Logging.Types.Record-import           Logging.Utils----- |'Formatter's are used to convert a LogRecord to text.------ 'Formatter's need to know how a 'LogRecord' is constructed. They are--- responsible for converting a 'LogRecord' to (usually) a string which can--- be interpreted by either a human or an external system. The base 'Formatter'--- allows a formatting string to be specified. If none is supplied, the--- default value, "%(message)s" is used.--------- The 'Formatter' can be initialized with a format string which makes use of--- knowledge of the 'LogRecord' attributes - e.g. the default value mentioned--- above makes use of a 'LogRecord''s message attribute. Currently, the useful--- attributes in a 'LogRecord' are described by:------ [@%(logger)s@]     Name of the logger (logging channel)--- [@%(level)s@]      Numeric logging level for the message (DEBUG, INFO, WARN,---                    ERROR, FATAL, LEVEL v)--- [@%(pathname)s@]   Full pathname of the source file where the logging---                    call was issued (if available)--- [@%(filename)s@]   Filename portion of pathname--- [@%(module)s@]     Module (name portion of filename)--- [@%(lineno)d@]     Source line number where the logging call was issued---                    (if available)--- [@%(created)f@]    Time when the LogRecord was created (picoseconds---                    since '1970-01-01 00:00:00')--- [@%(asctime)s@]    Textual time when the 'LogRecord' was created--- [@%(msecs)d@]      Millisecond portion of the creation time--- [@%(message)s@]    The main message passed to 'logv' 'debug' 'info' ..----data Formatter = Formatter { fmt     :: String-                           , datefmt :: String -- ^ see "Data.Time.Format"-                           } deriving (Eq)--instance Default Formatter where-  def = Formatter "%(message)s" "%Y-%m-%dT%H:%M:%S%6Q%z"--instance Formattable Formatter where-  format f@Formatter{..} rcd@LogRecord{..} = formats fmt-    where-      diffTime = zonedTimeToPOSIXSeconds created--      formats :: String -> String-      formats ('%':'%':cs) = ('%' :) $ formats cs-      formats ('%':'(':cs) =-        case break (== ')') cs of-          (attr, ')':c:cs') -> (formatAttr attr c) ++ (formats cs')-          _ -> error "Logging.Types.Formattable: no parse (Formatter)"-      formats (c:cs) = (c :) $ formats cs-      formats ""           = ""--      formatAttr :: String -> Char -> String-      formatAttr "logger" fc   = printf ['%', fc] logger -- %(logger)s-      formatAttr "level" fc    = printf ['%', fc] $ show level -- %(level)s-      formatAttr "pathname" fc = printf ['%', fc] $ takeDirectory filename -- %(pathname)s-      formatAttr "filename" fc = printf ['%', fc] $ takeFileName filename -- %(filename)s-      formatAttr "module" fc   = printf ['%', fc] modulename -- %(module)s-      formatAttr "lineno" fc   = printf ['%', fc] lineno -- %(lineno)d-      formatAttr "created" fc  = printf ['%', fc] $ timestamp diffTime -- %(created)f-      formatAttr "asctime" fc  = printf ['%', fc] $ formatTime f rcd -- %(asctime)s-      formatAttr "msecs" fc    = printf ['%', fc] $ microseconds diffTime -- %(msecs)d-      formatAttr "message" fc  = printf ['%', fc] message -- %(message)s-      formatAttr _ _           = "unknown"--  formatTime Formatter{..} LogRecord{..} =-    TF.formatTime TF.defaultTimeLocale datefmt created
src/Logging/Types/Handlers/FileHandler.hs view
@@ -9,10 +9,10 @@ import           Data.IORef import           GHC.Generics import           System.IO+import           Text.Format  import           Logging.Types.Class import           Logging.Types.Filter-import           Logging.Types.Formatter import           Logging.Types.Level import           Logging.Utils import           System.IO.Extra@@ -21,21 +21,20 @@ -- | A handler type which writes logging records, appropriately formatted, -- to a file. ---data FileHandler = FileHandler { stream    :: IORef Handle+data FileHandler = FileHandler { level     :: Level+                               , filterer  :: Filterer+                               , formatter :: Format1                                , file      :: FilePath                                , encoding  :: TextEncoding-                               , level     :: Level-                               , filterer  :: Filterer-                               , formatter :: Formatter+                               , stream    :: IORef Handle                                } deriving (Generic, Eq)  instance Handler FileHandler where   open FileHandler{..} = atomicWriteIORef stream =<< openLogFile file encoding    emit self@FileHandler{..} rcd = do-    flip hPutStrLn (format formatter rcd) =<< readIORef stream-    flush self--  flush FileHandler{..}= hFlush =<< readIORef stream+    stream' <- readIORef stream+    flip hPutStrLn (format1 formatter rcd) stream'+    hFlush stream'    close FileHandler{..} = hClose =<< readIORef stream
src/Logging/Types/Handlers/StreamHandler.hs view
@@ -4,28 +4,26 @@  module Logging.Types.Handlers.StreamHandler ( StreamHandler(..) ) where -import           Control.Monad           (unless)+import           Control.Monad        (unless) import           GHC.Generics import           System.IO+import           Text.Format  import           Logging.Types.Class import           Logging.Types.Filter-import           Logging.Types.Formatter import           Logging.Types.Level   -- | A handler type which writes logging records, appropriately formatted, -- to a stream. ---data StreamHandler = StreamHandler { stream    :: Handle-                                   , level     :: Level+data StreamHandler = StreamHandler { level     :: Level                                    , filterer  :: Filterer-                                   , formatter :: Formatter+                                   , formatter :: Format1+                                   , stream    :: Handle                                    } deriving (Generic, Eq)  instance Handler StreamHandler where   emit hdl@StreamHandler{..} rcd = do-    hPutStrLn stream $ format formatter rcd-    flush hdl--  flush = hFlush . stream+    hPutStrLn stream $ format1 formatter rcd+    hFlush stream
src/Logging/Types/Level.hs view
@@ -5,6 +5,7 @@ import           Data.Default import           Data.List    (stripPrefix) import           Data.String+import           Text.Format   -- |'Level' also known as severity, a higher 'Level' means a bigger 'Int'.@@ -24,6 +25,9 @@ -- True -- newtype Level = Level Int deriving (Eq, Ord)++instance FormatArg Level where+  formatArg = formatArg . show  instance Show Level where   show (Level 0)  = "NOTSET"
src/Logging/Types/Record.hs view
@@ -1,8 +1,12 @@+{-# LANGUAGE DeriveGeneric         #-} {-# LANGUAGE DuplicateRecordFields #-}  module Logging.Types.Record ( LogRecord(..) ) where +import           Data.Time.Clock import           Data.Time.LocalTime+import           GHC.Generics+import           Text.Format  import           Logging.Types.Level import           Logging.Types.Logger@@ -16,12 +20,47 @@ -- It includes the main message as well as information such as -- when the record was created, the source line where the logging call was made. ---data LogRecord = LogRecord { logger      :: Logger-                           , level       :: Level-                           , message     :: String-                           , filename    :: String-                           , packagename :: String-                           , modulename  :: String-                           , lineno      :: Int-                           , created     :: ZonedTime-                           }+-- 'LogRecord' can be formatted into string by 'Text.Format'+-- from 'vformat' package, see 'Text.Format.format1' for more information.+--+-- Currently, the useful attributes in a LogRecord are described by:+--+-- @+--  logger        name of the logger, see 'Logger'+--  level         logging level for the message, see 'Level'+--  message       the main message passed to logv debug info ..+--  pathname      full pathname of the source file where the logging call was issued (if available)+--  filename      filename portion of pathname+--  pkgname       package name where the logging call was issued (if available)+--  modulename    module name (e.g. Main, Logging.Types)+--  lineno        source line number where the logging call was issued (if available)+--  asctime       'ZonedTime' when the LogRecord was created+--  utctime       'UTCTime' when the LogRecord was created+--  created       timestamp when the LogRecord was created+--  msecs         millisecond portion of the creation time+-- @+--+-- Format examples:+--+-- @+--  "{message}"+--  "{logger} {level}: {message}"+--  "{logger:<20.20s} {level:<8s}: {message}"+--  "{asctime:%Y-%m-%dT%H:%M:%S%6Q%z} - {level} - {logger}] {message}"+-- @+--+data LogRecord = LogRecord { logger     :: Logger+                           , level      :: Level+                           , message    :: String+                           , pathname   :: String+                           , filename   :: String+                           , pkgname    :: String+                           , modulename :: String+                           , lineno     :: Int+                           , asctime    :: ZonedTime+                           , utctime    :: UTCTime+                           , created    :: Double+                           , msecs      :: Integer+                           } deriving Generic++instance FormatArg LogRecord
src/Logging/Utils.hs view
@@ -7,8 +7,11 @@   , milliseconds   , microseconds   , openLogFile+  , rotateFile+  , modifyBaseName   ) where +import           Control.Monad import           Data.Time.Clock import           Data.Time.Clock.POSIX import           Data.Time.LocalTime@@ -57,3 +60,11 @@   stream <- openFile file AppendMode   hSetEncoding stream encoding   return stream+++rotateFile :: FilePath -> FilePath -> IO ()+rotateFile src dest = doesFileExist src >>= (flip when $ renameFile src dest)+++modifyBaseName :: FilePath -> (String -> String) -> FilePath+modifyBaseName file modify = replaceBaseName file $ modify $ takeBaseName file
test/Logging/AesonSpec.hs view
@@ -24,13 +24,13 @@ import           System.IO.Unsafe import           Test.Hspec import           Test.Hspec.QuickCheck+import           Text.Format  import           Logging import           Logging.Aeson  spec :: Spec-spec = levelSpec >> filterSpec >> formatterSpec >> handlerSpec >>-       sinkSpec >>  managerSpec+spec = levelSpec >> filterSpec >> handlerSpec >> sinkSpec >> managerSpec   levelSpec :: Spec@@ -45,19 +45,6 @@     in (decode $ encode logger) == Just (Filter logger)  -formatterSpec :: Spec-formatterSpec = describe "Formatter" $ modifyMaxSize (const 1000) $ do-  prop "decode standard" $ \(fmt, datefmt) ->-    let formatter = Formatter fmt datefmt-        obj = object [("fmt", toJSON fmt), ("datefmt", toJSON datefmt)]-    in (decode $ encode obj) == Just formatter--  prop "decode missing filed" $ \fmt ->-    decode (encode $ object [("fmt", toJSON fmt)]) == Just (def {fmt = fmt})--  prop "decode string" $ \fmt -> decode (encode fmt) == Just (def {fmt = fmt})-- handlerSpec :: Spec handlerSpec = describe "Handler" $ modifyMaxSize (const 1000) $ do   it "decode StreamHandler simple" $ do@@ -66,7 +53,7 @@     stream == stderr `shouldBe` True     level == def `shouldBe` True     filterer == [] `shouldBe` True-    formatter == def `shouldBe` True+    formatter == "{message}" `shouldBe` True    it "decode StreamHandler standard" $ do     let StreamHandler{..} = fromJust $ decode $ encode $@@ -83,7 +70,7 @@     stream == stdout `shouldBe` True     level == "DEBUG" `shouldBe` True     filterer == [Filter "Module.Submodule"] `shouldBe` True-    formatter == def {fmt = "default"} `shouldBe` True+    formatter == "default" `shouldBe` True    it "decode FileHandler" $ do     FileHandler{..} <- fromJust $ decode $ encode $@@ -102,10 +89,10 @@     encoding `shouldBe` utf16     level `shouldBe` "INFO"     filterer == [Filter "Module.Submodule"] `shouldBe` True-    formatter == def {fmt = "default"} `shouldBe` True+    formatter == "default" `shouldBe` True    it "decode (formatters -> SomeHandler)" $ do-    let simple = def {fmt = "%(logger)s: %(message)s"}+    let simple = "{logger}: {message}"         func = fromJust $ decode $ encode           [aesonQQ|             {@@ -119,7 +106,7 @@     handler@(SomeHandler _) <- func $ M.singleton ("simple" :: String) simple     view (typed @Level) handler == "DEBUG" `shouldBe` True     view (typed @Filterer) handler == ["Module.Submodule"] `shouldBe` True-    view (typed @Formatter) handler == simple `shouldBe` True+    view (typed @Format1) handler == simple `shouldBe` True   sinkSpec :: Spec@@ -230,8 +217,6 @@       }     },     "formatters": {-      "default": {-        "fmt": "%(asctime)s - %(level)s - %(logger)s:%(lineno)d] %(message)s"-      }+      "default": "{asctime} - {level} - {logger}:{lineno}] {message}"     } }|]
test/Logging/TypesSpec.hs view
@@ -3,97 +3,19 @@  module Logging.TypesSpec ( spec ) where -import           Data.Default import           Data.String           (fromString)-import           Data.Time.Clock-import           Data.Time.Format      as TF-import           Data.Time.LocalTime-import           System.FilePath import           Test.Hspec import           Test.Hspec.QuickCheck import           Test.QuickCheck-import           Text.Printf  import           Logging.Types import           Logging.Utils  spec :: Spec-spec = levelSpec >> formatterSpec+spec = levelSpec   levelSpec :: Spec levelSpec = describe "Level" $ modifyMaxSize (const 1000) $ do   prop "read and show" $ \x -> (read . show) (Level x) == (Level x)   prop "overload string" $ \x -> fromString (show (Level x)) == (Level x)---formatterSpec :: Spec-formatterSpec = describe "Formatter" $ modifyMaxSize (const 1000) $ do-    -- one field only-    fmtProp "%(logger)s" "LogRecord's logger record" loggerFmt-    fmtProp "%(level)s" "LogRecord's level record" levelFmt-    fmtProp "%(message)s" "LogRecord's message record" message-    fmtProp "%(pathname)s" "LogRecord's filename (dir) record" pathnameFmt-    fmtProp "%(filename)s" "LogRecord's filename (no dir) record" filenameFmt-    fmtProp "%(module)s" "LogRecord's modulename record" modulename-    fmtProp "%(lineno)d" "LogRecord's lineno record" $ show . lineno-    fmtProp "%(created)f"-          "LogRecord's created (decimal second timestamp) record"-          createdFmt-    fmtProp "%(msecs)d"-          "LogRecord's created (millisecond timestamp) record"-          msecsFmt-    dateProp "%(asctime)s" "%Y-%m-%dT%H:%M:%S"-              "LogRecord's created (human friendly) record"-              asctimeFmt-    -- mixed fields-    dateProp-      "%(asctime)s - %(level)s - %(logger)s" "%Y-%m-%dT%H:%M:%S"-      "LogRecord's created (human friendly) - level - logger records"-      asctimeLevelLoggerFmt-    fmtProp "%(pathname)s/%(filename)s" "LogRecord's filename (/ split) record"-      pathnameFilenameFmt-    fmtProp "%(lineno)d] %(message)s" "LogRecord's lineno] message records"-      linenoMessageFmt-  where-    fmtProp :: String -> String -> (LogRecord -> String)-          -> SpecWith (Arg Property)-    fmtProp fmt desc manualFmt = prop (printf "format %s to %s" fmt desc) $-      \rcd -> format (def {fmt = fmt}) rcd == manualFmt rcd--    dateProp :: String -> String -> String -> (LogRecord -> String)-              -> SpecWith (Arg Property)-    dateProp fmt datefmt desc manualFmt =-      prop (printf "format %s(%s) to %s" fmt datefmt desc) $-        \rcd -> format (Formatter fmt datefmt) rcd == manualFmt rcd--    loggerFmt = \LogRecord{..} -> logger-    levelFmt = \LogRecord{..} -> show level-    pathnameFmt = takeDirectory . filename-    filenameFmt = takeFileName . filename-    createdFmt = (printf "%f") . timestamp . zonedTimeToPOSIXSeconds . created-    msecsFmt = show . milliseconds . zonedTimeToPOSIXSeconds . created-    asctimeFmt = TF.formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S" . created-    asctimeLevelLoggerFmt rcd =-      asctimeFmt rcd ++ " - " ++ (levelFmt rcd) ++ " - " ++(loggerFmt rcd)-    pathnameFilenameFmt rcd = pathnameFmt rcd ++ "/" ++ (filenameFmt rcd)-    linenoMessageFmt rcd = (show $ lineno rcd) ++ "] " ++ (message rcd)---deriving instance Show LogRecord---instance Arbitrary LogRecord where-  arbitrary = LogRecord-           <$> arbitrary-           <*> (Level <$> arbitrary)-           <*> arbitrary-           <*> arbitrary-           <*> arbitrary-           <*> arbitrary-           <*> arbitrary-           <*> (((`addZonedTime` zeroTime) . toEnum) <$> arbitrary)---zeroTime :: ZonedTime-zeroTime = read "1970-01-01 00:00:00"
test/LoggingSpec.hs view
@@ -18,6 +18,7 @@ import           Test.Hspec.QuickCheck import           Test.QuickCheck         hiding (run) import qualified Test.QuickCheck.Monadic as Q+import           Text.Format  import           Logging @@ -134,20 +135,20 @@   return (Manager root sinks disabled False, streams)  -formatters :: M.Map String Formatter+formatters :: M.Map String Format1 formatters = M.fromList-  [ ("simple", def {fmt = "%(message)s"})-  , ("logger", def {fmt = "%(logger)s: %(message)s"})-  , ("level", def {fmt = "%(logger)s %(level)s] %(message)s"})+  [ ("simple", "{message}")+  , ("logger", "{logger}: {message}")+  , ("level", "{logger} {level}] {message}")   ]  -createPipeHandler :: Level -> Filterer -> Formatter -> IO (SomeHandler, Handle)+createPipeHandler :: Level -> Filterer -> Format1 -> IO (SomeHandler, Handle) createPipeHandler level filterer formatter = do   (read, write) <- createPipe   hSetEncoding read utf8   hSetEncoding write utf8-  return $ ( toHandler $ StreamHandler write level filterer formatter+  return $ ( toHandler $ StreamHandler level filterer formatter write            , read            )