This repository was archived by the owner on Aug 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 629
[CBR-211] log rotation checks for size and age of files #3507
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| {-# LANGUAGE RecordWildCards #-} | ||
|
|
||
| -- | internal definitions for "Pos.Util.Log" | ||
|
|
||
| module Pos.Util.Log.Internal | ||
| ( newConfig | ||
| , registerBackends | ||
|
|
@@ -13,15 +15,21 @@ module Pos.Util.Log.Internal | |
| , incrementLinesLogged | ||
| , modifyLinesLogged | ||
| , LoggingHandler -- only export name | ||
| , FileDescription (..) | ||
| , mkFileDescription | ||
| ) where | ||
|
|
||
| import Control.AutoUpdate (UpdateSettings (..), defaultUpdateSettings, | ||
| mkAutoUpdate) | ||
| import Control.Concurrent.MVar (modifyMVar_, newMVar, withMVar) | ||
|
|
||
| import qualified Data.Text as T | ||
| import Data.Time (UTCTime, getCurrentTime) | ||
| import System.FilePath (splitFileName, (</>)) | ||
| import Universum hiding (newMVar) | ||
|
|
||
| import qualified Katip as K | ||
| import qualified Katip.Core as KC | ||
|
|
||
| import Pos.Util.Log.LoggerConfig (LoggerConfig (..)) | ||
| import Pos.Util.Log.Severity | ||
|
|
@@ -42,6 +50,20 @@ s2kname s = K.Namespace [s] | |
| s2knames :: [Text] -> K.Namespace | ||
| s2knames s = K.Namespace s | ||
|
|
||
| -- | log files have a prefix and a name | ||
| data FileDescription = FileDescription { | ||
| prefixpath :: FilePath, | ||
| filename :: FilePath } | ||
| deriving (Show) | ||
|
|
||
| mkFileDescription :: FilePath -> FilePath -> FileDescription | ||
| mkFileDescription bp fp = | ||
| -- if fp contains a filename in a directory path | ||
| -- move this path to the prefix and only keep the name | ||
| let (extbp, fname) = splitFileName fp | ||
| in | ||
| FileDescription { prefixpath = bp </> extbp | ||
| , filename = fname } | ||
|
|
||
| -- | Our internal state | ||
| data LoggingHandlerInternal = LoggingHandlerInternal | ||
|
|
@@ -89,13 +111,23 @@ registerBackends :: LoggingHandler -> [(T.Text, K.Scribe)] -> IO () | |
| registerBackends lh scribes = do | ||
| LoggingHandlerInternal cfg _ counter <- takeMVar (getLSI lh) | ||
| le0 <- K.initLogEnv (s2kname "cardano-sl") "production" | ||
| let le1 = updateEnv le0 getCurrentTime | ||
| -- use 'getCurrentTime' to get a more precise timestamp | ||
| -- as katip uses per default some internal buffered time variable | ||
| timer <- mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentTime, updateFreq = 10000 } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the time is fetched from the OS at most 100 times a second. |
||
| let le1 = updateEnv le0 timer | ||
| le <- register scribes le1 | ||
| putMVar (getLSI lh) $ LoggingHandlerInternal cfg (Just le) counter | ||
| where | ||
| register :: [(T.Text, K.Scribe)] -> K.LogEnv -> IO K.LogEnv | ||
| register [] le = return le | ||
| register ((n, s):scs) le = | ||
| register scs =<< K.registerScribe n s K.defaultScribeSettings le | ||
| register scs =<< K.registerScribe n s scribeSettings le | ||
| updateEnv :: K.LogEnv -> IO UTCTime -> K.LogEnv | ||
| updateEnv le f = le { K._logEnvTimer = f } | ||
| -- request a new time 'getCurrentTime' at most 100 times a second | ||
| updateEnv le timer = | ||
| le { K._logEnvTimer = timer, K._logEnvHost = "hostname" } | ||
|
|
||
| scribeSettings :: KC.ScribeSettings | ||
| scribeSettings = KC.ScribeSettings bufferSize | ||
| where | ||
| bufferSize = 5000 -- size of the queue (in log items) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can set the size of katip's queue here. |
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| {-# LANGUAGE RecordWildCards #-} | ||
|
|
||
| -- | monitor log files for max age and max size | ||
|
|
||
| module Pos.Util.Log.Rotator | ||
| ( cleanupRotator | ||
| , evalRotator | ||
| , initializeRotator | ||
| ) where | ||
|
|
||
| import Universum | ||
|
|
||
| import Control.Exception.Safe (Exception (..), catchIO) | ||
|
|
||
| import qualified Data.List.NonEmpty as NE | ||
| import Data.Time (UTCTime, addUTCTime, diffUTCTime, getCurrentTime, | ||
| parseTimeM) | ||
| import Data.Time.Format (defaultTimeLocale, formatTime) | ||
|
|
||
| import Pos.Util.Log.Internal (FileDescription (..)) | ||
| import Pos.Util.Log.LoggerConfig | ||
|
|
||
| import System.Directory (listDirectory, removeFile) | ||
| import System.FilePath ((</>)) | ||
| import System.IO (BufferMode (LineBuffering), Handle, | ||
| IOMode (WriteMode), hFileSize, hSetBuffering, stdout) | ||
|
|
||
|
|
||
| -- | format of a timestamp | ||
| tsformat :: String | ||
| tsformat = "%Y%m%d%H%M%S" | ||
|
|
||
| -- | get file path to a log file with current time | ||
| nameLogFile :: FileDescription -> IO FilePath | ||
| nameLogFile FileDescription{..} = do | ||
| now <- getCurrentTime | ||
| let tsnow = formatTime defaultTimeLocale tsformat now | ||
| return $ prefixpath </> filename ++ "-" ++ tsnow | ||
|
|
||
| -- | open a new log file | ||
| evalRotator :: RotationParameters -> FileDescription -> IO (Handle, Integer, UTCTime) | ||
| evalRotator rotation fdesc = do | ||
| let maxAge = toInteger $ rotation ^. rpMaxAgeHours | ||
| maxSize = toInteger $ rotation ^. rpLogLimitBytes | ||
|
|
||
| -- open new log file | ||
| fpath <- nameLogFile fdesc | ||
| hdl <- catchIO (openFile fpath WriteMode) $ | ||
| \e -> do | ||
| prtoutException fpath e | ||
| return stdout -- fallback to standard output in case of exception | ||
| hSetBuffering hdl LineBuffering | ||
|
|
||
| -- compute next rotation time | ||
| now <- getCurrentTime | ||
| let rottime = addUTCTime (fromInteger $ maxAge * 3600) now | ||
|
|
||
| return (hdl, maxSize, rottime) | ||
|
|
||
| prtoutException :: Exception e => FilePath -> e -> IO () | ||
| prtoutException fp e = do | ||
| putStrLn $ "error while opening log @ " ++ fp | ||
| putStrLn $ "exception: " ++ displayException e | ||
|
|
||
| -- | list filenames in prefix dir which match 'filename' | ||
| listLogFiles :: FileDescription -> IO (Maybe (NonEmpty FilePath)) | ||
| listLogFiles FileDescription{..} = do | ||
| -- find files in bp which begin with fp | ||
| files <- listDirectory $ prefixpath | ||
| return $ nonEmpty $ sort $ filter fpredicate files | ||
| where | ||
| tslen = 14 -- length of a timestamp | ||
| fplen = length filename | ||
| fpredicate path = take fplen path == filename | ||
| && take 1 (drop fplen path) == "-" | ||
| && length (drop (fplen + 1) path) == tslen | ||
|
|
||
| -- | latest log file in prefix dir which matches 'filename' | ||
| latestLogFile :: FileDescription -> IO (Maybe FilePath) | ||
| latestLogFile fdesc = | ||
| listLogFiles fdesc >>= \fs -> return $ latestLogFile' fs | ||
| where | ||
| latestLogFile' :: Maybe (NonEmpty FilePath) -> Maybe FilePath | ||
| latestLogFile' Nothing = Nothing | ||
| latestLogFile' (Just flist) = Just $ last flist | ||
|
|
||
| -- | initialize log file at startup | ||
| -- may append to existing file | ||
| initializeRotator :: RotationParameters -> FileDescription -> IO (Handle, Integer, UTCTime) | ||
| initializeRotator rotation fdesc = do | ||
| let maxAge = toInteger $ rotation ^. rpMaxAgeHours | ||
| maxSize = toInteger $ rotation ^. rpLogLimitBytes | ||
|
|
||
| latest <- latestLogFile fdesc | ||
| case latest of | ||
| Nothing -> -- no file to append, return new | ||
| evalRotator rotation fdesc | ||
| Just fname -> do | ||
| -- check date | ||
| now <- getCurrentTime | ||
| tsfp <- parseTimeM True defaultTimeLocale tsformat $ drop (fplen + 1) fname | ||
| if (round $ diffUTCTime now tsfp) > (3600 * maxAge) | ||
| then do -- file is too old, return new | ||
| evalRotator rotation fdesc | ||
| else do | ||
| hdl <- catchIO (openFile (prefixpath fdesc </> fname) AppendMode) $ | ||
| \e -> do | ||
| prtoutException fname e | ||
| return stdout -- fallback to standard output in case of exception | ||
| hSetBuffering hdl LineBuffering | ||
| cursize <- hFileSize hdl | ||
| let rottime = addUTCTime (fromInteger $ maxAge * 3600) now | ||
| return (hdl, (maxSize - cursize), rottime) | ||
| where | ||
| fplen = length $ filename fdesc | ||
|
|
||
| -- | remove old files; count them and only keep n (from config) | ||
| cleanupRotator :: RotationParameters -> FileDescription -> IO () | ||
| cleanupRotator rotation fdesc = do | ||
| let keepN0 = fromIntegral (rotation ^. rpKeepFilesNum) :: Int | ||
| keepN = max 1 $ min keepN0 99 | ||
| listLogFiles fdesc >>= removeOldFiles keepN | ||
| where | ||
| removeOldFiles :: Int -> Maybe (NonEmpty FilePath) -> IO () | ||
| removeOldFiles _ Nothing = return () | ||
| removeOldFiles n (Just flist) = do | ||
| putStrLn $ "dropping " ++ (show n) ++ " from " ++ (show flist) | ||
| removeFiles $ reverse $ NE.drop n $ NE.reverse flist | ||
| removeFiles [] = return () | ||
| removeFiles (fp : fps) = do | ||
| let bp = prefixpath fdesc | ||
| filepath = bp </> fp | ||
| putStrLn $ "removing file " ++ filepath | ||
| removeFile filepath -- destructive | ||
| removeFiles fps | ||
|
|
||
| {- | ||
|
|
||
| testing: | ||
|
|
||
| lc0 <- parseLoggerConfig "../log-configs/testing.yaml" | ||
| lc <- setLogPrefix (Just "/tmp/testlog/") lc0 | ||
| lh <- setupLogging lc | ||
|
|
||
| usingLoggerName lh "testing" $ do { forM_ [1..299] (\n -> logDebug $ T.pack $ "hello world " ++ (show n)) } | ||
| -} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some log-config files contain as filename a path like this:
pub/node.pubin that case, we add the directory path to the prefix dir and keep the filename apart.