packages feed

rotating-log (empty) → 0.1

raw patch · 5 files changed

+210/−0 lines, 5 filesdep +basedep +bytestringdep +directorysetup-changed

Dependencies added: base, bytestring, directory, filepath, old-locale, time

Files

+ LICENSE view
@@ -0,0 +1,3 @@+Copyright (c)2013, Soostone Inc++All rights reserved.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ rotating-log.cabal view
@@ -0,0 +1,40 @@+name:                rotating-log+version:             0.1+description:         Size-limited, concurrent, automatically-rotating log writer for production applications.+license:             BSD3+license-file:        LICENSE+author:              Ozgun Ataman, Doug Beardsley+maintainer:          doug.beardsley@soostone.com+copyright:           Soostone Inc+category:            Logging+build-type:          Simple+cabal-version:       >= 1.8++library+  hs-source-dirs: src++  exposed-modules: +    System.RotatingLog++  build-depends:       +    base               >= 4    && < 5,+    bytestring         >= 0.8,+    old-locale         >= 1.0,+    time               >= 1.1,+    filepath           >= 1.0,+    directory          >= 1.0+    +  ghc-options: -Wall -fwarn-tabs++executable test-rotate+  main-is: TestRotate.hs+  hs-source-dirs: test, src+  build-depends:+    base               >= 4,+    bytestring         >= 0.8,+    old-locale         >= 1.0,+    time               >= 1.1,+    filepath           >= 1.0,+    directory          >= 1.0++  ghc-options: -Wall -fwarn-tabs -threaded
+ src/System/RotatingLog.hs view
@@ -0,0 +1,139 @@+{-# LANGUAGE RecordWildCards #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  System.RotatingLog+-- Copyright   :  Soostone Inc+-- License     :  BSD3+--+-- Maintainer  :  admin@soostone.com+-- Stability   :  experimental+--+-- Convenient logging to a disk-based log file with automatic file+-- rotation based on size.+----------------------------------------------------------------------------++module System.RotatingLog+  (++  -- * Core API+    RotatingLog+  , mkRotatingLog+  , rotatedWrite+  , rotatedWrite'++  -- * Built-In Post-Rotate Actions+  , archiveFile++  ) where++-------------------------------------------------------------------------------+import           Control.Concurrent.MVar+import           Data.ByteString.Char8   (ByteString)+import qualified Data.ByteString.Char8   as B+import           Data.Time+import           Data.Word+import           System.Directory+import           System.FilePath.Posix+import           System.IO+import           System.Locale+-------------------------------------------------------------------------------+++------------------------------------------------------------------------------+-- | A size-limited rotating log.  Log filenames are of the format+-- prefix_timestamp.log.+data RotatingLog = RotatingLog+    { logInfo    :: MVar LogInfo+    , namePrefix :: String+    , sizeLimit  :: Word64+    , postAction :: FilePath -> IO ()+    }+++data LogInfo = LogInfo+    { curHandle    :: Handle+    , bytesWritten :: !Word64+    }+++curLogFileName :: String -> FilePath+curLogFileName = (++".log")+++logFileName :: String -> UTCTime -> FilePath+logFileName pre t = concat+    [pre, "_", formatTime defaultTimeLocale "%Y_%m_%d_%H_%M_%S%Q" t, ".log"]+++------------------------------------------------------------------------------+-- | Creates a rotating log given a prefix and size limit in bytes.+mkRotatingLog+    :: String+    -- ^ A prefix for the written log files.+    -> Word64+    -- ^ A size limit in bytes.+    -> (FilePath -> IO ())+    -- ^ An action to be performed on the finished file following+    -- rotation. For example, you could give a callback that moves or+    -- ships the files somewhere else.+    -> IO RotatingLog+mkRotatingLog pre limit pa = do+    let fp = curLogFileName pre+    h <- openFile fp AppendMode+    len <- hFileSize h+    mvar <- newMVar $ LogInfo h (fromIntegral len)+    return $ RotatingLog mvar pre limit pa+++------------------------------------------------------------------------------+-- | Like "rotatedWrite'", but doesn't need a UTCTime and obtains it+-- with a syscall.+rotatedWrite :: RotatingLog -> ByteString -> IO ()+rotatedWrite rlog bs = do+    t <- getCurrentTime+    rotatedWrite' rlog t bs+++------------------------------------------------------------------------------+-- | Writes ByteString to a rotating log file.  If this write would exceed the+-- size limit, then the file is closed and a new file opened.  This function+-- takes a UTCTime to allow a cached time to be used to avoid a system call.+--+-- Please note this function does NOT implicitly insert a newline at+-- the end of the string you provide. This is so that it can be used+-- to log non-textual streams such as binary serialized or compressed+-- content.+rotatedWrite' :: RotatingLog -> UTCTime -> ByteString -> IO ()+rotatedWrite' RotatingLog{..} t bs = do+    modifyMVar_ logInfo $ \LogInfo{..} -> do+        (h,b) <- if bytesWritten + len > sizeLimit+                   then do hClose curHandle+                           let newFile = logFileName namePrefix t+                           renameFile curFile newFile+                           postAction newFile+                           h <- openFile curFile AppendMode+                           return (h, 0)+                   else return (curHandle, bytesWritten)+        B.hPutStr h bs+        return $! LogInfo h (len + b)+  where+    len = fromIntegral $ B.length bs+    curFile = curLogFileName namePrefix+++-------------------------------------------------------------------------------+-- | A built-in post-rotate action that moves the finished file to a+-- given archive location.+archiveFile+    :: FilePath+    -- ^ A target archive directory+    -> (FilePath -> IO ())+archiveFile archive fp =+    let (_, fn) = splitFileName fp+        target = archive </> fn+    in do+        createDirectoryIfMissing True archive+        renameFile fp target+++
+ test/TestRotate.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import           Control.Concurrent+import qualified Data.ByteString.Char8 as B+import           System.RotatingLog++thread :: RotatingLog -> B.ByteString -> Int -> IO ()+thread _log pre n = do+    rotatedWrite _log (B.concat [pre, ": ", B.pack $ show n, "\n"])+    thread _log pre (n+1)++main :: IO ()+main = do+    _log <- mkRotatingLog "foo" 1000000 (archiveFile "logs")+    a <- forkIO $ thread _log "a" 0+    b <- forkIO $ thread _log "b" 0+    c <- forkIO $ thread _log "c" 0+    d <- forkIO $ thread _log "d" 0+    threadDelay 5000000+    killThread a+    killThread b+    killThread c+    killThread d+    putStrLn "done"