7

I'm running Rails 5 on Ubuntu 14.04. Is there a way to rotate my logs without relying on the Linux logrotate system? I have this set up ...

myuser@myapp:~$ cat /etc/logrotate.d/myapp
/home/rails/myapp/log/*.log {
  daily
  missingok
  rotate 2
  compress
  delaycompress
  notifempty
  copytruncate
}

but yet my logs never rotate. Behold how bloated they are ...

myuser@myapp:~$ ls -al /home/rails/myapp/log/
total 3958356
drwxr-xr-x  2 rails rails       4096 Jul  3 22:31 .
drwxr-xr-x 15 rails rails       4096 Sep 21 17:21 ..
-rw-rw-r--  1 rails rails          0 Jun 22 10:22 development.log
-rw-rw-r--  1 rails rails      14960 Jun  1 22:39 development.log.1
-rw-rw-r--  1 rails rails          0 Oct 22  2016 .keep
-rw-r--r--  1 rails rails  198362787 Oct 31 16:28 production.log
-rw-r--r--  1 rails rails    8615654 Jul  3 22:31 production.log.1
-rw-r--r--  1 rails rails  640621243 Jun 29 13:16 production.log.2.gz
-rw-rw-r--  1 rails rails 2856792698 Oct 31 17:12 sidekiq.log
-rw-rw-r--  1 rails rails  348853619 Jul  3 22:31 sidekiq.log.1
-rw-rw-r--  1 rails rails          0 Jul  3 22:31 test.log
-rw-rw-r--  1 rails rails      54246 Jul  3 22:31 test.log.1

Is there another way to get the logs rotated or is there a way to fix the configuration I have included?

Edit: Here's the cron script that's set

myuser@myapp:~$ cat /etc/cron.daily/logrotate
#!/bin/sh

# Clean non existent log file entries from status file
cd /var/lib/logrotate
test -e status || touch status
head -1 status > status.clean
sed 's/"//g' status | while read logfile date
do
    [ -e "$logfile" ] && echo "\"$logfile\" $date"
done >> status.clean
mv status.clean status

test -x /usr/sbin/logrotate || exit 0
/usr/sbin/logrotate /etc/logrotate.conf

Edit: Per the comment I tried adding this to my config/environment/production.rb file ...

config.logger = ActiveSupport::Logger.new(config.paths['log'].first, 1, 50 * 1024 * 1024)

but the logs get ever bigger without being rotated.

Dave
  • 15,639
  • 133
  • 442
  • 830
  • How often is the cron job run? What's the command that runs the cron job? If it's just a script in `/etc/cron.daily` (probably `etc/cron.daily/logrotate`), what are its contents? What are the contents of the global logrotate conf file (probably at `/etc/logrotate.conf`)? – Benjamin W. Nov 01 '17 at 05:20
  • Do I have to explicitly tell logrotate to run? I thought it was one of those Linux utilities that runs automatically. – Dave Nov 01 '17 at 13:39
  • It's run by a cron job. On Ubuntu, it's typically `/etc/cron.daily/logrotate` and it's already there, so changing the confs *should* start getting things rotated. – Benjamin W. Nov 01 '17 at 13:41
  • I added the script as an edit to my question. I"m not sure how to change it though to get things to run. – Dave Nov 01 '17 at 17:03
  • Is there a reason you don't want to use the environment file in your config directory ala https://stackoverflow.com/a/37499712/627702 ? – SWoo Nov 01 '17 at 17:06
  • You also have to add `/etc/logrotate.conf` to the question, as the settings in there are combined with the ones in your `myapp` conf file. – Benjamin W. Nov 01 '17 at 17:14
  • @SWoo, using the logic in the question you referenced, what happens to the logs once it exceeds the size set? Is it deleted, archived? – Dave Nov 02 '17 at 21:32
  • What I have seen in production servers is that the log files are rotated when the size is exceeded, up to the max number of files to rotate. Oldest file is deleted. You can set this on your development server on localhost and set the size really small to play with it to see if it does what you want. – SWoo Nov 03 '17 at 19:20
  • What's the output of `logrotate -d /etc/logrotate.conf`? And what does `/var/lib/logrotate/status` look like? – Benjamin W. Nov 12 '17 at 22:36
  • @Dave you might want to take a look at this post https://stackoverflow.com/questions/4883891/ruby-on-rails-production-log-rotation – chanakya devraj Nov 13 '17 at 07:11
  • I'm already using logrotate. I posted my config and results in teh question. – Dave Nov 14 '17 at 20:11

3 Answers3

8

By following the 12-factor methodologies (Treat logs as event streams) you could "should" delegate this task to the supervisor.

For example, by using immortal it will do all the rotation process for you not depending on the operating system.

A basic configuration file (run.yml) may look like:

cmd: bundle exec unicorn -c unicorn.rb
cwd: /arena/app-1
env:
    DEBUG: 1
    ENVIRONMENT: production
log:
    file: /var/log/app-1.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

In case you would like to split the logs stderr & stdout this could be used:

cmd: bundle exec unicorn -c unicorn.rb
cwd: /arena/app-1
env:
    DEBUG: 1
    ENVIRONMENT: production
log:
  file: /var/log/app.log
  age: 86400 # seconds
  num: 7     # int
  size: 1    # MegaBytes
stderr:
  file: /var/log/app-error.log
  age: 86400 # seconds
  num: 7     # int
  size: 1    # MegaBytes
  timestamp: true # will add timesamp to log

As a side note, from the 12-factor site:

Twelve-factor app processes should never daemonize or write PID files. Instead, rely on the operating system’s process manager to manage output streams, respond to crashed processes, and handle user-initiated restarts and shutdowns.

[Disclaimer: I'm the author of immortal], and one of the ideas behind was indeed to cover the automation of applications at scale without need to worry about filling up the disk when logs where not properly rotated besides being available to deploy (start/restart) in the easiest possible way by just modifying a run.yml file without requiring root privileges.

nbari
  • 25,603
  • 10
  • 76
  • 131
  • Where on my file system does run.yml get installed? – Dave Nov 11 '17 at 19:36
  • @Dave check [immortaldir](https://immortal.run/post/immortaldir/) you could add your files in `/etc/immortal/` this will keep them persistent across reboots, or if just want to test you could just run `immortal -c your.yml` – nbari Nov 11 '17 at 19:38
  • @Dave Probably also this could help https://immortal.run/post/systemd/ It should work for Ubuntu 14 and also new versions >=16 – nbari Nov 11 '17 at 19:56
  • I'm having a lot of trouble installing immortal following their directions. When I run "git clone git@github.com:immortal/immortal.git $HOME/go/src/github.com/immortal/immortal", I get a "Permission denied (publickey)." error. When I download the immortal zip and run make, I get a "make: execvp: go: Permission denied" error. Why couldn't they just make a yum install like everyone else?! – Dave Nov 14 '17 at 20:25
  • @Dave try to download a precompiled version probably is easier https://github.com/immortal/immortal/releases, any help creating a `yum` file will be more than welcome, in any case, please feel free to create any issue/question here: https://github.com/immortal/immortal/issues – nbari Nov 14 '17 at 21:04
  • @Dave try downloading https://github.com/immortal/immortal/releases/download/0.17.0/immortal_0.17.0_amd64.deb and try with `sudo dpkg -i immortal_0.17.0_amd64.deb` – nbari Nov 14 '17 at 21:11
  • Cool, thx, I think that "dpkg" command worked in as far as I got no errors and it said "Setting up immortal (0.17.0) ...". However, when I went to create that run.yml file, I didnt' see a "/etc/immortal/" directory. Is taht something I need to create on my own or is there another setup step? – Dave Nov 14 '17 at 21:20
  • @Dave please check this https://immortal.run/post/how-to-install/ should guide you to setup immortal on your system, in case need anything else please let me know. – nbari Nov 15 '17 at 09:52
  • Thanks. Two more questions I couldn't figure out from teh docs -- in the run.yml file, what is supposed to go here -- "cwd: /arena/app-1"? Also, I would liek to rotate both my production.log and sidekiq.log files, but the "file:" seems to only allow for a single file. How can I incorporate multiple files here? – Dave Nov 15 '17 at 15:52
  • `cwd` is for change directory, so before you run the process it changes to that directory, for sidekiq you may create a `sidekiq.yml` you can have as many `*.yml` files just be aware that your applications should write to `stdout` or `stderr` – nbari Nov 15 '17 at 17:20
  • Oh ok so I shoudl be "cwd"'ing to the directory from where I Ican run "bundle exec unicorn -c unicorn.rb"? Btw, why do I need to run this command? – Dave Nov 15 '17 at 20:12
  • @Dave you don't need that option is just an example, please check the project site https://immortal.run for more info and give a try to the multiple options should be pretty straightforward, in case that you found an issue please use this: https://github.com/immortal/immortal/issues – nbari Nov 15 '17 at 21:21
  • I don't need which option -- the "cwd" or the "bundle exec unicorn" option? – Dave Nov 15 '17 at 21:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159098/discussion-between-nbari-and-dave). – nbari Nov 15 '17 at 22:04
3

You can use ruby Logger class and shift_age method to select frequency of rotation: daily, weekly or monthly.

shift_age: Number of old log files to keep, or frequency of rotation (daily, weekly or monthly). Default value is 0, which disables log file rotation.

https://ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html#method-c-new

To use it in Rails you can insert a line like this in your config/application.rb:

config.logger = ActiveSupport::Logger.new("log/#{Rails.env}.log", shift_age = 'daily')

Note: today's log will not have any date, only tomorrow (if daily rotation) the old log will be 'rotated' and date appended to filename.

Paulo Belo
  • 3,759
  • 1
  • 22
  • 20
1

Recommending using STDOUT for app logging is a terrible idea. The Twelve-factor app is highly focused for container-based deployment when UNIX daemons are not welcomed, you should not buy this just as-is.

Standard output has no structure, it's just a plain-text interface. While it makes much sense for UNIX multi-process communication, it's just awful for logging. The only proper logging interface has always been either syslog or system journal. Our Rails app takes advantage of both and you can configure it either with STDOUT (for dev environments), syslog or journald for structured logging.

This way you can embed things like request, session or correlation id along with all messages. Logs can be sent over the wire to logging servers easily. Log file rotation is no problem at all if you use this approach. And finally, you can integrate with central logging solutions like ElasticSearch (or ELK stack) for further data analysis.

Indeed it's a little bit of work and unfortunately Rails developers are very opinionated about this - they use super-simple and not flexible Ruby logger, they also send input parameters to INFO level log and refuse to make it a configurable option (some APIs have huge inputs in our app which floods log). But it is what it is - you can override this in your app and give Rails a proper logging stack. Our solution:

lzap
  • 16,417
  • 12
  • 71
  • 108