packages feed

log4hs 0.8.0.0 → 0.9.0.0

raw patch · 21 files changed

+291/−283 lines, 21 filesdep +aeson-defaultdep +microlensdep −data-defaultdep −lensPVP ok

version bump matches the API change (PVP)

Dependencies added: aeson-default, microlens

Dependencies removed: data-default, lens

API changes (from Hackage documentation)

- Logging.Config.Type: data Config
- Logging.Config.Type: data Handler
- Logging.Config.Type: data Sink
- Logging.Config.Type: instance Data.Aeson.Types.FromJSON.FromJSON Logging.Config.Type.Config
- Logging.Config.Type: instance Data.Aeson.Types.FromJSON.FromJSON Logging.Config.Type.Handler
- Logging.Config.Type: instance Data.Aeson.Types.FromJSON.FromJSON Logging.Config.Type.Sink
- Logging.Config.Type: instance GHC.Generics.Generic Logging.Config.Type.Config
- Logging.Config.Type: instance GHC.Generics.Generic Logging.Config.Type.Handler
- Logging.Config.Type: instance GHC.Generics.Generic Logging.Config.Type.Sink
- Logging.Config.Type: instance GHC.Show.Show Logging.Config.Type.Config
- Logging.Config.Type: instance GHC.Show.Show Logging.Config.Type.Handler
- Logging.Config.Type: instance GHC.Show.Show Logging.Config.Type.Sink
- Logging.Level: instance Data.Default.Class.Default Logging.Level.Level
+ Logging.Config.Type: data ConfigH f
+ Logging.Config.Type: data HandlerH f
+ Logging.Config.Type: data SinkH f
+ Logging.Config.Type: instance Data.Aeson.Default.Class.Default Logging.Config.Type.ConfigH
+ Logging.Config.Type: instance Data.Aeson.Default.Class.Default Logging.Config.Type.HandlerH
+ Logging.Config.Type: instance Data.Aeson.Default.Class.Default Logging.Config.Type.SinkH
+ Logging.Config.Type: instance Data.Aeson.Types.FromJSON.FromJSON (Logging.Config.Type.ConfigH GHC.Maybe.Maybe)
+ Logging.Config.Type: instance Data.Aeson.Types.FromJSON.FromJSON (Logging.Config.Type.HandlerH GHC.Maybe.Maybe)
+ Logging.Config.Type: instance Data.Aeson.Types.FromJSON.FromJSON (Logging.Config.Type.SinkH GHC.Maybe.Maybe)
+ Logging.Config.Type: instance GHC.Generics.Generic (Logging.Config.Type.ConfigH f)
+ Logging.Config.Type: instance GHC.Generics.Generic (Logging.Config.Type.HandlerH f)
+ Logging.Config.Type: instance GHC.Generics.Generic (Logging.Config.Type.SinkH f)
+ Logging.Config.Type: type Config = ConfigH Identity
+ Logging.Config.Type: type Handler = HandlerH Identity
+ Logging.Config.Type: type Sink = SinkH Identity
+ Logging.Filter: instance Data.Aeson.Types.FromJSON.FromJSON Logging.Filter.Filter
+ Logging.Handler.TimeRotatingFileHandler: instance Data.Aeson.Types.FromJSON.FromJSON Logging.Handler.TimeRotatingFileHandler.RotateTime
+ Logging.Level: instance Data.Aeson.Types.FromJSON.FromJSON Logging.Level.Level
+ Logging.Prelude: mkStdHandle :: String -> Handle
- Logging.Config.Type: Config :: Maybe (Map String Sink) -> Maybe (Map String Handler) -> Maybe (Map String String) -> Maybe String -> Maybe Bool -> Config
+ Logging.Config.Type: Config :: HKD (MapH String SinkH f) f -> HKD (MapH String HandlerH f) f -> HKD (Map String String) f -> Maybe String -> HKD Bool f -> ConfigH f
- Logging.Config.Type: FileHandler :: Maybe String -> Maybe [String] -> Maybe String -> FilePath -> Maybe String -> Handler
+ Logging.Config.Type: FileHandler :: HKD Level f -> HKD Filterer f -> HKD String f -> FilePath -> HKD String f -> HandlerH f
- Logging.Config.Type: RotatingFileHandler :: Maybe String -> Maybe [String] -> Maybe String -> FilePath -> Maybe String -> Maybe Int -> Maybe Int -> Handler
+ Logging.Config.Type: RotatingFileHandler :: HKD Level f -> HKD Filterer f -> HKD String f -> FilePath -> HKD String f -> HKD Int f -> HKD Int f -> HandlerH f
- Logging.Config.Type: Sink :: Maybe String -> Maybe [String] -> Maybe [String] -> Maybe Bool -> Maybe Bool -> Sink
+ Logging.Config.Type: Sink :: HKD Level f -> HKD Filterer f -> HKD [String] f -> HKD Bool f -> HKD Bool f -> SinkH f
- Logging.Config.Type: StreamHandler :: Maybe String -> Maybe [String] -> Maybe String -> Maybe String -> Handler
+ Logging.Config.Type: StreamHandler :: HKD Level f -> HKD Filterer f -> HKD String f -> HKD String f -> HandlerH f
- Logging.Config.Type: TimeRotatingFileHandler :: Maybe String -> Maybe [String] -> Maybe String -> FilePath -> Maybe String -> Maybe String -> Maybe String -> Maybe Int -> Handler
+ Logging.Config.Type: TimeRotatingFileHandler :: HKD Level f -> HKD Filterer f -> HKD String f -> FilePath -> HKD String f -> Maybe String -> HKD RotateTime f -> HKD Int f -> HandlerH f
- Logging.Config.Type: [$sel:backupCount:StreamHandler] :: Handler -> Maybe Int
+ Logging.Config.Type: [$sel:backupCount:StreamHandler] :: HandlerH f -> HKD Int f
- Logging.Config.Type: [$sel:disabled:Config] :: Config -> Maybe Bool
+ Logging.Config.Type: [$sel:disabled:Config] :: ConfigH f -> HKD Bool f
- Logging.Config.Type: [$sel:disabled:Sink] :: Sink -> Maybe Bool
+ Logging.Config.Type: [$sel:disabled:Sink] :: SinkH f -> HKD Bool f
- Logging.Config.Type: [$sel:encoding:StreamHandler] :: Handler -> Maybe String
+ Logging.Config.Type: [$sel:encoding:StreamHandler] :: HandlerH f -> HKD String f
- Logging.Config.Type: [$sel:file:StreamHandler] :: Handler -> FilePath
+ Logging.Config.Type: [$sel:file:StreamHandler] :: HandlerH f -> FilePath
- Logging.Config.Type: [$sel:filterer:Sink] :: Sink -> Maybe [String]
+ Logging.Config.Type: [$sel:filterer:Sink] :: SinkH f -> HKD Filterer f
- Logging.Config.Type: [$sel:filterer:StreamHandler] :: Handler -> Maybe [String]
+ Logging.Config.Type: [$sel:filterer:StreamHandler] :: HandlerH f -> HKD Filterer f
- Logging.Config.Type: [$sel:formatter:StreamHandler] :: Handler -> Maybe String
+ Logging.Config.Type: [$sel:formatter:StreamHandler] :: HandlerH f -> HKD String f
- Logging.Config.Type: [$sel:formatters:Config] :: Config -> Maybe (Map String String)
+ Logging.Config.Type: [$sel:formatters:Config] :: ConfigH f -> HKD (Map String String) f
- Logging.Config.Type: [$sel:handlers:Config] :: Config -> Maybe (Map String Handler)
+ Logging.Config.Type: [$sel:handlers:Config] :: ConfigH f -> HKD (MapH String HandlerH f) f
- Logging.Config.Type: [$sel:handlers:Sink] :: Sink -> Maybe [String]
+ Logging.Config.Type: [$sel:handlers:Sink] :: SinkH f -> HKD [String] f
- Logging.Config.Type: [$sel:level:Sink] :: Sink -> Maybe String
+ Logging.Config.Type: [$sel:level:Sink] :: SinkH f -> HKD Level f
- Logging.Config.Type: [$sel:level:StreamHandler] :: Handler -> Maybe String
+ Logging.Config.Type: [$sel:level:StreamHandler] :: HandlerH f -> HKD Level f
- Logging.Config.Type: [$sel:maxBytes:StreamHandler] :: Handler -> Maybe Int
+ Logging.Config.Type: [$sel:maxBytes:StreamHandler] :: HandlerH f -> HKD Int f
- Logging.Config.Type: [$sel:propagate:Sink] :: Sink -> Maybe Bool
+ Logging.Config.Type: [$sel:propagate:Sink] :: SinkH f -> HKD Bool f
- Logging.Config.Type: [$sel:rotateTime:StreamHandler] :: Handler -> Maybe String
+ Logging.Config.Type: [$sel:rotateTime:StreamHandler] :: HandlerH f -> HKD RotateTime f
- Logging.Config.Type: [$sel:sinks:Config] :: Config -> Maybe (Map String Sink)
+ Logging.Config.Type: [$sel:sinks:Config] :: ConfigH f -> HKD (MapH String SinkH f) f
- Logging.Config.Type: [$sel:stream:StreamHandler] :: Handler -> Maybe String
+ Logging.Config.Type: [$sel:stream:StreamHandler] :: HandlerH f -> HKD String f
- Logging.Config.Type: [$sel:timezone:Config] :: Config -> Maybe String
+ Logging.Config.Type: [$sel:timezone:Config] :: ConfigH f -> Maybe String
- Logging.Config.Type: [$sel:timezone:StreamHandler] :: Handler -> Maybe String
+ Logging.Config.Type: [$sel:timezone:StreamHandler] :: HandlerH f -> Maybe String

Files

ChangeLog.md view
@@ -1,5 +1,11 @@ # Changelog for log4hs +## V0.9.0+- Fix log `UserInterrupt` issue+- Improve config datatypes+- Improve document and deprecate `Logging` module+- Replace lens with macrolens and remove `data-default`+ ## V0.8.0 - Add `IsMessage` class - Deprecate `catchUncaughtException`
README.md view
@@ -1,13 +1,38 @@ # A python logging style log library.  - ## Documentation -See [hackage](http://hackage.haskell.org/package/log4hs)+Please See http://hackage.haskell.org/package/log4hs  +## Example+```haskell+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-} +module Main ( main ) where++import           Logging.Config.Json (getManager)+import           Logging.Global      (run)+import           Logging.Global.TH   (debug, error, fatal, info, logv, warn)+import           Prelude             hiding (error)++main :: IO ()+main = getManager "{}" >>= flip run app++logger = "Main"++app :: IO ()+app = do+  $(debug) logger "this is a test message"+  $(info) logger "this is a test message"+  $(warn) logger "this is a test message"+  $(error) logger "this is a test message"+  $(fatal) logger "this is a test message"+  $(logv) logger "LEVEL 100" "this is a test message"+```+ ## Contributing  The source code will be opened to public after version 1.0.0 released. So, please wait.@@ -16,7 +41,7 @@  ## Issue Reporting -Please send email to log4hs@version.cloud for now.+Please send email to jorah@version.cloud for now.   
bench/Main.hs view
@@ -16,8 +16,9 @@ import           Data.Maybe import           System.IO.Unsafe -import           Logging import           Logging.Config.Json+import           Logging.Global+import           Logging.Manager   main :: IO ()
log4hs.cabal view
@@ -4,14 +4,23 @@ -- -- see: https://github.com/sol/hpack ----- hash: 8762d58dd571f8027cd0efeab16d060754fa1b0c76d87bd50f61fac31e7f116a+-- hash: 54029a04d065d8d1436594a85e50470a60c6e1632498264c0264328308d53be7  name:           log4hs-version:        0.8.0.0+version:        0.9.0.0 synopsis:       A python logging style log library-description:    Please see the http://hackage.haskell.org/package/log4hs-category:       logging-maintainer:     Jorah Gao <log4hs@version.cloud>+description:    @log4hs@ aims at providing a rich featured logging library for Haskell,+                just like @logging@ for Python and @log4j@ for Java.+                .+                .+                To get started, see the sample example in @README.md@ and document in+                hackage.+                .+                .+                It's still under development, so be carefull when using it in production+                environment.+category:       logging, log, library+maintainer:     Jorah Gao <jorah@version.cloud> copyright:      (c) 2019 Version Cloud license:        BSD3 license-file:   LICENSE@@ -44,9 +53,6 @@       Logging.Prelude       Logging.Record       Logging.Sink-      Logging.TH-      Logging.Types-      Logging.Utils   other-modules:       Logging.Class.Filterable       Logging.Class.Handler@@ -58,14 +64,14 @@       src   build-depends:       aeson >=1.2 && <2.0+    , aeson-default >=0.9 && <1.0     , base >=4.7 && <5     , bytestring >=0.10 && <1.0     , containers >=0.5.10 && <1.0-    , data-default >=0.5 && <1.0     , directory >=1.2 && <1.4     , filepath >=1.3 && <1.5     , generic-lens >=0.5 && <2.0-    , lens >=4.15 && <5.0+    , microlens >=0.4 && <1.0     , mtl >=2.2 && <3.0     , template-haskell >=2.0 && <3.0     , text >=1.2 && <2.0@@ -91,18 +97,18 @@   build-depends:       QuickCheck >=2.0 && <3.0     , aeson >=1.2 && <2.0+    , aeson-default >=0.9 && <1.0     , aeson-qq >=0.8 && <1.0     , base >=4.7 && <5     , bytestring >=0.10 && <1.0     , containers >=0.5.10 && <1.0-    , data-default >=0.5 && <1.0     , directory >=1.2 && <1.4     , filepath >=1.3 && <1.5     , generic-lens >=0.5 && <2.0     , hspec >=2.1 && <3.0     , hspec-core >=2.1 && <3.0-    , lens >=4.15 && <5.0     , log4hs+    , microlens >=0.4 && <1.0     , mtl >=2.2 && <3.0     , process >=1.2 && <2.0     , template-haskell >=2.0 && <3.0@@ -124,17 +130,17 @@   ghc-options: -threaded -rtsopts -with-rtsopts=-N   build-depends:       aeson >=1.2 && <2.0+    , aeson-default >=0.9 && <1.0     , aeson-qq >=0.8 && <1.0     , base >=4.7 && <5     , bytestring >=0.10 && <1.0     , containers >=0.5.10 && <1.0     , criterion >=1.0 && <2.0-    , data-default >=0.5 && <1.0     , directory >=1.2 && <1.4     , filepath >=1.3 && <1.5     , generic-lens >=0.5 && <2.0-    , lens >=4.15 && <5.0     , log4hs+    , microlens >=0.4 && <1.0     , mtl >=2.2 && <3.0     , template-haskell >=2.0 && <3.0     , text >=1.2 && <2.0
src/Logging.hs view
@@ -2,49 +2,12 @@ Module          : Logging Copyright       : (c) 2019 Version Cloud License         : BSD3-Maintainer      : Jorah Gao <log4hs@version.cloud>+Maintainer      : Jorah Gao <jorah@version.cloud> Stability       : experimental Portability     : portable--= A python logging style log library.--=== A simple example:--@-  \{\-\# LANGUAGE OverloadedStrings \#\-\}-  \{\-\# LANGUAGE TemplateHaskell   \#\-\}--  module Main ( main ) where--  import           Logging.Config.Json (getManager)-  import           Logging.Global      (run)-  import           Logging.Global.TH   (debug, error, fatal, info, logv, warn)-  import           Prelude             hiding (error)--  main :: IO ()-  main = getManager "{}" >>= flip run app--  logger = \"Main\"--  app :: IO ()-  app = do-    $(debug) logger \"this is a test message\"-    $(info) logger \"this is a test message\"-    $(warn) logger \"this is a test message\"-    $(error) logger \"this is a test message\"-    $(fatal) logger \"this is a test message\"-    $(logv) logger \"LEVEL 100\" \"this is a test message\"-@--See "Logging.Config.Json" and "Logging.Config.Yaml" to lean more about-decoding 'Manager' from json or yaml -}-module Logging-  ( module Logging.Types-  , module Logging.TH-  , module Logging.Global+module Logging {-# Deprecated "Use submodules directly" #-}+  ( module Logging.Global   ) where  import           Logging.Global-import           Logging.TH-import           Logging.Types
src/Logging/Class/Handler.hs view
@@ -7,10 +7,11 @@ module Logging.Class.Handler ( SomeHandler(..), Handler(..) ) where  import           Control.Exception           (SomeException, catch)-import           Control.Lens                (set, view) import           Control.Monad               (when) import           Data.Generics.Product.Typed import           Data.Typeable+import           Lens.Micro                  (set)+import           Lens.Micro.Extras           (view) import           Prelude                     hiding (filter) import           Text.Format 
src/Logging/Config/Json.hs view
@@ -1,32 +1,9 @@-{-| This module provides a convenient way to decode a 'Manager' from json string--There is a 'Config' type in "Logging.Config.Type" module, it is an instance-of 'FromJSON', 1) decode 'Config' from json string, 2) construct an 'Manager'-from 'Config'.---All 'Maybe' fileds can be omitted, the default value will be used, e.g.--1) 'List' type field will use '[]' as default--2) 'Purelude.Bool' type filed will use False as default--3) if the field is an instance of 'Data.Default.Default', use 'def'--4) an appropriate value as default for other fields---In fact, you can decode 'Config' from an empty json (object) string,--@-  manager <- getManager "{}" -- enable the 'OverloadedStrings' extension-@--it will create a manager with a root sink (i.e. 'Logging.defaultRoot'),-the 'Logging.defaultRoot' will be used when the root sink is omitted.+{-|This module provides a convenient way to decode a 'Manager' from json string -Here is a json string template:+Note: You are recommend to use "Logging.Config.Yaml", because of yaml is easier+to read and write. +=== Template @   {     \"sinks\":@@ -61,7 +38,7 @@         \"level\": \"Level 100\",         \"filterer\": [\"App.Json\"],         \"formatter\": \"standard\",-        \"file\": \"/etc\/my\/json.log\",+        \"file\": \"\/var\/log\/my\/json.log",         \"encoding\": \"utf8\"       },       \"rotate\":@@ -70,7 +47,7 @@         \"level\": \"INFO\",         \"filterer\": [\"App.Yaml\"],         \"formatter\": \"standard\",-        \"file\": \"/etc\/my\/yaml.log\",+        \"file\": \"\/var\/log\/my\/json.log",         \"encoding\": \"utf8\",         \"maxBytes\": 1048576,         \"backupCount\": 10@@ -81,13 +58,10 @@       \"simple\": \"{message}\",       \"standard\": \"{asctime:%Y-%m-%dT%H:%M:%S%6Q%z} - {level} - {logger}] {message}\"     },-    \"disabled\": false,-    \"catchUncaughtException\": true+    \"disabled\": false   } @- -}- module Logging.Config.Json (getManager, getManagerFile) where  @@ -100,7 +74,8 @@ import           Logging.Manager  --- | decode a 'Manager' from strict 'ByteString'+-- | decode a 'Manager' from strict 'ByteString', it+-- 1) decodes 'Config' from json string, 2) constructs an 'Manager' -- -- @since 0.4.0 getManager :: ByteString -> IO Manager@@ -110,7 +85,8 @@     Right config -> createManager config  --- | decode a 'Manager' from a file+-- | decode a 'Manager' from a file, it+-- 1) reads json string from file, 2) calls getManager to construct an 'Manager' -- -- @since 0.4.0 getManagerFile :: FilePath -> IO Manager
src/Logging/Config/Type.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE DeriveGeneric         #-} {-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE NamedFieldPuns        #-} {-# LANGUAGE OverloadedStrings     #-} {-# LANGUAGE RecordWildCards       #-} @@ -8,6 +10,9 @@   , Sink(..)   , Config(..)   , ConfigException(..)+  , HandlerH(..)+  , SinkH(..)+  , ConfigH(..)   , createManager   ) where @@ -15,7 +20,10 @@ import           Control.Concurrent.MVar import           Control.Exception                       (Exception) import           Data.Aeson-import           Data.Default+import           Data.Aeson.Default+import           Data.Aeson.Default.HKD+import           Data.Aeson.Default.Map.Strict+import           Data.Functor.Identity import           Data.IORef import           Data.Map.Lazy import           Data.Maybe@@ -23,92 +31,154 @@ import           Data.Time.Clock import           Data.Time.LocalTime import           GHC.Generics-import           System.IO- import           Prelude                                 hiding (map) import qualified Prelude                                 as P+import           System.IO import           Text.Format                             hiding (defaultOptions)  import qualified Logging.Class                           as T+import qualified Logging.Filter                          as T import qualified Logging.Handler.FileHandler             as T import qualified Logging.Handler.RotatingFileHandler     as T import qualified Logging.Handler.StreamHandler           as T import qualified Logging.Handler.TimeRotatingFileHandler as T+import qualified Logging.Level                           as T import qualified Logging.Manager                         as T import           Logging.Prelude import qualified Logging.Sink                            as T  -data Handler = StreamHandler { level     :: Maybe String-                             , filterer  :: Maybe [String]-                             , formatter :: Maybe String-                             , stream    :: Maybe String-                             }-             | FileHandler { level     :: Maybe String-                           , filterer  :: Maybe [String]-                           , formatter :: Maybe String-                           , file      :: FilePath-                           , encoding  :: Maybe String-                             -- ^ See 'System.IO.mkTextEncoding',-                             -- default is utf8.-                           }-             | RotatingFileHandler { level       :: Maybe String-                                   , filterer    :: Maybe [String]-                                   , formatter   :: Maybe String-                                   , file        :: FilePath-                                   , encoding    :: Maybe String-                                     -- ^ See 'System.IO.mkTextEncoding',-                                     -- default is utf8.-                                   , maxBytes    :: Maybe Int-                                     -- ^ Default is 100 MB.-                                   , backupCount :: Maybe Int-                                     -- ^ Default is 10.-                                   }-             | TimeRotatingFileHandler { level       :: Maybe String-                                       , filterer    :: Maybe [String]-                                       , formatter   :: Maybe String-                                       , file        :: FilePath-                                       , encoding    :: Maybe String-                                       , timezone    :: Maybe String-                                         -- ^ If not set, same as 'Manager''s-                                         -- timezone.-                                       , rotateTime  :: Maybe String-                                         -- ^ Indicates when to rotate file,-                                         -- e.g. @D3@ means every 3 days,-                                         -- @W4@ means at 0 clock of 'Thursday',-                                         -- try showing 'RotateTime'.-                                       , backupCount :: Maybe Int-                                       }-              -- ^ @since 0.7.0-              deriving (Generic, Show)+{-| A datatype used to decode json or yaml into 'Handler' datatypes. -instance FromJSON Handler where+All 'HKD' wrapped and 'Maybe' fields are optional fields, they can be omitted.++There are tow config file templates in "Logging.Config.Json" and+"Logging.Config.Yaml".++Note: When decoding from json or yaml, use 'type' field to specify handler type.+-}+data HandlerH f = StreamHandler { level     :: HKD T.Level f+                                -- ^ Default is @NOTSET@.+                                , filterer  :: HKD T.Filterer f+                                -- ^ Default is [].+                                , formatter :: HKD String f+                                -- ^ It represents key of 'ConfigH''s+                                -- formatters.+                                , stream    :: HKD String f+                                -- ^ Only support 'stderr' and 'stdout',+                                -- default is 'stderr'.+                                }++                | FileHandler { level     :: HKD T.Level f+                              -- ^ Default is @NOTSET@.+                              , filterer  :: HKD T.Filterer f+                              -- ^ Default is [].+                              , formatter :: HKD String f+                              -- ^ It represents key of 'ConfigH''s+                              -- formatters.+                              , file      :: FilePath+                              , encoding  :: HKD String f+                              -- ^ See 'System.IO.mkTextEncoding',+                              -- default is utf8.+                              }+                | RotatingFileHandler { level       :: HKD T.Level f+                                      -- ^ Default is @NOTSET@.+                                      , filterer    :: HKD T.Filterer f+                                      -- ^ Default is [].+                                      , formatter   :: HKD String f+                                      -- ^ It represents key of 'ConfigH''s+                                      -- formatters.+                                      , file        :: FilePath+                                      , encoding    :: HKD String f+                                      -- ^ See 'System.IO.mkTextEncoding',+                                      -- default is utf8.+                                      , maxBytes    :: HKD Int f+                                      -- ^ Default is 100 MB.+                                      , backupCount :: HKD Int f+                                      -- ^ Default is 10.+                                      }+                | TimeRotatingFileHandler { level       :: HKD T.Level f+                                          -- ^ Default is @NOTSET@.+                                          , filterer    :: HKD T.Filterer f+                                          -- ^ Default is [].+                                          , formatter   :: HKD String f+                                          -- ^ It represents key of 'ConfigH''s+                                          -- formatters.+                                          , file        :: FilePath+                                          , encoding    :: HKD String f+                                          , timezone    :: Maybe String+                                          -- ^ If not set, same as 'Manager''s+                                          -- timezone.+                                          , rotateTime  :: HKD T.RotateTime f+                                          -- ^ Indicates when to rotate file,+                                          -- e.g. @D3@ means every 3 days,+                                          -- @W4@ means at 0 clock of 'Thursday',+                                          -- try reading and showing+                                          -- 'RotateTime'.+                                          , backupCount :: HKD Int f+                                          }+                 -- ^ @since 0.7.0+                 deriving Generic++instance FromJSON (HandlerH Maybe) where   parseJSON = genericParseJSON option     where       sumEncoding = defaultTaggedObject { tagFieldName = "type" }       option = defaultOptions { sumEncoding = sumEncoding } +instance Default HandlerH where+  constrDef constr+      | constr == "StreamHandler" = StreamHandler{..}+      | constr == "FileHandler" = FileHandler{..}+      | constr == "RotatingFileHandler" = RotatingFileHandler{..}+      | constr == "TimeRotatingFileHandler" = TimeRotatingFileHandler{..}+    where+      (level, filterer, formatter) = ("NOTSET", [], "{message}")+      (stream, file, encoding) = ("stderr", "./logging.log", "UTF-8")+      (maxBytes, backupCount) = (104857600, 10)+      (timezone, rotateTime) = (Nothing, T.Day 1) -data Sink = Sink { level     :: Maybe String-                 , filterer  :: Maybe [String]-                 , handlers  :: Maybe [String]-                 , propagate :: Maybe Bool-                 , disabled  :: Maybe Bool-                 } deriving (Generic, Show)+type Handler = HandlerH Identity -instance FromJSON Sink +--------------------------------------------------------------------------------+data SinkH f = Sink { level     :: HKD T.Level f+                    -- Default is "NOTSET"+                    , filterer  :: HKD T.Filterer f+                    -- Default is []+                    , handlers  :: HKD [String] f+                    -- ^ A list of key of 'ConfigH''s handlers+                    , propagate :: HKD Bool f+                    , disabled  :: HKD Bool f+                    } deriving Generic -data Config = Config { sinks      :: Maybe (Map String Sink)-                     , handlers   :: Maybe (Map String Handler)-                     , formatters :: Maybe (Map String String)-                     , timezone   :: Maybe String -- ^ @since 0.7.0-                     , disabled   :: Maybe Bool-                     } deriving (Generic, Show)+instance FromJSON (SinkH Maybe) -instance FromJSON Config+instance Default SinkH where+  constrDef _ = Sink "NOTSET" [] [] True False +type Sink = SinkH Identity ++--------------------------------------------------------------------------------+data ConfigH f = Config { sinks      :: HKD (MapH String SinkH f) f+                        , handlers   :: HKD (MapH String HandlerH f) f+                        , formatters :: HKD (Map String String) f+                        -- See "Format1" to learn how to write format string.+                        , timezone   :: Maybe String -- ^ @since 0.7.0+                        -- Default is the result of 'getCurrentTimeZone'.+                        , disabled   :: HKD Bool f+                        } deriving Generic++instance FromJSON (ConfigH Maybe)++instance Default ConfigH where+  constrDef _ = Config mempty mempty mempty Nothing False++type Config = ConfigH Identity+++-------------------------------------------------------------------------------- newtype ConfigException = ConfigException { message :: String }  instance Show ConfigException where@@ -117,80 +187,53 @@ instance Exception ConfigException  +--------------------------------------------------------------------------------+{-| Create "Logging.Manager.Manager" from 'Config'.++This function is only used by "Logging.Config.Json" and "Logging.Config.Yaml",+use functions provided in these two modules directly.+-} createManager :: Config -> IO T.Manager createManager Config{..} = do-    handlers <- mapM (createHandler formatters') $ fromMaybe empty handlers-    sinks <- sequence $ mapWithKey (createSink handlers) $ fromMaybe empty sinks-    defaultTimezone <- getCurrentTimeZone--    let root = findWithDefault defaultRoot "root" sinks-        sinks' = delete "root" sinks-        timezone' = maybe defaultTimezone read timezone-        disabled' = fromMaybe False disabled-    return $ T.Manager root sinks' timezone' disabled' False+    hs <- mapM (createHandler $ map fromString formatters) $ unMapH handlers+    ss <- sequence $ mapWithKey (createSink hs) $ unMapH sinks+    timezone <- maybe getCurrentTimeZone (pure . read) timezone+    let root = findWithDefault defaultRoot "root" ss+        sinks = delete "root" ss+        catchUncaughtException = False -- TODO remove+    return $ T.Manager{..}   where-    formatters' :: Map String Format1-    formatters' = maybe (singleton "" "{message}") (map fromString) formatters-     stderrHandler :: T.SomeHandler-    stderrHandler = T.toHandler $ T.StreamHandler def [] "{message}" stderr+    stderrHandler = T.toHandler $ T.StreamHandler "NOTSET" [] "{message}" stderr      defaultRoot :: T.Sink     defaultRoot = T.Sink "" "DEBUG" [] [stderrHandler] False False      createSink :: Map String T.SomeHandler -> String -> Sink -> IO T.Sink     createSink hs logger Sink{..} = do-      let logger' = if logger == "root" then "" else logger-          level' = maybe def read level-          filterer' = maybe [] (P.map fromString) filterer-          handlers' = [hs ! h | h <- fromMaybe [] handlers]-          disabled' = fromMaybe False disabled-          propagate' = fromMaybe False propagate-      return $ T.Sink logger' level' filterer' handlers' disabled' propagate'--    getStream :: String -> Handle-    getStream "stderr" = stderr-    getStream "stdout" = stdout-    getStream _        = error "Logging.Config: no parse (stream)"+      logger <- pure $ if logger == "root" then "" else logger+      handlers <- pure [hs ! h | h <- handlers]+      return $ T.Sink{..}      createHandler :: Map String Format1 -> Handler -> IO T.SomeHandler     createHandler fs StreamHandler{..} = do-      let level' = maybe def read level-          filterer' = maybe [] (P.map fromString) filterer-          formatter' = maybe "{message}" ((!) fs) formatter-          stream' = getStream $ fromMaybe "stderr" stream-      return $ T.toHandler $ T.StreamHandler level' filterer' formatter' stream'+      formatter <- pure $ fs ! formatter+      stream <- pure $ mkStdHandle stream+      return $ T.toHandler $ T.StreamHandler{..}     createHandler fs FileHandler{..} = do-      let level' = maybe def read level-          filterer' = maybe [] (P.map fromString) filterer-          formatter' = maybe "{message}" ((!) fs) formatter-      encoding' <- mkTextEncoding $ fromMaybe "utf8" encoding+      formatter <- pure $ fs ! formatter+      encoding <- mkTextEncoding encoding       stream <- newIORef undefined-      return $ T.toHandler $-        T.FileHandler level' filterer' formatter' file encoding' stream+      return $ T.toHandler $ T.FileHandler{..}     createHandler fs RotatingFileHandler{..} = do-      let level' = maybe def read level-          filterer' = maybe [] (P.map fromString) filterer-          formatter' = maybe "{message}" ((!) fs) formatter-          maxBytes' = fromMaybe 104857600 maxBytes-          backupCount' = fromMaybe 10 backupCount-      encoding' <- mkTextEncoding $ fromMaybe "utf8" encoding+      formatter <- return $ fs ! formatter+      encoding <- mkTextEncoding encoding       stream <- newEmptyMVar-      return $ T.toHandler $-        T.RotatingFileHandler level' filterer' formatter' file encoding'-                              maxBytes' backupCount' stream+      return $ T.toHandler $ T.RotatingFileHandler{..}     createHandler fs TimeRotatingFileHandler{timezone=tz, ..} = do-      let level' = maybe def read level-          filterer' = maybe [] (P.map fromString) filterer-          formatter' = maybe "{message}" ((!) fs) formatter-          tz' = mappend tz timezone-          rotateTime' = maybe (T.Day 1) read rotateTime-          backupCount' = fromMaybe 10 backupCount-      encoding' <- mkTextEncoding $ fromMaybe "utf8" encoding-      timezone' <- maybe getCurrentTimeZone (return . read) tz'+      formatter <- pure $ fs ! formatter+      timezone <- maybe getCurrentTimeZone (pure . read) $ mappend tz timezone+      encoding <- mkTextEncoding encoding       rotateAt <- newIORef =<< getCurrentTime       stream <- newEmptyMVar-      return $ T.toHandler $-        T.TimeRotatingFileHandler level' filterer' formatter' file encoding'-                                  timezone' rotateTime' backupCount' rotateAt-                                  stream+      return $ T.toHandler $ T.TimeRotatingFileHandler{..}
src/Logging/Config/Yaml.hs view
@@ -1,7 +1,6 @@-{-| This module provides a convenient way to decode a 'Manager' from yaml string-+{-|This module provides a convenient way to decode a 'Manager' from yaml string -Here is a yaml string template:+=== Template  @   sinks:@@ -28,13 +27,13 @@       type: FileHandler       level: "Level 100"       formatter: standard-      file: /etc/my/json.log+      file: \/var\/log\/my\/yaml.log       encoding: utf8     rotate:       type: RotatingFileHandler       level: INFO       formatter: standard-      file: /etc/my/json.log+      file: \/var\/log\/my\/yaml.log       encoding: utf8       maxBytes: 1048576       backupCount: 10@@ -44,10 +43,7 @@     standard: "{asctime:%Y-%m-%dT%H:%M:%S%6Q%z} - {level} - {logger}] {message}"    disabled: false--  catchUncaughtException: true @- -} module Logging.Config.Yaml (getManager, getManagerFile) where @@ -60,9 +56,11 @@ import           Logging.Manager  --- | decode a 'Manager' from strict 'ByteString'------ @since 0.4.0+{-| decode a 'Manager' from strict 'ByteString', it+1) decodes 'Config' from json string, 2) constructs an 'Manager'++ @since 0.4.0+-} getManager :: ByteString -> IO Manager getManager bs =   case decodeEither' bs of@@ -70,9 +68,11 @@     Right config -> createManager config  --- | decode a 'Manager' from a file------ @since 0.4.0+{- | decode a 'Manager' from a file, it+1) reads json string from file, 2) calls 'getManager' to construct an 'Manager'++@since 0.4.0+-} getManagerFile :: FilePath -> IO Manager getManagerFile path = do   res <- decodeFileEither path
src/Logging/Filter.hs view
@@ -3,6 +3,7 @@  module Logging.Filter ( Filter(..), Filterer ) where +import           Data.Aeson import           Data.List                (stripPrefix) import           Data.String import           Prelude                  hiding (filter)@@ -49,6 +50,9 @@                     Just ('.':_) -> True                     _            -> False   filter (Filter _ (Just f)) rcd = f rcd++instance FromJSON Filter where+  parseJSON = (fmap read) . parseJSON   -- |List of Filter
src/Logging/Global.hs view
@@ -1,11 +1,14 @@-{-| Run logging globally.+{-|+Copyright       : (c) 2019 Version Cloud+License         : BSD3+Maintainer      : Jorah Gao <jorah@version.cloud>+Stability       : experimental+Portability     : portable +Run logging globally.+ The 'run' function will properly "initialize" and "terminate" the global "Manager".--If the "Manager"\'s 'catchUncaughtException' is True, the 'run' function will-set an uncaught exception handler that will log all uncaught exceptions,- and set the handler to the original one before 'run' is complete. -}  module Logging.Global
src/Logging/Global/Internal.hs view
@@ -77,4 +77,5 @@     unknownLoc = ("unknown file", "unknown package", "unknown module", 0)      logThrow :: SomeException -> IO a+    logThrow e | Just UserInterrupt <- fromException e = throw e     logThrow e = log "" "ERROR" e Null unknownLoc >> throw e
src/Logging/Handler/TimeRotatingFileHandler.hs view
@@ -10,6 +10,7 @@  import           Control.Concurrent.MVar import           Control.Monad+import           Data.Aeson import           Data.Char import           Data.IORef import           Data.List@@ -144,6 +145,9 @@   show (Hour n)    = 'H' : (show n)   show (WeekDay n) = 'W' : (show $ fromEnum n)   show (Day n)     = 'D' : (show n)++instance FromJSON RotateTime where+  parseJSON = (fmap read) . parseJSON   interval :: RotateTime -> NominalDiffTime
src/Logging/Level.hs view
@@ -2,8 +2,8 @@  module Logging.Level ( Level(..) ) where -import           Data.Default-import           Data.List    (stripPrefix)+import           Data.Aeson+import           Data.List   (stripPrefix) import           Data.String import           Text.Format @@ -53,8 +53,8 @@   toEnum = Level   fromEnum (Level v) = v -instance Default Level where-  def = "NOTSET"- instance FormatArg Level where   formatArg = formatArg . show++instance FromJSON Level where+  parseJSON = (fmap read) . parseJSON
src/Logging/Monad.hs view
@@ -1,10 +1,14 @@-{-| Run logging in a 'Reader' monad.+{-|+Copyright       : (c) 2019 Version Cloud+License         : BSD3+Maintainer      : Jorah Gao <jorah@version.cloud>+Stability       : experimental+Portability     : portable +Run logging in a 'Reader' monad.+ You should 'initialize' the 'Manager' before using it in the 'Reader' monad, and 'terminate' it when it is no longer needed.--If you want to log uncaught exceptions,-see "GHC.Conc.setUncaughtExceptionHandler". -}  
src/Logging/Monad/Internal.hs view
@@ -14,12 +14,10 @@  import           Control.Concurrent import           Control.Exception           (SomeException, bracket_)-import           Control.Lens                (view) import           Control.Monad import           Control.Monad.IO.Class      (MonadIO (..)) import           Control.Monad.Reader import           Data.Aeson-import           Data.Default import           Data.Generics.Product.Typed import           Data.IORef import           Data.List                   (dropWhileEnd, group)@@ -28,6 +26,7 @@ import           Data.Time.Clock.POSIX import           Data.Time.LocalTime import           GHC.Conc                    (setUncaughtExceptionHandler)+import           Lens.Micro.Extras           (view) import           Prelude                     hiding (filter, log) import           System.FilePath import           System.IO                   (stderr, stdout)
src/Logging/Prelude.hs view
@@ -16,6 +16,7 @@   , lastModifyTime   , modifyBaseName   , appendBaseName+  , mkStdHandle   ) where  import           Control.Concurrent@@ -97,3 +98,9 @@  appendBaseName :: FilePath -> String -> FilePath appendBaseName file suffix = modifyBaseName file (++ suffix)+++mkStdHandle :: String -> Handle+mkStdHandle "stderr" = stderr+mkStdHandle "stdout" = stdout+mkStdHandle _        = error "unknown std handle name"
− src/Logging/TH.hs
@@ -1,5 +0,0 @@-module Logging.TH {-# DEPRECATED "Renamed as Logging.Global.TH" #-}-  ( module Logging.Global.TH-  ) where--import           Logging.Global.TH
− src/Logging/Types.hs
@@ -1,23 +0,0 @@-module Logging.Types {-# DEPRECATED "This module has been flattened, use relevant modules directly" #-}-  ( module Logging.Logger-  , module Logging.Level-  , module Logging.Filter-  , module Logging.Record-  , module Logging.Handler.FileHandler-  , module Logging.Handler.RotatingFileHandler-  , module Logging.Handler.StreamHandler-  , module Logging.Sink-  , module Logging.Manager-  , module Logging.Class-  ) where--import           Logging.Class-import           Logging.Filter-import           Logging.Handler.FileHandler-import           Logging.Handler.RotatingFileHandler-import           Logging.Handler.StreamHandler-import           Logging.Level-import           Logging.Logger-import           Logging.Manager-import           Logging.Record-import           Logging.Sink
− src/Logging/Utils.hs
@@ -1,6 +0,0 @@-module Logging.Utils {-# DEPRECATED "Renamed as Logging.Prelude" #-}-  ( module Logging.Prelude-  ) where---import           Logging.Prelude
test/LoggingTest/TypesSpec.hs view
@@ -5,9 +5,7 @@ module LoggingTest.TypesSpec ( spec ) where  import           Control.Concurrent.MVar-import           Control.Lens                            hiding (Level (..)) import           Control.Monad-import           Data.Default                            (def) import           Data.Generics.Product.Typed import           Data.IORef import           Data.List                               (isPrefixOf)@@ -15,6 +13,7 @@ import           Data.Time.Clock import           Data.Time.Clock.POSIX import           Data.Time.LocalTime+import           Lens.Micro import           Prelude                                 hiding (filter) import           System.Directory import           System.FilePath@@ -76,15 +75,15 @@   timeRotateRef <- runIO $ newIORef undefined   timeFileMVar <- runIO newEmptyMVar   runIO $ hSetEncoding read utf8 >> hSetEncoding write utf8-  let handler = toHandler $ StreamHandler def [] "{message}" write+  let handler = toHandler $ StreamHandler "NOTSET" [] "{message}" write       fileHandler = toHandler $-        FileHandler def [] "{message}" "/tmp/log4hs/file.log" utf8 fileRef+        FileHandler "NOTSET" [] "{message}" "/tmp/log4hs/file.log" utf8 fileRef       rotateHandler = toHandler $-        RotatingFileHandler def [] "{message}" "/tmp/log4hs/rotate.log" utf8-          1000 5 rotateFileMVar+        RotatingFileHandler "NOTSET" [] "{message}" "/tmp/log4hs/rotate.log"+          utf8 1000 5 rotateFileMVar       timeHandler = toHandler $-        TimeRotatingFileHandler def [] "{message}" "/tmp/log4hs/time.log" utf8-          utc (Minute 5) 5 timeRotateRef timeFileMVar+        TimeRotatingFileHandler "NOTSET" [] "{message}" "/tmp/log4hs/time.log"+          utf8 utc (Minute 5) 5 timeRotateRef timeFileMVar   runIO $ open handler >> open fileHandler >> open rotateHandler    prop "filter" $ \(rcd@LogRecord{..}) -> monadicIO $ do