1

I want to rotate our application logs using logrotate. After each rotation, it executes a script which moves all rotated logs to another directory. For example in /home/dev/logs/frontend, I have these log files:

webapp1-access.log
webapp2-access.log
anotherapp3-access.log
codename-access.log
...

And my logrotate config file:

$ cat app.daily
/home/dev/logs/frontend/*access.log {
   rotate 1
   daily
   copytruncate
   compress
   notifempty
   missingok
   lastaction
      bash /path/to/script.sh
   endscript
}

And what script.sh does is to create a directory based on the log file name then move the rotated log file there. The webapp1-access.log would be rotated to webapp1-access.log.1.gz then the script will move it to /x/y/webbapp1/renamed.log.gz. And so on for other rotated logs.

When I tested the logrotate using logrotate -fv /path/to/config, and It works perfectly as I expected. Then I put the logrotate config file to /etc/logrotate.d (as a symlink).

Next day I checked /x/y dir, I got:

webbapp1/                       <---- created from initial logrotate -fv
webbapp1-access.log/            <---- Unexpected
anotherapp3/                    <---- created from initial logrotate -fv
anotherapp3-access.log/         <---- Unexpected
codename/                       <---- created from initial logrotate -fv
codename-access.log/            <---- Unexpected

The *-access.log/ dirs are created by the logrotate. But running logrotate -vf config multiple times wont produce that unexpected result. That happens when I leave logrotate do its daily rotation.

Why is this happening? How can I fix this?


script.sh

#! /usr/bin/bash

exec 3>> /var/log/archived-log.log
[ "${1:-}" = "-d" ] && debug=1

environment="frontend"

rotateddir="/home/dev/logs/$environment"
destdir="/x/y"
[ "${debug:-}" ] && echo "DEST DIR: $destdir"

log() {
    timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "$timestamp: $1" >&3
}

for archive in $rotateddir/*.gz; do
    [ "${debug:-}" ] && echo "ARCHIVE: $archive"
    [ "${archive##*/}" = "*.gz"  ] && continue
    base_name=$(basename "$archive")
    [ "${debug:-}" ] && echo "BASENAME: $base_name"
    extension="${base_name##*.}"
    [ "${debug:-}" ] && echo "EXT: $extension"
    newdir_name="${base_name%-*}"
    [ "${debug:-}" ] && echo "NEWDIR_NAME: $newdir_name"
    tanggal=$(stat -c %y "$archive" | cut -d" " -f1 | { read dat; date -d $dat +%Y%m%d; })
    #jam=$(stat -c %y "$archive" | cut -d" " -f2 | { read dat; date -d $dat +%H%M; })
    newdir_path="$destdir/$newdir_name"
    [ "${debug:-}" ] && echo "NEWDIR_PATH: $newdir_path"
    #dest_archive="$newdir_path/$tanggal-$jam.log.$extension"
    dest_archive="$newdir_path/$tanggal.log.$extension"
    [ "${debug:-}" ] && echo "DEST_ARCHIVE: $dest_archive"

    [ ! -d "$newdir_path" ] && {
        if [ "${debug:-}" ]; then
            echo "Would create $newdir_path"
        else
            mkdir -p "$newdir_path"
            log "Created directory: $newdir_path"
        fi
    }

    [ ! -f "$dest_archive" ] && {
        if [ "${debug:-}" ]; then
            echo "Would move $archive to $dest_archive"
        else
            rsync -a --no-owner --no-group --remove-source-files "$archive" "$dest_archive"
            #cp -u --no-preserve=mode,ownership "$archive" "$dest_archive" && rm -f "$archive"
            log "Relocated $archive to $dest_archive"
        fi
    }

    rotates=$(ls -1 "$newdir_path" | wc -l )
    [ "$rotates" -ge 7 ] && {
        oldest=$(ls -t "$newdir_path" | tail -1)
        if [ "${debug:-}" ]; then
            echo "Would delete the oldest archive: $oldest"
        else
            rm -f "$newdir_path/$oldest"
            log "Deleted oldest archive of $base_name : $oldest"
        fi
    }   
done

exit 0



Update

I still don't know why this happens, but finally I decided to run the script by cron few minutes after rotation.

I suspect that this part of my "script.sh" newdir_name="${base_name%-*}" is not evaluated well by logrotate. But again, it worked as expected if I run it forcefully using logrotate -fv config

1
  • Shouldn't be hard to see if "newdir_name="${base_name%-*}" is not evaluated well by logrotate. " - put a set -x at the top and check the stderr.
    – muru
    Mar 10, 2020 at 2:37

1 Answer 1

0

Turns out that logrotate executes the script using sh and my script contains bashishms.

From man page:

lastaction/endscript

The lines between lastaction and endscript (both of which must appear on lines by themselves) are executed (using /bin/sh) ...

In the end, I execute my script via crontab few minutes after the logrotate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.