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


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

# 準備

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

```pl: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のファイルを作成します。

```bash:/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で受けるために、設定ファイルを作ります。

```bash:/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を再起動します。

```bash
sudo systemctl restart rsyslog
```

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

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

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

```bash
tail -f /var/log/test.log
```

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



# journaldの制限解除

単位時間に一定量以上のログを流すとフィルタリングされてしまいます。  
必要に応じて、その制限を解除しておきます。

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

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

```bash
sudo systemctl restart systemd-journald
```

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

```bash
sudo systemctl restart test
```
    
※もしかするとjournaldを再起動したら、システムまるごと再起動したほうがいいのかな・・・？

# logrotateの設定

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

```bash:/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（実際にローテートせずに実験）してみます。


```bash
$ 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
```

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