現在、Fedora は journald と rsyslog で二重にログを取っているような状態になっています。rsyslog を停止したいのですが、私のところでは logwatch が rsyslog のログファイルを参照しているので、止められません。そこで、rsyslog なしで logwatch が動くようにします。

Logwatch の設定を変更する(不採用)

この項は「知らなくて変な方法を採用しているわけではないですよ」アピールなので、興味がなければ読み飛ばしてください。

logwatch は journald のログを直接参照することができます。ですが、今回、この方法は採用しません。いくつか理由があります。

  • 設定の工数が大きいです。logwatch がサポートする全サービスを使用しているわけではないにせよ、サービス一つ一つについて設定ファイルを作成する必要があります。不要なサービスの設定を省略するにしても、このサービスは本当に設定しなくてよいのか? という判断が難しいものもあります。
  • 将来的に、logwatch が別の仕組みを採用した場合、設定の切り戻し工数が大きくなります。今回のように独自に対策を採る場合、こうした切り戻しについてあらかじめ考えておく必要があります。
  • journalctl -u (タグ指定)のログの抽出が不安定です。タグは sshd.service のようなものです。不安定という点について、もう少し正確に言うと、ログ出力時に unit のタグが付く時と付かない時があります。このため、タグでログを抽出しようとすると、ログを取りこぼすことがあります。foo.service で実行した場合、実行中のコマンドから出力したログは一律でタグが付くわけではないようで、再現性もなく困っています。

対応方針

logwatch.serviceExecStartPre= を設定し、テキスト形式のログを出力するスクリプトを実行します。logwatch の実行後、ExecStartPost= でテキスト形式のログを削除します。この方法なら、logwatch の設定を変更する必要がありません。この部分と、スクリプトだけで済みますので、切り戻しも簡単です。

rsyslog と同様のログファイルを出力する

rsyslog と(logwatch が関係する部分だけ)概ね等価なスクリプトを書きます。logwatch で処理するために、前日のログをテキスト形式で出力します。

#!/bin/bash

LOGDIR="/var/log"
LOCALE="C.utf8"
JOURNALCTL="/usr/bin/journalctl"

extract_logs() {
    LANG=${LOCALE} ${JOURNALCTL} -q -S yesterday -U today --no-pager \
        --facility=$1 \
        -p info \
      | sed -r 's/^([A-Za-z]+) 0/\1  /' \
      > "${LOGDIR}/$2"
}

# # Log anything (except mail) of level info or higher.
# # Don't log private authentication messages!
# *.info;mail.none;authpriv.none;cron.none                /var/log/messages
extract_logs "kern,user,daemon,auth,syslog,lpr,news,ftp,local0,local1,local2,local3,local4,local5,local6,local7" "messages"

# # The authpriv file has restricted access.
# authpriv.*                                              /var/log/secure
extract_logs "authpriv" "secure"

# # Log all the mail messages in one place.
# mail.*                                                  -/var/log/maillog
extract_logs "mail" "maillog"

# # Log cron stuff
# cron.*                                                  /var/log/cron
extract_logs "cron" "cron"

# # Save news errors of level crit and higher in a special file.
# uucp,news.crit                                          /var/log/spooler
extract_logs "uucp,news" "spooler"
  
# # Save boot messages also to boot.log
# local7.*                                                /var/log/boot.log
extract_logs "local7" "boot.log"

コメント部分は /etc/rsyslog.conf の各ログファイルの設定から持ってきたものです。この設定に対して、こういうコードを与えています、と読んでいただければ、と思います。

/var/log/secure はファシリティ authpriv のログを出力することになっているので、journalctl --facility=authpriv を指定してログを出力します。他のログも概ねシンプルで、同様の方法で出力できます。

/var/log/messages はちょっとややこしいのですが、プライオリティが info 以上で、ファシリティが mail, authpriv, cron でないログを集めています。journalctl では否定条件が書けないのですが、ファシリティは有限なので、残りをすべて列挙しています。

journalctl でロケールを設定しているのは、日付のフォーマットを rsyslog のものと揃えるためです。残念ながら、これだけでは不足で、sed でさらに加工しています。Sep 03 23:58:45 ではダメで、Sep 3 23:58:45 にしないと logwatch が正しく認識してくれません。一部、日本語で出力されているログがあるのでエンコーディングを UTF-8 に設定しています。エンコーディングを設定しなければ日本語部分はエスケープして出力されます。