I have a process that's writing a lot of data to stdout, which I'm redirecting to a log file. I'd like to limit the size of the file by occasionally copying the current file to a new name and truncating it.
My usual techniques of truncating a file, like
cp /dev/null file
don't work, presumably because the process is using it.
Is there some way I can truncate the file? Or delete it and somehow associate the process' stdout with a new file?
FWIW, it's a third party product that I can't modify to change its logging model.
EDIT redirecting over the file seems to have the same issue as the copy above - the file returns to its previous size next time it's written to:
ls -l sample.log ; echo > sample.log ; ls -l sample.log ; sleep 10 ; ls -l sample.log
-rw-rw-r-- 1 user group 1291999 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1292311 Jun 11 2009 sample.log
Redirect the output using >> instead of >. This will allow you to truncate the file without the file growing back to its original size. Also, don't forget to redirect STDERR (2>&1).
So the end result would be:
myprogram >> myprogram.log 2>&1 &
as the file is being used, if you try to nullify it or something like that, sometimes it might "confuse" the app that's writing into the log file and it might not log anything after that.
What I'd try ot do is to set up a kind of proxy/filter for that log, instead of redirecting to file, redirect to a process or something that would get input and write to a rolling file.
Maybe it can be done by script otherwise you could write a simple app for that ( java or something else ). The impact on app performance should be quite small, but you'll have to run some tests.
Btw, your app, is it a stand-alone, web app, ... ? Maybe there are other options to be investigated.
Edit: there's also an Append Redirection Operator >> that I've personally never used, but it might not lock the file.
I downloaded and compiled the latest
coreutils
so I could havetruncate
available.Ran
./configure
andmake
, but did not runmake install
.All the compiled utilities appear in the "src" folder.
I ran
[path]/src/truncate -s 1024000 textfileineedtotruncate.log
on a 1.7 GB log file.
It did not change the size listed when using
ls -l
, but it did free up all the disk space - which is what I really needed to do before/var
filled up and killed the process.Thanks for the tip on "truncate"!
The interesting thing about those regrown files is that the first 128 KB or so will be all zeroes after you truncate the file by copying
/dev/null
over it. This happens because the file is truncated to zero length, but the file descriptor in the application still points immediately after its last write. When it writes again, the file system treats the start of the file as all zero bytes - without actually writing the zeroes to disk.Ideally, you should ask the vendor of the application to open the log file with the
O_APPEND
flag. This means that after you truncate the file, the next write will implicitly seek to the end of the file (meaning back to offset zero) and then write the new information.This code rigs standard output so it is in
O_APPEND
mode and then invokes the command given by its arguments (rather likenice
runs a command after adjusting its nice-level, ornohup
runs a command after fixing things so it ignores SIGHUP).My testing of it was somewhat casual, but just barely enough to persuade me that it worked.
Simpler alternative
Billy notes in his answer that '
>>
' is the append operator - and indeed, on Solaris 10, bash (version 3.00.16(1)) does use theO_APPEND
flag - thereby making the code above unnecessary, as shown ('Black JL:' is my prompt on this machine):Use append redirection rather than the wrapper ('cantrip') code above. This just goes to show that when you use one particular technique for other (valid) purposes, adapting it to yet another is not necessarily the simplest mechanism - even though it works.
Did you check the behavior of any signals like SIGHUP to the third party product, to see if it will start logging a fresh file? You would move the old file to a permanent name, first.
kill -HUP [process-id]
And then it would start writing out again.
Alternatively (as Billy suggested) maybe redirecting the output from the application to a logging program like multilog or the one that is commonly used with Apache, known as cronolog. Then you'll have more fine grained control of where everything goes before it is written to that initial file descriptor (file), which is really all it is.
instead of redirecting it to a file you could pipe it to a program that automatically rotates the file by closing it, moving it and opening a new one every time it gets too big.