assistant: Logs are rotated to avoid them using too much disk space.
This cannot completely guard against a runaway log event, and only runs every hour anyway, but it should avoid most problems with very long-running, active assistants using up too much space.
This commit is contained in:
		
					parent
					
						
							
								fed56e24a4
							
						
					
				
			
			
				commit
				
					
						1865b28094
					
				
			
		
					 5 changed files with 73 additions and 34 deletions
				
			
		|  | @ -175,7 +175,7 @@ startDaemon assistant foreground startbrowser = do | ||||||
| 				fdToHandle =<< dup stdOutput | 				fdToHandle =<< dup stdOutput | ||||||
| 			origerr <- liftIO $ catchMaybeIO $  | 			origerr <- liftIO $ catchMaybeIO $  | ||||||
| 				fdToHandle =<< dup stdError | 				fdToHandle =<< dup stdError | ||||||
| 			liftIO $ Utility.Daemon.redirLog logfd | 			liftIO $ Utility.LogFile.redirLog logfd | ||||||
| 			showStart (if assistant then "assistant" else "watch") "." | 			showStart (if assistant then "assistant" else "watch") "." | ||||||
| 			start id $  | 			start id $  | ||||||
| 				case startbrowser of | 				case startbrowser of | ||||||
|  | @ -217,7 +217,8 @@ startDaemon assistant foreground startbrowser = do | ||||||
| 			, assist $ transferPollerThread | 			, assist $ transferPollerThread | ||||||
| 			, assist $ transfererThread | 			, assist $ transfererThread | ||||||
| 			, assist $ daemonStatusThread | 			, assist $ daemonStatusThread | ||||||
| 			, assist $ sanityCheckerThread | 			, assist $ sanityCheckerDailyThread | ||||||
|  | 			, assist $ sanityCheckerHourlyThread | ||||||
| 			, assist $ mountWatcherThread | 			, assist $ mountWatcherThread | ||||||
| 			, assist $ netWatcherThread | 			, assist $ netWatcherThread | ||||||
| 			, assist $ netWatcherFallbackThread | 			, assist $ netWatcherFallbackThread | ||||||
|  |  | ||||||
|  | @ -6,7 +6,8 @@ | ||||||
|  -} |  -} | ||||||
| 
 | 
 | ||||||
| module Assistant.Threads.SanityChecker ( | module Assistant.Threads.SanityChecker ( | ||||||
| 	sanityCheckerThread | 	sanityCheckerDailyThread, | ||||||
|  | 	sanityCheckerHourlyThread | ||||||
| ) where | ) where | ||||||
| 
 | 
 | ||||||
| import Assistant.Common | import Assistant.Common | ||||||
|  | @ -15,12 +16,19 @@ import Assistant.Alert | ||||||
| import qualified Git.LsFiles | import qualified Git.LsFiles | ||||||
| import Utility.ThreadScheduler | import Utility.ThreadScheduler | ||||||
| import qualified Assistant.Threads.Watcher as Watcher | import qualified Assistant.Threads.Watcher as Watcher | ||||||
|  | import Utility.LogFile | ||||||
| 
 | 
 | ||||||
| import Data.Time.Clock.POSIX | import Data.Time.Clock.POSIX | ||||||
| 
 | 
 | ||||||
| {- This thread wakes up occasionally to make sure the tree is in good shape. -} | {- This thread wakes up hourly for inxepensive frequent sanity checks. -} | ||||||
| sanityCheckerThread :: NamedThread | sanityCheckerHourlyThread :: NamedThread | ||||||
| sanityCheckerThread = namedThread "SanityChecker" $ forever $ do | sanityCheckerHourlyThread = namedThread "SanityCheckerHourly" $ forever $ do | ||||||
|  | 	liftIO $ threadDelaySeconds $ Seconds oneHour | ||||||
|  | 	hourlyCheck | ||||||
|  | 
 | ||||||
|  | {- This thread wakes up daily to make sure the tree is in good shape. -} | ||||||
|  | sanityCheckerDailyThread :: NamedThread | ||||||
|  | sanityCheckerDailyThread = namedThread "SanityCheckerDaily" $ forever $ do | ||||||
| 	waitForNextCheck | 	waitForNextCheck | ||||||
| 
 | 
 | ||||||
| 	debug ["starting sanity check"] | 	debug ["starting sanity check"] | ||||||
|  | @ -31,7 +39,7 @@ sanityCheckerThread = namedThread "SanityChecker" $ forever $ do | ||||||
| 		modifyDaemonStatus_ $ \s -> s { sanityCheckRunning = True } | 		modifyDaemonStatus_ $ \s -> s { sanityCheckRunning = True } | ||||||
| 
 | 
 | ||||||
| 		now <- liftIO $ getPOSIXTime -- before check started | 		now <- liftIO $ getPOSIXTime -- before check started | ||||||
| 		r <- either showerr return =<< tryIO <~> check | 		r <- either showerr return =<< tryIO <~> dailyCheck | ||||||
| 
 | 
 | ||||||
| 		modifyDaemonStatus_ $ \s -> s | 		modifyDaemonStatus_ $ \s -> s | ||||||
| 			{ sanityCheckRunning = False | 			{ sanityCheckRunning = False | ||||||
|  | @ -57,14 +65,11 @@ waitForNextCheck = do | ||||||
| 			oneDay - truncate (now - lastcheck) | 			oneDay - truncate (now - lastcheck) | ||||||
| 		| otherwise = oneDay | 		| otherwise = oneDay | ||||||
| 
 | 
 | ||||||
| oneDay :: Int |  | ||||||
| oneDay = 24 * 60 * 60 |  | ||||||
| 
 |  | ||||||
| {- It's important to stay out of the Annex monad as much as possible while | {- It's important to stay out of the Annex monad as much as possible while | ||||||
|  - running potentially expensive parts of this check, since remaining in it |  - running potentially expensive parts of this check, since remaining in it | ||||||
|  - will block the watcher. -} |  - will block the watcher. -} | ||||||
| check :: Assistant Bool | dailyCheck :: Assistant Bool | ||||||
| check = do | dailyCheck = do | ||||||
| 	g <- liftAnnex gitRepo | 	g <- liftAnnex gitRepo | ||||||
| 	-- Find old unstaged symlinks, and add them to git. | 	-- Find old unstaged symlinks, and add them to git. | ||||||
| 	(unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo False ["."] g | 	(unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo False ["."] g | ||||||
|  | @ -86,3 +91,32 @@ check = do | ||||||
| 	addsymlink file s = do | 	addsymlink file s = do | ||||||
| 		Watcher.runHandler Watcher.onAddSymlink file s | 		Watcher.runHandler Watcher.onAddSymlink file s | ||||||
| 		insanity $ "found unstaged symlink: " ++ file | 		insanity $ "found unstaged symlink: " ++ file | ||||||
|  | 
 | ||||||
|  | hourlyCheck :: Assistant () | ||||||
|  | hourlyCheck = checkLogSize 0 | ||||||
|  | 
 | ||||||
|  | {- Rotate logs until log file size is < 1 mb. -} | ||||||
|  | checkLogSize :: Int -> Assistant () | ||||||
|  | checkLogSize n = do | ||||||
|  | 	f <- liftAnnex $ fromRepo gitAnnexLogFile | ||||||
|  | 	logs <- liftIO $ listLogs f | ||||||
|  | 	totalsize <- liftIO $ sum <$> mapM filesize logs | ||||||
|  | 	when (totalsize > oneMegabyte) $ do | ||||||
|  | 		notice ["Rotated logs due to size:", show totalsize] | ||||||
|  | 		liftIO $ do | ||||||
|  | 			rotateLog f | ||||||
|  | 			logfd <- openLog f | ||||||
|  | 			redirLog logfd | ||||||
|  | 		when (n < maxLogs + 1) $ | ||||||
|  | 			checkLogSize $ n + 1 | ||||||
|  |   where | ||||||
|  | 	filesize f = fromIntegral . fileSize <$> liftIO (getFileStatus f) | ||||||
|  | 
 | ||||||
|  | oneMegabyte :: Int | ||||||
|  | oneMegabyte = 1000000 | ||||||
|  | 
 | ||||||
|  | oneHour :: Int | ||||||
|  | oneHour = 60 * 60 | ||||||
|  | 
 | ||||||
|  | oneDay :: Int | ||||||
|  | oneDay = 24 * oneHour | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| module Utility.Daemon where | module Utility.Daemon where | ||||||
| 
 | 
 | ||||||
| import Common | import Common | ||||||
|  | import Utility.LogFile | ||||||
| 
 | 
 | ||||||
| import System.Posix | import System.Posix | ||||||
| 
 | 
 | ||||||
|  | @ -40,16 +41,6 @@ daemonize logfd pidfile changedirectory a = do | ||||||
| 		out | 		out | ||||||
| 	out = exitImmediately ExitSuccess | 	out = exitImmediately ExitSuccess | ||||||
| 
 | 
 | ||||||
| redirLog :: Fd -> IO () |  | ||||||
| redirLog logfd = do |  | ||||||
| 	mapM_ (redir logfd) [stdOutput, stdError] |  | ||||||
| 	closeFd logfd |  | ||||||
| 
 |  | ||||||
| redir :: Fd -> Fd -> IO () |  | ||||||
| redir newh h = do |  | ||||||
| 	closeFd h |  | ||||||
| 	void $ dupTo newh h |  | ||||||
| 
 |  | ||||||
| {- Locks the pid file, with an exclusive, non-blocking lock. | {- Locks the pid file, with an exclusive, non-blocking lock. | ||||||
|  - Writes the pid to the file, fully atomically. |  - Writes the pid to the file, fully atomically. | ||||||
|  - Fails if the pid file is already locked by another process. -} |  - Fails if the pid file is already locked by another process. -} | ||||||
|  |  | ||||||
|  | @ -13,22 +13,24 @@ import System.Posix | ||||||
| 
 | 
 | ||||||
| openLog :: FilePath -> IO Fd | openLog :: FilePath -> IO Fd | ||||||
| openLog logfile = do | openLog logfile = do | ||||||
| 	rotateLog logfile 0 | 	rotateLog logfile | ||||||
| 	openFd logfile WriteOnly (Just stdFileMode) | 	openFd logfile WriteOnly (Just stdFileMode) | ||||||
| 		defaultFileFlags { append = True } | 		defaultFileFlags { append = True } | ||||||
| 
 | 
 | ||||||
| rotateLog :: FilePath -> Int -> IO () | rotateLog :: FilePath -> IO () | ||||||
| rotateLog logfile num | rotateLog logfile = go 0 | ||||||
| 	| num > maxLogs = return () |  | ||||||
| 	| otherwise = whenM (doesFileExist currfile) $ do |  | ||||||
| 		rotateLog logfile (num + 1) |  | ||||||
| 		renameFile currfile nextfile |  | ||||||
|   where |   where | ||||||
| 	currfile = filename num | 	go num | ||||||
| 	nextfile = filename (num + 1) | 		| num > maxLogs = return () | ||||||
| 	filename n | 		| otherwise = whenM (doesFileExist currfile) $ do | ||||||
| 		| n == 0 = logfile | 			go (num + 1) | ||||||
| 		| otherwise = rotatedLog logfile n | 			renameFile currfile nextfile | ||||||
|  | 	  where | ||||||
|  | 		currfile = filename num | ||||||
|  | 		nextfile = filename (num + 1) | ||||||
|  | 		filename n | ||||||
|  | 			| n == 0 = logfile | ||||||
|  | 			| otherwise = rotatedLog logfile n | ||||||
| 
 | 
 | ||||||
| rotatedLog :: FilePath -> Int -> FilePath | rotatedLog :: FilePath -> Int -> FilePath | ||||||
| rotatedLog logfile n = logfile ++ "." ++ show n | rotatedLog logfile n = logfile ++ "." ++ show n | ||||||
|  | @ -40,3 +42,13 @@ listLogs logfile = filterM doesFileExist $ reverse $ | ||||||
| 
 | 
 | ||||||
| maxLogs :: Int | maxLogs :: Int | ||||||
| maxLogs = 9 | maxLogs = 9 | ||||||
|  | 
 | ||||||
|  | redirLog :: Fd -> IO () | ||||||
|  | redirLog logfd = do | ||||||
|  | 	mapM_ (redir logfd) [stdOutput, stdError] | ||||||
|  | 	closeFd logfd | ||||||
|  | 
 | ||||||
|  | redir :: Fd -> Fd -> IO () | ||||||
|  | redir newh h = do | ||||||
|  | 	closeFd h | ||||||
|  | 	void $ dupTo newh h | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								debian/changelog
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/changelog
									
										
									
									
										vendored
									
									
								
							|  | @ -4,6 +4,7 @@ git-annex (4.20130228) UNRELEASED; urgency=low | ||||||
|   * Android: Enable test suite.  |   * Android: Enable test suite.  | ||||||
|   * webapp: Only show up to 10 queued transfers. |   * webapp: Only show up to 10 queued transfers. | ||||||
|   * Several improvements to Makefile and cabal file. Thanks, Peter Simmons |   * Several improvements to Makefile and cabal file. Thanks, Peter Simmons | ||||||
|  |   * assistant: Logs are rotated to avoid them using too much disk space. | ||||||
| 
 | 
 | ||||||
|  -- Joey Hess <joeyh@debian.org>  Wed, 27 Feb 2013 23:20:40 -0400 |  -- Joey Hess <joeyh@debian.org>  Wed, 27 Feb 2013 23:20:40 -0400 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Joey Hess
				Joey Hess