I would like to capture output from a UNIX process but limit max file size and/or rotate to a new file.
I have seen logrotate, but it does not work real-time. As I
Another solution will be to use Apache rotatelogs utility.
Or following script:
#!/bin/ksh
#rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]
numberOfFiles=10
while getopts "n:fltvecp:L:" opt; do
case $opt in
n) numberOfFiles="$OPTARG"
if ! printf '%s\n' "$numberOfFiles" | grep '^[0-9][0-9]*$' >/dev/null; then
printf 'Numeric numberOfFiles required %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$numberOfFiles" 1>&2
exit 1
elif [ $numberOfFiles -lt 3 ]; then
printf 'numberOfFiles < 3 %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$numberOfFiles" 1>&2
fi
;;
*) printf '-%s ignored. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$opt" 1>&2
;;
esac
done
shift $(( $OPTIND - 1 ))
pathToLog="$1"
fileSize="$2"
if ! printf '%s\n' "$fileSize" | grep '^[0-9][0-9]*[BKMG]$' >/dev/null; then
printf 'Numeric fileSize followed by B|K|M|G required %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$fileSize" 1>&2
exit 1
fi
sizeQualifier=`printf "%s\n" "$fileSize" | sed "s%^[0-9][0-9]*\([BKMG]\)$%\1%"`
multip=1
case $sizeQualifier in
B) multip=1 ;;
K) multip=1024 ;;
M) multip=1048576 ;;
G) multip=1073741824 ;;
esac
fileSize=`printf "%s\n" "$fileSize" | sed "s%^\([0-9][0-9]*\)[BKMG]$%\1%"`
fileSize=$(( $fileSize * $multip ))
fileSize=$(( $fileSize / 1024 ))
if [ $fileSize -le 10 ]; then
printf 'fileSize %sKB < 10KB. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$fileSize" 1>&2
exit 1
fi
if ! touch "$pathToLog"; then
printf 'Could not write to log file %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$pathToLog" 1>&2
exit 1
fi
lineCnt=0
while read line
do
printf "%s\n" "$line" >>"$pathToLog"
lineCnt=$(( $lineCnt + 1 ))
if [ $lineCnt -gt 200 ]; then
lineCnt=0
curFileSize=`du -k "$pathToLog" | sed -e 's/^[ ][ ]*//' -e 's%[ ][ ]*$%%' -e 's/[ ][ ]*/[ ]/g' | cut -f1 -d" "`
if [ $curFileSize -gt $fileSize ]; then
DATE=`date +%Y%m%d_%H%M%S`
cat "$pathToLog" | gzip -c >"${pathToLog}.${DATE}".gz && cat /dev/null >"$pathToLog"
curNumberOfFiles=`ls "$pathToLog".[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9].gz | wc -l | sed -e 's/^[ ][ ]*//' -e 's%[ ][ ]*$%%' -e 's/[ ][ ]*/[ ]/g'`
while [ $curNumberOfFiles -ge $numberOfFiles ]; do
fileToRemove=`ls "$pathToLog".[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9].gz | head -1`
if [ -f "$fileToRemove" ]; then
rm -f "$fileToRemove"
curNumberOfFiles=`ls "$pathToLog".[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9].gz | wc -l | sed -e 's/^[ ][ ]*//' -e 's%[ ][ ]*$%%' -e 's/[ ][ ]*/[ ]/g'`
else
break
fi
done
fi
fi
done
or using awk
program | awk 'BEGIN{max=100} {n+=length($0); print $0 > "log."int(n/max)}'
It keeps lines together, so the max is not exact, but this could be nice especially for logging purposes. You can use awk's sprintf to format the file name.
Here's a pipable script, using awk
#!/bin/bash
maxb=$((1024*1024)) # default 1MiB
out="log" # output file name
width=3 # width: log.001, log.002
while getopts "b:o:w:" opt; do
case $opt in
b ) maxb=$OPTARG;;
o ) out="$OPTARG";;
w ) width=$OPTARG;;
* ) echo "Unimplented option."; exit 1
esac
done
shift $(($OPTIND-1))
IFS='\n' # keep leading whitespaces
if [ $# -ge 1 ]; then # read from file
cat $1
else # read from pipe
while read arg; do
echo $arg
done
fi | awk -v b=$maxb -v o="$out" -v w=$width '{
n+=length($0); print $0 > sprintf("%s.%0.*d",o,w,n/b)}'
save this to a file called 'bee', run 'chmod +x bee
' and you can use it as
program | bee
or to split an existing file as
bee -b1000 -o proglog -w8 file
To limit the size to 100 bytes, you can simply use dd:
my_program | dd bs=1 count=100 > log
When 100 bytes are written, dd will close the pipe and my_program receives EPIPE.
Limiting the max size can also be done with head:
my_program | head -c 100 # Limit to 100 first bytes
See this for benefits over dd: https://unix.stackexchange.com/a/121888/
In package apache2-utils
is present utility called rotatelogs
, it fully meet to your requirements.
Synopsis:
rotatelogs [ -l ] [ -L linkname ] [ -p program ] [ -f ] [ -t ] [ -v ] [ -e ] [ -c ] [ -n number-of-files ] logfile rotationtime|filesize(B|K|M|G) [ offset ]
Example:
your_program | rotatelogs -n 5 /var/log/logfile 1M
Full manual you may read on this link.
use split:
my_program | tee >(split -d -b 100000 -)
Or if you don't want to see the output, you can directly pipe to split:
my_program | split -d -b 100000 -
As for the log rotation, there's no tool in coreutils that does it automatically. You could create a symlink and periodically update it using a bash command:
while ((1)); do ln -fns target_log_name $(ls -t | head -1); sleep 1; done