fast-logger 2.4.11 → 2.4.12
raw patch · 7 files changed
+143/−102 lines, 7 filesdep +unix-compatdep −Win32dep −old-localedep −timedep ~unix-timePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: unix-compat
Dependencies removed: Win32, old-locale, time, unix
Dependency ranges changed: unix-time
API changes (from Hackage documentation)
- System.Log.FastLogger.Date: type FormattedTime = ByteString
- System.Log.FastLogger.Date: type TimeFormat = ByteString
+ System.Log.FastLogger: LogFileTimedRotate :: TimedFileLogSpec -> BufSize -> LogType
+ System.Log.FastLogger.File: TimedFileLogSpec :: FilePath -> TimeFormat -> (FormattedTime -> FormattedTime -> Bool) -> (FilePath -> IO ()) -> TimedFileLogSpec
+ System.Log.FastLogger.File: [timed_log_file] :: TimedFileLogSpec -> FilePath
+ System.Log.FastLogger.File: [timed_post_process] :: TimedFileLogSpec -> FilePath -> IO ()
+ System.Log.FastLogger.File: [timed_same_timeframe] :: TimedFileLogSpec -> FormattedTime -> FormattedTime -> Bool
+ System.Log.FastLogger.File: [timed_timefmt] :: TimedFileLogSpec -> TimeFormat
+ System.Log.FastLogger.File: data TimedFileLogSpec
+ System.Log.FastLogger.File: prefixTime :: FormattedTime -> FilePath -> FilePath
+ System.Log.FastLogger.Types: type FormattedTime = ByteString
+ System.Log.FastLogger.Types: type TimeFormat = ByteString
- System.Log.FastLogger: LogCallback :: (LogStr -> IO ()) -> (IO ()) -> LogType
+ System.Log.FastLogger: LogCallback :: (LogStr -> IO ()) -> IO () -> LogType
Files
- System/Log/FastLogger.hs +75/−12
- System/Log/FastLogger/Date.hs +6/−35
- System/Log/FastLogger/File.hs +39/−2
- System/Log/FastLogger/FileIO.hs +2/−43
- System/Log/FastLogger/LogStr.hs +2/−0
- System/Log/FastLogger/Types.hs +13/−0
- fast-logger.cabal +6/−10
System/Log/FastLogger.hs view
@@ -43,6 +43,8 @@ , module System.Log.FastLogger.Date -- * File rotation , module System.Log.FastLogger.File+ -- * Types+ , module System.Log.FastLogger.Types ) where #if __GLASGOW_HASKELL__ < 709@@ -53,6 +55,7 @@ import Control.Exception (handle, SomeException(..), bracket) import Control.Monad (when, replicateM) import Data.Array (Array, listArray, (!), bounds)+import Data.Foldable (forM_) import Data.Maybe (isJust) import System.EasyFile (getFileSize) import System.Log.FastLogger.File@@ -62,6 +65,7 @@ import System.Log.FastLogger.LogStr import System.Log.FastLogger.Logger import System.Log.FastLogger.Date+import System.Log.FastLogger.Types ---------------------------------------------------------------- @@ -175,6 +179,15 @@ type FastLogger = LogStr -> IO () -- | 'TimedFastLogger' pass 'FormattedTime' to callback and simply log its result. -- this can be used to customize how to log timestamp.+-- +-- Usually, one would write a wrapper on top of 'TimedFastLogger', for example:+--+-- @+-- {-# LANGUAGE OverloadedStrings #-}+--+-- log :: TimedFastLogger -> LogStr -> IO ()+-- log logger msg = logger (\time -> toLogStr (show time) <> " " <> msg <> "\n")+-- @ type TimedFastLogger = (FormattedTime -> LogStr) -> IO () -- | Logger Type.@@ -194,6 +207,11 @@ -- 'BufSize' is a buffer size -- for each capability. -- File rotation is done on-demand.+ | LogFileTimedRotate TimedFileLogSpec BufSize -- ^ Logging to a file.+ -- 'BufSize' is a buffer size+ -- for each capability.+ -- Rotation happens based on check specified+ -- in 'TimedFileLogSpec'. | LogCallback (LogStr -> IO ()) (IO ()) -- ^ Logging with a log and flush action. -- run flush after log each message. @@ -201,12 +219,13 @@ -- a tuple of logger and clean up action are returned. newFastLogger :: LogType -> IO (FastLogger, IO ()) newFastLogger typ = case typ of- LogNone -> return (const noOp, noOp)- LogStdout bsize -> newStdoutLoggerSet bsize >>= stdLoggerInit- LogStderr bsize -> newStderrLoggerSet bsize >>= stdLoggerInit- LogFileNoRotate fp bsize -> newFileLoggerSet bsize fp >>= fileLoggerInit- LogFile fspec bsize -> rotateLoggerInit fspec bsize- LogCallback cb flush -> return (\str -> cb str >> flush, noOp)+ LogNone -> return (const noOp, noOp)+ LogStdout bsize -> newStdoutLoggerSet bsize >>= stdLoggerInit+ LogStderr bsize -> newStderrLoggerSet bsize >>= stdLoggerInit+ LogFileNoRotate fp bsize -> newFileLoggerSet bsize fp >>= fileLoggerInit+ LogFile fspec bsize -> rotateLoggerInit fspec bsize+ LogFileTimedRotate fspec bsize -> timedRotateLoggerInit fspec bsize+ LogCallback cb flush -> return (\str -> cb str >> flush, noOp) where stdLoggerInit lgrset = return (pushLogStr lgrset, rmLoggerSet lgrset) fileLoggerInit lgrset = return (pushLogStr lgrset, rmLoggerSet lgrset)@@ -219,6 +238,18 @@ pushLogStr lgrset str when (cnt <= 0) $ tryRotate lgrset fspec ref mvar return (logger, rmLoggerSet lgrset)+ timedRotateLoggerInit fspec bsize = do+ cache <- newTimeCache $ timed_timefmt fspec+ now <- cache+ lgrset <- newFileLoggerSet bsize $ prefixTime now $ timed_log_file fspec+ ref <- newIORef now+ mvar <- newMVar lgrset+ let logger str = do+ ct <- cache+ updated <- updateTime (timed_same_timeframe fspec) ref ct+ when updated $ tryTimedRotate fspec ct mvar+ pushLogStr lgrset str+ return (logger, rmLoggerSet lgrset) -- | 'bracket' version of 'newFastLogger' withFastLogger :: LogType -> (FastLogger -> IO a) -> IO a@@ -231,12 +262,13 @@ -- "System.Log.FastLogger.Date" provide cached formatted time. -> LogType -> IO (TimedFastLogger, IO ()) newTimedFastLogger tgetter typ = case typ of- LogNone -> return (const noOp, noOp)- LogStdout bsize -> newStdoutLoggerSet bsize >>= stdLoggerInit- LogStderr bsize -> newStderrLoggerSet bsize >>= stdLoggerInit- LogFileNoRotate fp bsize -> newFileLoggerSet bsize fp >>= fileLoggerInit- LogFile fspec bsize -> rotateLoggerInit fspec bsize- LogCallback cb flush -> return (\f -> tgetter >>= cb . f >> flush, noOp)+ LogNone -> return (const noOp, noOp)+ LogStdout bsize -> newStdoutLoggerSet bsize >>= stdLoggerInit+ LogStderr bsize -> newStderrLoggerSet bsize >>= stdLoggerInit+ LogFileNoRotate fp bsize -> newFileLoggerSet bsize fp >>= fileLoggerInit+ LogFile fspec bsize -> rotateLoggerInit fspec bsize+ LogFileTimedRotate fspec bsize -> timedRotateLoggerInit fspec bsize+ LogCallback cb flush -> return (\f -> tgetter >>= cb . f >> flush, noOp) where stdLoggerInit lgrset = return ( \f -> tgetter >>= pushLogStr lgrset . f, rmLoggerSet lgrset) fileLoggerInit lgrset = return (\f -> tgetter >>= pushLogStr lgrset . f, rmLoggerSet lgrset)@@ -250,6 +282,19 @@ pushLogStr lgrset (f t) when (cnt <= 0) $ tryRotate lgrset fspec ref mvar return (logger, rmLoggerSet lgrset)+ timedRotateLoggerInit fspec bsize = do+ cache <- newTimeCache $ timed_timefmt fspec+ now <- cache+ lgrset <- newFileLoggerSet bsize $ prefixTime now $ timed_log_file fspec+ ref <- newIORef now+ mvar <- newMVar lgrset+ let logger f = do+ ct <- cache+ updated <- updateTime (timed_same_timeframe fspec) ref ct+ when updated $ tryTimedRotate fspec ct mvar+ t <- tgetter+ pushLogStr lgrset (f t)+ return (logger, rmLoggerSet lgrset) -- | 'bracket' version of 'newTimeFastLogger' withTimedFastLogger :: IO FormattedTime -> LogType -> (TimedFastLogger -> IO a) -> IO a@@ -263,6 +308,10 @@ decrease :: IORef Int -> IO Int decrease ref = atomicModifyIORef' ref (\x -> (x - 1, x - 1)) +-- updateTime returns whether the timeframe has changed+updateTime :: (FormattedTime -> FormattedTime -> Bool) -> IORef FormattedTime -> FormattedTime -> IO Bool+updateTime cmp ref newTime = atomicModifyIORef' ref (\x -> (newTime, not $ cmp x newTime))+ tryRotate :: LoggerSet -> FileLogSpec -> IORef Int -> MVar () -> IO () tryRotate lgrset spec ref mvar = bracket lock unlock rotateFiles where@@ -292,3 +341,17 @@ Just . fromIntegral <$> getFileSize file -- 200 is an ad-hoc value for the length of log line. estimate x = fromInteger (x `div` 200)+++tryTimedRotate :: TimedFileLogSpec -> FormattedTime -> MVar LoggerSet -> IO ()+tryTimedRotate spec now mvar = bracket lock unlock rotateFiles+ where+ lock = tryTakeMVar mvar+ unlock Nothing = return ()+ unlock (Just (LoggerSet current_path a b c)) = do+ putMVar mvar $ LoggerSet (Just new_file_path) a b c+ forM_ current_path (timed_post_process spec)+ rotateFiles Nothing = return ()+ rotateFiles (Just (LoggerSet _ a b c)) = renewLoggerSet $ LoggerSet (Just new_file_path) a b c+ new_file_path = prefixTime now $ timed_log_file spec+
System/Log/FastLogger/Date.hs view
@@ -4,56 +4,27 @@ -- | -- Formatting time is slow. -- This package provides mechanisms to cache formatted date.-module System.Log.FastLogger.Date (- -- * Types- TimeFormat- , FormattedTime- -- * Date cacher- , newTimeCache+module System.Log.FastLogger.Date+ ( -- * Date cacher+ newTimeCache , simpleTimeFormat , simpleTimeFormat' ) where import Control.AutoUpdate (mkAutoUpdate, defaultUpdateSettings, updateAction)-import Data.ByteString (ByteString)-#if WINDOWS-import qualified Data.ByteString.Char8 as BS-import Data.Time (UTCTime, formatTime, getCurrentTime, utcToLocalZonedTime)-# if MIN_VERSION_time(1,5,0)-import Data.Time (defaultTimeLocale)-# else-import System.Locale (defaultTimeLocale)-# endif-#else+import System.Log.FastLogger.Types (TimeFormat, FormattedTime) import Data.UnixTime (formatUnixTime, fromEpochTime)-import System.Posix (EpochTime, epochTime)-#endif---------------------------------------------------------------------- | Type aliaes for date format and formatted date.-type FormattedTime = ByteString-type TimeFormat = ByteString+import System.PosixCompat.Types (EpochTime)+import System.PosixCompat.Time (epochTime) ---------------------------------------------------------------- -#if WINDOWS--- | Get date using UTC.-getTime :: IO UTCTime-getTime = getCurrentTime--- | Format UTC date.-formatDate :: TimeFormat -> UTCTime -> IO FormattedTime-formatDate fmt ut = do- zt <- utcToLocalZonedTime ut- return $ BS.pack $ formatTime defaultTimeLocale (BS.unpack fmt) zt-#else -- | Get date using UnixTime. getTime :: IO EpochTime getTime = epochTime -- | Format unix EpochTime date. formatDate :: TimeFormat -> EpochTime -> IO FormattedTime formatDate fmt = formatUnixTime fmt . fromEpochTime-#endif ----------------------------------------------------------------
System/Log/FastLogger/File.hs view
@@ -1,10 +1,18 @@ {-# LANGUAGE Safe #-} -module System.Log.FastLogger.File where+module System.Log.FastLogger.File+ ( FileLogSpec(..)+ , TimedFileLogSpec (..)+ , check+ , rotate+ , prefixTime+ ) where import Control.Monad (unless, when)+import Data.ByteString.Char8 (unpack) import System.Directory (doesFileExist, doesDirectoryExist, getPermissions, writable, renameFile)-import System.FilePath (takeDirectory)+import System.FilePath (takeDirectory, dropFileName, takeFileName, (</>))+import System.Log.FastLogger.Types (TimeFormat, FormattedTime) -- | The spec for logging files data FileLogSpec = FileLogSpec {@@ -13,6 +21,31 @@ , log_backup_number :: Int -- ^ Max number of rotated log files to keep around before overwriting the oldest one. } +-- | The spec for time based rotation. It supports post processing of log files. Does+-- not delete any logs. Example:+--+-- @+-- timeRotate fname = LogFileTimedRotate+-- (TimedFileLogSpec fname timeFormat sametime compressFile)+-- defaultBufSize+-- where+-- timeFormat = "%FT%H%M%S"+-- sametime = (==) `on` C8.takeWhile (/='T')+-- compressFile fp = void . forkIO $+-- callProcess "tar" [ "--remove-files", "-caf", fp <> ".gz", fp ]+-- @+data TimedFileLogSpec = TimedFileLogSpec {+ timed_log_file :: FilePath -- ^ base file path+ , timed_timefmt :: TimeFormat -- ^ time format to prepend+ , timed_same_timeframe :: FormattedTime -> FormattedTime -> Bool+ -- ^ function that compares two+ -- formatted times as specified by+ -- timed_timefmt and decides if a+ -- new rotation is supposed to+ -- begin+ , timed_post_process :: FilePath -> IO () -- ^ processing function called asynchronously after a file is added to the rotation+ }+ -- | Checking if a log file can be written. check :: FilePath -> IO () check file = do@@ -40,3 +73,7 @@ move (src,dst) = do exist <- doesFileExist src when exist $ renameFile src dst++-- | Prefix file name with formatted time+prefixTime :: FormattedTime -> FilePath -> FilePath+prefixTime time path = dropFileName path </> unpack time ++ "-" ++ takeFileName path
System/Log/FastLogger/FileIO.hs view
@@ -1,49 +1,10 @@-{-# LANGUAGE CPP #-}- module System.Log.FastLogger.FileIO where -import Foreign.Ptr (Ptr) import Data.Word (Word8)--#ifdef mingw32_HOST_OS-import System.Win32.Types (HANDLE, UINT)-import System.Win32.File-import Graphics.Win32.Misc (getStdHandle, sTD_OUTPUT_HANDLE, sTD_ERROR_HANDLE)-import Data.Bits ((.|.))--type FD = HANDLE-#if !MIN_VERSION_Win32(2,4,0)--- This flag is not defined in System.Win32.File-fILE_APPEND_DATA :: UINT-fILE_APPEND_DATA = 0x0004-#endif--closeFD :: FD -> IO ()-closeFD = closeHandle--openFileFD :: FilePath -> IO FD-openFileFD f = createFile f- fILE_APPEND_DATA- (fILE_SHARE_READ .|. fILE_SHARE_DELETE)- Nothing- oPEN_ALWAYS- fILE_ATTRIBUTE_NORMAL- Nothing--getStderrFD :: IO FD-getStderrFD = getStdHandle sTD_ERROR_HANDLE--getStdoutFD :: IO FD-getStdoutFD = getStdHandle sTD_OUTPUT_HANDLE--writeRawBufferPtr2FD :: FD -> Ptr Word8 -> Int -> IO Int-writeRawBufferPtr2FD h bf len = fromIntegral `fmap` win32_WriteFile h bf (fromIntegral len) Nothing--#else-+import Foreign.Ptr (Ptr) import GHC.IO.Device (close)-import qualified GHC.IO.FD as POSIX (FD(..)) import GHC.IO.FD (openFile, stderr, stdout, writeRawBufferPtr)+import qualified GHC.IO.FD as POSIX (FD(..)) import GHC.IO.IOMode (IOMode(..)) type FD = POSIX.FD@@ -62,5 +23,3 @@ writeRawBufferPtr2FD :: FD -> Ptr Word8 -> Int -> IO Int writeRawBufferPtr2FD fd bf len = fromIntegral `fmap` writeRawBufferPtr "write" fd bf 0 (fromIntegral len)--#endif
System/Log/FastLogger/LogStr.hs view
@@ -76,6 +76,8 @@ toLogStr bs = LogStr (BS.length bs) (toBuilder bs) instance ToLogStr BL.ByteString where toLogStr = toLogStr . S8.concat . BL.toChunks+instance ToLogStr Builder where+ toLogStr x = let b = B.toLazyByteString x in LogStr (fromIntegral (BL.length b)) (B.lazyByteString b) instance ToLogStr String where toLogStr = toLogStr . TL.pack instance ToLogStr T.Text where
+ System/Log/FastLogger/Types.hs view
@@ -0,0 +1,13 @@+module System.Log.FastLogger.Types (+ -- * Types+ TimeFormat+ , FormattedTime+ ) where++import Data.ByteString (ByteString)++----------------------------------------------------------------+ +-- | Type aliaes for date format and formatted date.+type FormattedTime = ByteString+type TimeFormat = ByteString
fast-logger.cabal view
@@ -1,5 +1,5 @@ Name: fast-logger-Version: 2.4.11+Version: 2.4.12 Author: Kazu Yamamoto <kazu@iij.ad.jp> Maintainer: Kazu Yamamoto <kazu@iij.ad.jp> License: BSD3@@ -10,13 +10,15 @@ Category: System Cabal-Version: >= 1.8 Build-Type: Simple-extra-source-files: README.md ChangeLog.md+Extra-Source-Files: README.md ChangeLog.md+Tested-With: GHC ==7.8.4 || ==7.10.3 || ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.3 Library GHC-Options: -Wall Exposed-Modules: System.Log.FastLogger System.Log.FastLogger.File System.Log.FastLogger.Date+ System.Log.FastLogger.Types Other-Modules: System.Log.FastLogger.IO System.Log.FastLogger.FileIO System.Log.FastLogger.IORef@@ -30,16 +32,10 @@ , directory , filepath , text+ , unix-time >= 0.4.3+ , unix-compat if impl(ghc < 7.8) Build-Depends: bytestring-builder- if os(windows)- Cpp-Options: -DWINDOWS- Build-Depends: time- , Win32- , old-locale- else- Build-Depends: unix- , unix-time >= 0.2.2 Test-Suite spec Main-Is: Spec.hs