Setting up Ansible for hourly Rails log rotation

By Daniele, on 1월 2018

Logs are an important part of any web application. Too few logs make debugging issues a problem. On the flip-side, too many logs can make a disk max out of space, or finding a specific error harder than it should be.

I was recently tasked with setting up hourly log rotation for a server that was running Rails, deployed via Capistrano.

Here is the Ansible setup which I used to programmatically add the rotate logic to cron. Please note that this setup is for an Ubuntu server, and requires that you input the sudo password when running the Ansible playbook. It also sets up the cron job to run under the root user's cron file. It would be possible to have it run on another user's cron file but that option could wipe out the crontab.

The directory structure

The file content

000_cross_group_env_vars.yml

The files ./production/group_vars/all/000_cross_group_env_vars.yml and ./staging/group_vars/all/000_cross_group_env_vars.yml should be symlinks to the file 000_cross_group_env_vars.yml which is at the base of the directory tree. The latter should contain something like the following:

---
base_os:
  packages:
    - vim
    - cron
    - logrotate
logrotate:
  rails_file: /etc/logrotate.d/rails
You can then create the symlinks with the following commands:
cd environments/staging/group_vars/all
ln -s ../../../../000_cross_group_env_vars.yml ./000_cross_group_env_vars.yml
cd environments/production/group_vars/all
ln -s ../../../../000_cross_group_env_vars.yml ./000_cross_group_env_vars.yml
./bin/provision

This is a simple bash file which should be executable (i.e. you should run chmod +x provision)

#!/bin/bash
ansible-playbook -i ./environments/$@ playbook.yml --ask-become-pass
hosts

These files should contain a list of the staging or production servers, for example:

[all]
123.50.180.123
./roles/base_os/tasks/main.yml

This should contain the rules to install the base packages which are necessary on your server:

---
- name: Ensure required packages installed
  apt:
    update_cache: yes
    name: "{{ item }}"
    state: latest
  with_items: "{{ base_os.packages }}"
./roles/logrotate/tasks/main.yml

This should copy over the logrotate file and create the cron entry:

---
- name: Add Rails app logrotate rules
  template:
    src: templates/logrotate_rails.j2
    dest: "{{ logrotate.rails_file }}"
    owner: root
    group: root
    mode: '0644'
    force: yes

- name: Set up cron
  cron:
    name: Hourly log rotation
    minute: "0"
    hour: "*"
    user: root
    job: "/usr/sbin/logrotate -f {{ logrotate.rails_file }} > /dev/null"
./roles/logrotate/templates/logrotate_rails.j2

This file contains the template with the logrotate directives:

# {{ ansible_managed }}

# Path to your Rails app:
"/var/www/app/current/log/*.log" {
  hourly
  su deploy deploy
  size 25M
  maxsize 50M
  rotate 150
  missingok
  notifempty
  compress
  delaycompress
  copytruncate
}
playbook.yml

This is the main entry-point that the Ansible playbook will run:

---
- hosts: all
  become: true
  remote_user: deploy  # The remote user that the ansible script will run as
  roles:
    - base_os
    - logrotate

Conclusion

With this setup in place you will be able to easily add log rotation to a Rails application via Ansible, simply by running:

./bin/provision staging
# or
./bin/provision production

Back