Help us understand the problem. What is going on with this article?

systemd環境でアプリのログをsyslogに流してlogrotateする

初めに

perlにはstart_serverというデーモン管理プログラムがあるのですが、
これがlogrotate時にログファイルを再オープンするような機能を、
どうも持ってなさそうだったので、別の方法として表題の手法を取りました。
(環境はCentOS7です)

準備

例えば下記のようなwebアプリがあったとして
(例はperlですが、別にrubyでもpythonでもnodeでも何でも構いません)

app.psgi
use strict;
use warnings;
use 5.010;

use Plack::Request;

my $i   = 0;
my $app = sub {
        my ($env)       = @_;
        my $req         = Plack::Request->new($env);

        say "STDOUT : request $i";
        warn "STDERR : request $i";
        $i++;

        my $res = $req->new_response(200);
        $res->body("$i\n");
        return $res->finalize;
};

$app;

こいつをデーモンとして起動するとします。

systemd設定

perlのstart_serverを利用するとしたら、
例えば下記のようなsystemdのファイルを作成します。

/etc/systemd/system/test.service
[Unit]
Description = test Daemon

[Service]
Environment = "PATH=/bin:/usr/bin:/home/vagrant/.anyenv/envs/plenv/shims/"
ExecStart = /home/vagrant/.anyenv/envs/plenv/shims/start_server --backlog 1024 --port 5000 --pid /tmp/test.pid --status-file /tmp/test.status  --signal-on-hup=USR1 -- plackup -s Gazelle --workers=1 /home/vagrant/mysrc/perl/log-test/app.psgi
Restart = always
Type = simple
User = vagrant
Group = vagrant
LimitNOFILE = 60000

StandardOutput=syslog # キモ
StandardError=syslog  # キモ

SyslogIdentifier=test-daemon

[Install]
WantedBy = multi-user.target

キモと記載している部分が重要で、
標準出力、標準エラー出力をsyslogに流してくれます。

rsyslogd設定

先のデーモンログをrsyslogdで受けるために、設定ファイルを作ります。

/etc/rsyslog.d/test-daemon.conf
if $programname == 'test-daemon' then /var/log/test.log
# messagesに出るのを抑止する
if $programname == 'test-daemon' then ~

"test-daemon"の部分は、serviceファイルの SyslogIdentifier で記載した名前にします。

rsyslogdを再起動します。

sudo systemctl restart rsyslog

これで、/var/log/test.logにアプリケーションのログが出力されます。

while [ 1 ] ; do curl http://localhost:5000/; done

とかでアクセスをしてみて、

tail -f /var/log/test.log

でログがでていればOKです。

journaldの制限解除

単位時間に一定量以上のログを流すとフィルタリングされてしまいます。

必要に応じて、その制限を解除しておきます。

/etc/systemd/journald.conf
[Journal]
...
#RateLimitInterval=30s
#無制限にする
RateLimitInterval=0
...

この後に、journaldの再起動をします。

sudo systemctl restart systemd-journald

デーモン側のプロセスも再起動しないと、ログが全く出てこなくなったので、
そちらも再起動しておきます。

sudo systemctl restart test

※もしかするとjournaldを再起動したら、システムまるごと再起動したほうがいいのかな・・・?

logrotateの設定

/etc/logrotate.d/ 配下に設定ファイルを作成します。

/etc/logrotate.d/test
/var/log/test.log
{
    missingok
    notifempty
    compress
    compresscmd /usr/bin/xz
    compressoptions -6
    rotate 90
    daily
    sharedscripts
    postrotate
        /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}

dry-run(実際にローテートせずに実験)してみます。

$ logrotate -dv /etc/logrotate.d/test
reading config file /etc/logrotate.d/test
compress_prog is now /usr/bin/xz
compress_options is now  -6
Allocating hash table for state file, size 15360 B

Handling 1 logs

rotating pattern: /var/log/test.log
 after 1 days (90 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/test.log
  log does not need rotating (log has been already rotated)not running postrotate script, since no logs were rotated

ちゃんとローテートできそうですね。

radiko
スマホやPC、スマートスピーカーでラジオ放送が聴けるネットサービス『radiko』の企画・開発・運営を手がけています。
https://radiko.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away