Okay, first things first:
Yes, I am aware of the logrotate tool, which would usually be used for something like this. However, since the logfile i want to rotate already contains a timestamp in the filename, logrotate will never delete old logs.
This script is supposed to:
- tell the application to create a new logfile (via SIGHUP)
- find all existing, unprocessed logfiles of the application
- compress all logfiles except the one currently in use by the application
- keep the most recent 7 logs and delete the rest.
From what I can tell, everything seems to work just fine, but I'm curious if anything could be improved.
#!/usr/bin/bash
procname="foo"
srcpath="/var/log"
srcname="*${procname}.log" # example: 2021-02-05_1200_foo.log
count=7
echo "rotate $procname logs: ${srcpath}/${srcname} ($count rotations)"
pid="$(pidof $procname)"
if [[ ! $? == 0 ]]
then
# don't rotate anything if the application is not running
echo "$procname process not running"
exit 1
fi
# ask the application to create a new logfile
kill -HUP "$pid"
sleep 1 #probably not necessary?
# get array of all logfiles (should be exactly 2 in most cases)
mapfile -t list < <(find "$srcpath" -maxdepth 1 -name "$srcname" | sort)
size=${#list[@]}
# don't do anything unless there are at least 2 files
if [[ $size -lt 2 ]]
then
echo "nothing to do"
exit 0
fi
# find the active logfile (should be the last one) and delete it from the array
for ((i=size-1; i>=0; i--));
do
if [[ $(find -L /proc/"$pid"/fd -inum "$(stat -c '%i' "${list[i]}")") ]]
then
unset "list[i]"
break
fi
done
# compress all remaining files (usually just one)
gzip "${list[@]}"
unset list
unset size
# get array of all compressed logfiles
mapfile -t list < <(find "$srcpath" -maxdepth 1 -name "$srcname.gz" | sort)
size=${#list[@]}
if [[ $size -gt $count ]]
then
idx=("${!list[@]}")
# remove $count most recent files from $list
for i in "${idx[@]: -$count:$count}"
do
unset "list[$i]"
done
# delete the remaining old files
rm -f "${list[@]}"
fi
exit 0