システムサービスとして実行されるシェルスクリプトがあり、メッセージを記録したいと思います。細かい優先順位を持つサービスの systemd ログを入力します。
ジャーナリングを使用すると、logger(1)
一部のメッセージのみが記録され、残りは削除されます。どのメッセージがサービスログに記録されるかは完全にランダムであるようです。時には1つまたは2つのメッセージだけが記録され、時にはメッセージはまったく記録されません。
journalctl --system
最初は起動順序/依存関係の問題だと思いました。ただし、すべてのメッセージがシステムログ(例:)には表示されますが、サービスログ(例journalctl -u SERVICE.service
)には表示されないため、そうではありません。私も試してみましたが、systemd-cat
残念ながら同様に動作します。
スクリプトベースのサービスが優先順位メッセージを独自のシステムログに記録する正しい方法は何ですか?
答え1
systemdが直面する問題は、logger
独自のツールsystemd-notify
が直面する問題と同じです。このプロトコルはデータグラムに基づく非同期プロトコルであり、ツールは1つの操作のみを完了します。呼び出し側は、ツールを実行するためにプロセスを分岐します。データグラムがエクスポートされ、そのプロセスは終了します。
systemd
ログ記録と準備通知プロトコルのためのサーバープロセスは、送信者がどのサービスに属しているかを知りたいと思います。 Linux でデータグラムの送信者のプロセス ID を取得し、プロセステーブルに移動して、プロセスが属する制御グループとプロセスが属するサービスを探します。
転送プロセスがタスクを完了してすぐに終了する場合は機能しません(競争条件によって異なります)。プロセスがプロセステーブルに存在しなくなりました。
systemd-notify
通知に失敗しました。logger
メッセージは関連サービスに属するものとしてマークされません。ストリーミングプロトコルに切り替えても(例:logger
's--tcp
オプションを使用しても)問題は解決されません。〜しない限りロギングプロトコル自体は返品クライアントはストリームを閉じて終了する前にサーバーの応答を待つように変更されましたが、そうではありません。 RFC 5426サーバーの承認はクライアントに再送信されません。
したがって、ログ情報がログにある間は、サービス名でタグ付けされず、サービス名で照会してもインポートされません。 (しかし、これは私が思うように別々のログではありません。1つの
journalctl
大きなログに適用されるフィルタだけです。
-u
フィルタです。)
これはずっと前からよく知られている間違いです。
人々はsystemd
これをLinuxの欠陥と説明しています。プロセスセットをカプセル化して追跡するために使用できる適切な作業オブジェクトはありません。対応するデータグラムソケットメカニズムAF_LOCAL
もそのような情報を送信しません。そうでsystemd
あれば、すべてのサービスプロセスを1つのジョブに配置でき、そのログ記録と準備通知サーバーは、クライアントプロセスが終了してもデータグラムを受信したときにクライアントジョブ情報を抽出できます。
system-journald
一部のバージョンlogger
では動作する特別なプロトコルがあります。いいえ、_SYSTEMD_UNIT
これはサーバーによって設定された「信頼できるフィールド」です。これを設定しようとするクライアントのすべての試みは無視されます。これもデータグラムに基づく非同期プロトコルなので、確認は不要です。同じ問題があります。
正しいサービスでログエントリに確実にタグを付けるには標準エラーに書き込む。これにより、長寿命でサーバー側のサービスユニット名へのより信頼性の高い接続が可能になります。はい、レガシー施設と優先順位を指定することはできません。
追加読書
- ジョナサンデボインポラード(2015)。 」クライアント資格情報を抽出するときに同期プロトコルを使用する」Unixデーモンの準備プロトコルの問題。一般的な答え。
- ジョナサンデボインポラード(2016)。Linux制御グループが機能しない。一般的な答え。
- https://unix.stackexchange.com/a/383575/5132
- デビッド・リマの次(2017-02-23)。 Journalctl はセルのログを表示しません。。 Red Hatのバグ#1426152。
- https://unix.stackexchange.com/a/294206/5132
答え2
@JdeBPの非常に便利な回答で、私のメッセージが正しいサービスログに表示されるようにする方法を見つけることができました。問題は、Harvestjournald
とHarvestの間の競合状態で発生するため、次の寿命のlogger
長い中間データグラムを持つことができます。各スクリプト/サービスについて。これにより、journald
転送プロセスが常に見つかります。
socat
次の関数は、まだ起動していない場合はリレーを起動し、デフォルトではなくlogger
リレーソケットに送信するようにメッセージを設定します。親スクリプトが終了すると、リレーは自動的に終了し、対応するソケットを削除します。
# Usage: builtin_logger TAG SEVERITY MESSAGE
builtin_logger() {
if [[ "${SHELL_SERVICE_LOG_SOCK-}" == "" ]]; then
declare -g SHELL_SERVICE_LOG_SOCK
SHELL_SERVICE_LOG_SOCK="/tmp/service-log.$$"
sh -c "socat UNIX-RECV:'$SHELL_SERVICE_LOG_SOCK' UNIX-SENDTO:'/dev/log' &
SOCAT_PID=\$!
trap \"if [ -e '$SHELL_SERVICE_LOG_SOCK' ]; then rm '$SHELL_SERVICE_LOG_SOCK'; kill \$SOCAT_PID; fi\" EXIT INT TERM
tail --pid=$$ -f /dev/null" &
sleep 0.1 # waiting for socat to run (TODO inotify)
fi
logger -u "$SHELL_SERVICE_LOG_SOCK" -t "$1" -p user."$2" "$3"
}