22

I have a simple script which outputs a bunch of logs to screen and I piped the STDOUT to a file to store the logs. Since this script is long running, I needed to rotate the log files so they are chucked into smaller more manageable ones.

The problem I faced was that once the logrotate moves the current log file into a new one, the newly created log file is not populated with the logs anymore. It seems that the once the original log file is removed, its file handler is lost and redirection won't work anymore.

I also found this post which had the same problem as me and claims that it can be fixed by using >> instead of > to redirect the output. I tested his solution but it didn't work for me. Does anyone have any idea how to keep the redirection work?

Mehran
  • 519
  • 1
  • 5
  • 19
  • 4
    I'd still recommend, in your case, to use ">>" instead of ">" if you intend to write to a truncated file : as ">>" open in append mode, it will seek until the end of the file each time it writes. That way, when you truncate the file (making it go from XXXX bytes to 0 bytes), it will "seek until end", so will know it now has to write after byte 0. Otherwise it may write after byte XXXX, and thus create a sparse file with XXXX null bytes before it (ie, when ">", the fd can just remember where it was in that file, and write from there, without realising the file size shrunk!) – Olivier Dulac Jul 03 '14 at 13:26

4 Answers4

25

You should use the copytruncate directive in your logrotate config for this log file.

copytruncate Truncate the original log file in place after creating a copy, instead of moving the old log file and optionally creating a new one. It can be used when some program cannot be told to close its logfile and thus might continue writing (appending) to the previous log file forever. Note that there is a very small time slice between copying the file and truncating it, so some logging data might be lost. When this option is used, the create option will have no effect, as the old log file stays in place

user9517
  • 114,104
  • 20
  • 206
  • 289
  • 2
    May be worth mentioning: For a short time, before the `compress` operation, the data is duplicated. That caused us an issue once but that was our bad since we shouldn't have been that close to the `lv` space limit. Also, as stated in the `man` snippet, you may lose some log data in between the copy and truncate operations. – Belmin Fernandez Jul 03 '14 at 11:55
6

As an alternative, you may also:

  • use the logger utility in your script instead of piping, with a dedicated facility (e.g. local5), for example:

    logger -p local5.info -t myscriptname "this is some log data"

  • configure syslog to write this facility to wanted log file, example (rsyslog.conf) :

    local5.* /var/log/mylogfile

  • setup logrotate rule for this log.

user9517
  • 114,104
  • 20
  • 206
  • 289
tonioc
  • 1,017
  • 8
  • 11
  • This only works if you have explicit output commands like `echo`. The output of third party tools which are called from the script and also output something cannot be redirected to the logger this way – Daniel Alder Jun 08 '15 at 14:22
4

Another alternative to Iain solution is to use a postrotate script to relaunch your script once the rotation has taken place. This is done for lots of daemons (restart or reload the daemon), but not knowing your script I don't know if this solution will suit you or not (does your script depend on some state generated a while ago?).

Contents of /etc/logrotate.d/your-script-name:

/var/log/your-script-name.log {
    # your current logrotate options
    ...
    postrotate
        # this supposing you have the current pid stored
        cat /run/your-script-name.pid | xargs -r kill
        #relaunch it again
        /usr/local/bin/your-script-name
    endscript
}
Carlos Campderrós
  • 763
  • 2
  • 6
  • 17
0

You can pipe stdout to "split" (part of coreutils in linux). It allows you to split file/stdin into chunks based on size, number of lines, etc. Once you get it chunked you can manage it with logrotate if needed.

Nazar
  • 111