Dockerコンテナのロギング機能を使ってみる
Dockerではコンテナ内で実行されたプロセスの出力をログとして記録しておく機能が用意されている。このログ出力機構では、さまざまなログ記録システムにログを転送することが可能であり、複数の異なるホストで稼動しているコンテナのログを1つのマシンに集約する、といったこともできる。今回はこのログ機能について紹介する。
DockerのLogging Driver機構
Dockerコンテナでは、コンテナ作成後にコンテナ内のファイルシステムに書き込まれたデータはコンテナの削除時に一緒に破棄されてしまう。そのため、各種ログやエラーメッセージ出力などの保存しておきたい情報はコンテナ外に出力して保存しておく必要がある。Dockerではこれを支援する機能の1つとして、ログを外部のログ記録ソフトウェアに転送する機構が用意されている。これを利用することで、多数のコンテナが稼動するような環境や、複数のマシンを組み合わせてDockerを運用するDockerクラスタのような環境でログの管理を効率化することが可能になる。
Dockerが標準で提供するログ機能は、「docker run」コマンドで起動したプロセスによる標準出力および標準エラー出力内容を、コンテナを稼動させているホストのファイルシステム上に記録するというものだ。
コンテナを実行させる「docker run」コマンドでは、「-i」オプションおよび「-d」オプションでコンソールへの出力動作を変更できるようになっている。「-i」オプションを指定した場合はコンテナ内で標準出力に出力された内容が「docker run」コマンドを実行したコンソールにそのまま出力され、「-d」オプションを指定した場合コンテナはバックグラウンドで実行されてコンソールへの出力は行われなくなるのだが、このどちらの場合も標準出力に出力された内容と同じものがDockerデーモンに送られて記録されるようになっている。
デフォルトの設定では、記録された内容は「docker logs <コンテナ名>」コマンドで確認できる。たとえば以下の例は、「-d」オプション付きで起動した「srad-db」コンテナの出力を確認する例だ。
# docker logs srad-db 160817 11:23:13 mysqld_safe Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect. 160817 11:23:13 mysqld_safe Logging to '/var/log/mysql/error.log'. 160817 11:23:13 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql 160817 11:24:09 mysqld_safe Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect. 160817 11:24:09 mysqld_safe Logging to '/var/log/mysql/error.log'. 160817 11:24:09 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
また、「-t」オプション付きで「docker logs」コマンドを実行すると、ログの出力時間も同時に表示される。
$ docker logs -t srad-db 2016-08-17T11:23:13.825549757Z 160817 11:23:13 mysqld_safe Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect. 2016-08-17T11:23:13.826463609Z 160817 11:23:13 mysqld_safe Logging to '/var/log/mysql/error.log'. 2016-08-17T11:23:13.850192255Z 160817 11:23:13 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql 2016-08-17T11:24:09.164633785Z 160817 11:24:09 mysqld_safe Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect. 2016-08-17T11:24:09.165478795Z 160817 11:24:09 mysqld_safe Logging to '/var/log/mysql/error.log'. 2016-08-17T11:24:09.188621934Z 160817 11:24:09 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
ちなみにこのログは、「/var/lib/docker/containers/<コンテナID>/<コンテナID>-json.log」というファイルにJSON形式で記録されている。コンテナIDは「docker ps --no-trunc」コマンドで確認が可能だ。
たとえば次の例は、「srad-db」コンテナのコンテナIDを確認したものだ。ちなみに「--no-trunc」オプションはコンテナIDをフルで表示するためのオプションであり、これを付けずに「docker ps」コマンドを実行した場合はコンテナIDの先頭数桁のみが表示される。
# docker ps --no-trunc a27df48ed043b5071d8f03bf1c2dc5696b6e7aa5bdc01b31219d4a158deced28 osdn/srad-db "/run-mysql-server.sh mysqld" 11 weeks ago Up 11 weeks 3306/tcp srad-db,compose_slash_1/srad-db,compose_slash_1/srad-db,compose_slash_1/srad-db_1,compose_slash-frontend_1/srad-db,compose_slash-frontend_1/srad-db,compose_slash-frontend_1/srad-db_1,mojolicious/sd-db,agitated_blackwell/sd-db,high_wescoff/sd-db
この場合、「srad-db」コンテナのコンテナIDは「a27df48ed043b5071d8f03bf1c2dc5696b6e7aa5bdc01b31219d4a158deced28」となり、ログファイルは「/var/lib/docker/containers/a27df48ed043b5071d8f03bf1c2dc5696b6e7aa5bdc01b31219d4a158deced28/a27df48ed043b5071d8f03bf1c2dc5696b6e7aa5bdc01b31219d4a158deced28-json.log」となる。
# cat /var/lib/docker/containers/a27df48ed043b5071d8f03bf1c2dc5696b6e7aa5bdc01b31219d4a158deced28/a27df48ed043b5071d8f03bf1c2dc5696b6e7aa5bdc01b31219d4a158deced28-json.log {"log":"160817 11:23:13 mysqld_safe Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect.\n","stream":"stderr","time":"2016-08-17T11:23:13.825549757Z"} {"log":"160817 11:23:13 mysqld_safe Logging to '/var/log/mysql/error.log'.\n","stream":"stdout","time":"2016-08-17T11:23:13.826463609Z"} {"log":"160817 11:23:13 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql\n","stream":"stdout","time":"2016-08-17T11:23:13.850192255Z"} {"log":"160817 11:24:09 mysqld_safe Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect.\n","stream":"stderr","time":"2016-08-17T11:24:09.164633785Z"} {"log":"160817 11:24:09 mysqld_safe Logging to '/var/log/mysql/error.log'.\n","stream":"stdout","time":"2016-08-17T11:24:09.165478795Z"} {"log":"160817 11:24:09 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql\n","stream":"stdout","time":"2016-08-17T11:24:09.188621934Z"}
ここで分かるように、このJSONファイルの中には「docker logs」コマンドで確認できる情報に加えて、出力先ストリーム(標準出力なら「stdout」、標準エラー出力なら「stderr」)の情報も記録されている。このファイルが格納されているディレクトリはコンテナを削除すると同時に削除されるため、コンテナ削除後もログを保存しておきたい場合はこのファイルを別のディレクトリにコピーしておくなどの対応が必要となる。
Logging Driverを変更する
Docker標準のJSONファイルを使ったログ機構は必要最低限の機能を備えているものの、これでは機能が不足するケースもある。たとえば複数台のマシン上でDockerを動かしている場合、ログを確認するためには目的のコンテナを稼動させているマシンに直接アクセスする必要がある。そういった場合に有用なのが、JSONファイル以外のログ記録システムにログを転送できる「Logging Drivers」機能だ。
Logging Driversでは、Dockerデーモンの起動時もしくはコンテナの起動時に出力先を指定することで、指定されたログ管理システムにログを出力することが可能になる。Dockerのデフォルト設定では先に説明したようにJSON形式のファイルに出力する「json-file」ドライバが使われるようになっているが、Docker 1.12時点では次の出力先が標準で利用可能になっている(表1)。
ドライバ名 | ログの出力先 |
---|---|
none | ログを記録しない |
json-file | JSON形式でファイルに保存(Dockerのデフォルト) |
syslog | syslog |
journald | journald |
gelf | 「Graylog Extended Log Format(GELF)」をサポートするログ管理システム |
fluentd | ログ管理ツール「Fluentd」 |
awslogs | クラウド型のログ管理システム「Amazon CloudWatch Logs」 |
splunk | ログ管理システム「Splunk」 |
etwlogs | WindowsのEvent Tracing for Windows(ETW) |
gcplogs | クラウド型のログ管理システム「Google Cloud Logging」 |
使用するドライバはDockerデーモン(dockerd)の「--log-driver=<ドライバ名>」もしくは「docker run」コマンドの「--log-driver=<ドライバ名>」オプションで指定できる。「docker run」コマンドでドライバが指定されなかった場合、Dockerデーモンの起動時に--log-driverオプションが指定されていた場合は指定されたドライバが、指定されていなかった場合は「json-file」ドライバが使用される。
ちなみに先に紹介した「docker logs」コマンドはLogging Driverとして「json-file」もしくは「journald」を選択した場合のみ利用できる。それ以外のドライバを選択した場合は、対応するログ管理ツールを使ってログの閲覧等を行うことになる。「none」を指定した場合はログは記録されない。
それぞれのドライバでは、さらに「--log-opt」オプションを使用してオプションパラメータを指定できる。詳しくはDockerのドキュメントを参照してほしいが、たとえばjson-fileドライバでは「max-size」オプションでログファイルの最大サイズを指定したり、ログファイル数の上限を指定することが可能だ。
また、「--log-opt labels=<ラベル>」や「--log-opt env=<環境変数>」といったオプションを指定することで、コンテナに割り当てられたラベル情報や指定した環境変数の情報をログと一緒に出力することが可能になる。
たとえば、json-fileドライバの場合、「--log-opt labels=foo」というオプションと「--log-opt env=hoge」というオプションを指定することで、ログには「foo」というラベルに設定した文字列と「hoge」という環境変数に設定した文字列が一緒に記録される。次の例は、このオプションを指定してbusyboxコンテナ内でlsコマンドを実行したものだ。
$ docker run --label foo=bar -e hoge=moge --log-opt labels=foo --log-opt env=hoge busybox ls bin dev etc home proc root sys tmp usr var
この場合、ログファイルには以下のように標準出力に出力された文字列に加えて「"attrs":{"foo":"bar","hoge":"moge"}」という属性が追加され、さらにそれぞれにはラベル「foo」に設定した「bar」という文字列と、環境変数「hoge」に設定した「moge」という文字列が出力されていることが分かる。
{"log":"bin\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845294296Z"} {"log":"dev\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845343951Z"} {"log":"etc\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845355202Z"} {"log":"home\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845374855Z"} {"log":"proc\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845390947Z"} {"log":"root\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845398848Z"} {"log":"sys\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845406244Z"} {"log":"tmp\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845413708Z"} {"log":"usr\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845420926Z"} {"log":"var\n","stream":"stdout","attrs":{"foo":"bar","hoge":"moge"},"time":"2016-11-08T11:15:42.845428143Z"}
なお「--log-opt」オプションは「docker run」コマンドの実行時だけでなく、Dockerデーモンの起動時にも指定が可能だ。
syslogにログを出力する
Docker標準のログ機構(json-file)以外のログ管理機構として、最もよく知られているのはsyslogだろう。Dockerのsyslogドライバを利用することで、syslogにログを出力することが可能となる。たとえば次の例は、busyboxコンテナ上でlsコマンドを実行し、その出力をsyslogに出力するものだ。
$ docker run --rm --log-driver=syslog busybox ls bin dev etc home proc root sys tmp usr var
コマンドの実行後、syslogファイル(/var/log/syslog)には次のようなログが出力されていることが分かる。
Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: bin Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: dev Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: etc Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: home Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: proc Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: root Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: sys Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: tmp Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: usr Nov 8 20:28:36 ubuntu docker/c4d29c1603ef[26437]: var
また、syslogドライバではネットワーク経由で別のマシン上のsyslogにログを送信することもできる。ログの送信先は「syslog-address」オプションで指定可能だ。たとえば「192.0.2.100」というホストの601番ポートでTCPで待ち受けをしているsyslogにログを送信する場合、次のようなオプションを追加すれば良い。
--log-opt syslog-address=tcp://192.0.2.100:601
また、TLSで保護されたTCPでログを送信することも可能だが、この場合は証明書などの情報を別途オプションで指定する必要がある。また、syslogで使われるfacilityの値については「--log-opt syslog-facility=<ファシリティ>」オプションで指定可能だ。そのほか詳しいオプションなどはDockerのドキュメントを参照して欲しい。
fluentdを使ったログ集積
複数のマシン上でDockerを稼動させるDockerクラスタ環境では、ログを1つのマシン上に集約して管理することが好ましい。awslogsやgcplogsといったクラウド型のログ収集機構を使うという方法もあるが、クローズドな環境で利用する場合は「Fluentd」を使用すると手軽にログの集約が実現できる。
Fluentdについては以前『柔軟なログ収集を可能にする「fluentd」入門』という記事で紹介しているので詳しくはそちらを参照して欲しいが、Fluentdは公式Dockerイメージも提供されており、Docker環境で容易に導入できる。続いてはこのFluentdを使ったログ収集の設定手順を紹介しておこう。
まずDockerコンテナでFluentdを実行させる設定だが、たとえばDockerホスト上の「/var/fluentd」というディレクトリ内にログを格納したい場合、次のように「docker run」コマンドを実行するだけで良い。
# mkdir -p /var/fluentd # chown 1000:1000 /var/fluentd # docker run -d -p 24224:24224 -v /var/fluentd:/fluentd/log fluent/fluentd
ここでは「-v /var/fluentd:/fluentd/log」オプションを使い、Dockerホストの/var/fluentdディレクトリをコンテナ内の/fluentd/logディレクトリにマウントすることで、記録したログを永続的に残せるよう設定している。このとき、Fluentdはコンテナ内でuidおよびgidがそれぞれ1000の「fluent」というユーザーの権限で実行されるので、このユーザーがマウントしたディレクトリにアクセスできるよう、コンテナ内にマウントされるDockerホスト上のディレクトリのパーミッション設定を行っておく必要がある。
また、Fluentdの稼働後はログを送信したいノードからfluendにアクセスできるかを確認しておこう。たとえばfluentdコンテナを稼動させているホストのIPアドレスが192.168.1.10、ポートが24224番の場合、telnetコマンドで次のように実行することで確認できる。
# telnet 192.168.1.10 24224 Trying 192.168.1.10... Connected to 192.168.1.10. Escape character is '^]'. ^] telnet> Connection closed.
ここで「Connected to <ホスト>」というメッセージが表示されれば接続は成功だ。Ctrl+]キーを入力し、続いてCtrl-Dキーを入力することでTelnetを終了できる。
Fluentdを稼動させたら、続いてはDocker側の設定を行っていく。「docker run」コマンドの実行時にオプションでこのドライバを利用するよう指定することもできるが、Dockerクラスタ環境で利用する場合はDockerデーモンの起動時にfluentdドライバを利用するよう設定しておくほうが便利だ。具体的には、Dockerデーモン(dockerd)の起動オプションとして「--log-driver=fluentd」オプションを指定し、さらに「--log-opt」オプションでドライバオプションを指定すれば良い。
Docker Daemonの起動オプション設定方法についてはディストリビューションや使用するDockerパッケージによって異なるが、Red Hat Enterprise Linux(RHEL)やその互換環境の場合「/etc/default/docker」ファイル内で設定されている「OPTIONS」変数で指定できる。また、UbuntuやDebianでは「/etc/default/docker」ファイル内の「DOCKER_OPTS」変数で指定が可能だ。
なお、Ubuntu 15.04以降ではこの方法では設定できず、systemdの設定ファイルに直接オプションを書き込む必要がある。具体的な手順は以下の通りだ。まず、「/usr/lib/systemd/system/docker.service」ファイルを「/etc/systemd/system/」ディレクトリ以下にコピーする。
# cp /usr/lib/systemd/system/docker.service /etc/systemd/system/
続いてコピーした/etc/systemd/system/docker.serviceファイルをエディタで開き、「ExecStart=/usr/bin/dockerd」行にオプションを直接追加すれば良い。また、変更後は「daemon-reload」コマンドを実行して変更を反映させておく必要がある。
さて、fluentdドライバでは以下の2つのオプションを最低限指定しておく必要がある。
- fluentdが稼動しているホストを指定する「fluentd-address」オプション
- fluentdのログに記録するタグを指定する「tag」オプション
fluentd-addressオプションでは、「<ホスト名>:<ポート>」という形式でログの送信先を指定する。また、「tag」オプションは送信されたログがどのコンテナから送られたものかを判断するために必要だ。このtagオプションで指定する値では、「{{.ID}}や「{{.FullID}}」、「{{.Name}}」といった形で変数を指定することで、コンテナIDやコンテナ名などを埋め込むことができる。
たとえばfluentdが「192.168.1.10」というホストで稼動している場合、設定オプションは次のようになる。
--log-driver=fluentd --log-opt fluentd-address=192.168.1.10:24224 --log-opt tag="docker.{{.Name}}"
ここではタグとして「docker.{{.Name}}」と指定している。この場合、ログには「docker.<コンテナ名>」というタグが埋め込まれるようになる。
設定の変更後、Docker Daemonを再起動して「docker info」コマンドを実行し、変更が反映されているか確認しよう。以下のように「Logging Driver」が「fluentd」となっていれば良い。
# docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 10 Server Version: 1.12.3 Storage Driver: devicemapper : : Logging Driver: fluentd Cgroup Driver: cgroupfs : :
ちなみにfluentdドライバを利用する場合、何らかの原因で指定されたFluentdに接続できないとDocker Daemonの起動に失敗する。そのため、事前にFluentdを稼動させ、Dockerホストからアクセスできることを確認しておこう。
fluentdドライバを使用したコンテナでは、コンテナ内での標準出力および標準エラー出力が指定したFluentdにログとして送信される。送信されたログは、デフォルトではfluentdコンテナ内にマウントしたディレクトリ内に作成される「data.<日付>.<ID文字列>.log」といったファイルに出力される。
# ls -l /var/fluentd/ 合計 4 -rw-r--r-- 1 1000 1000 3866 11月 10 02:09 data.20161109.b540e14e1009e7d55.log lrwxrwxrwx 1 1000 1000 48 11月 10 02:08 data.log -> /fluentd/log/data.20161109.b540e14e1009e7d55.log
たとえば「docker run --name busybox-test busybox ls」のようにコンテナを実行して標準出力への出力を発生させた場合、ログは以下のようになる。
# cat /var/fluentd/data.20161109.b540e14e1009e7d55.log 20161109T170953+0000 "docker.busybox-test" {"log":"bin","container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout"} 20161109T170953+0000 "docker.busybox-test" {"log":"dev","container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout"} 20161109T170953+0000 "docker.busybox-test" {"log":"etc","container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout"} 20161109T170953+0000 "docker.busybox-test" {"source":"stdout","log":"home","container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test"} 20161109T170953+0000 "docker.busybox-test" {"container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout","log":"proc"} 20161109T170953+0000 "docker.busybox-test" {"container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout","log":"root"} 20161109T170953+0000 "docker.busybox-test" {"log":"sys","container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout"} 20161109T170953+0000 "docker.busybox-test" {"log":"tmp","container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout"} 20161109T170953+0000 "docker.busybox-test" {"container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout","log":"usr"} 20161109T170953+0000 "docker.busybox-test" {"log":"var","container_id":"472f439d144c2e2e35890a280e009562a7857975b7ae2834a3ef974cac150b2d","container_name":"/busybox-test","source":"stdout"}
ここで1列目が日付、2列目がタグ、3列目が出力されたログになる。ログには標準出力や標準エラー出力に出力された内容に加えて、コンテナ名やコンテナID、stdour/stderrといった情報が含まれていることが分かる。
出力先のカスタマイズ
Fluentdではログのテキストファイルへの出力だけでなく、さまざまな形式でログを保存したり転送することが可能だ。Fluentdの公式コンテナイメージではあらかじめこのようにテキストファイルにログを出力するような設定が含まれているが、もちろん自分で独自の設定を利用することも可能だ。
Fluentdコンテナでは、デフォルトではコンテナ内の/fluentd/etc/fluent.confファイルを設定ファイルとして読み込むよう設定されている。コンテナの実行時にボリュームマウント機能を利用してDockerホスト内のディレクトリをコンテナの/fluentd/etcディレクトリにマウントしたり、もしくはDockerホスト上の設定ファイルを/fluentd/etc/fluent.confとしてマウントさせることで、その設定を上書きできる。
また、「FLUENTD_CONF」環境変数で設定ファイルとして利用するファイル名を指定することも可能だ(「/fluentd/etc/$FLUENTD_CONF」というパス名のファイルが設定ファイルとして使われる)。設定ファイルの作成方法などについては、『柔軟なログ収集を可能にする「fluentd」入門』で説明しているのでそちらを参照して欲しい。
コンテナ内で残しておく必要があるログは標準出力に出力しておこう
このように、Dockerのログ機構ではかなり柔軟に標準出力や標準エラー出力の内容を記録できるようになっている。そのため、Dockerが公式に提供されているアプリケーションイメージでは標準出力に各種ログを出力するよう設定されているものが多い。Apache HTTP Server(httpd)イメージがその代表で、このイメージではWebサーバーへのアクセスログが標準出力に出力されるよう設定されている。これによってコンテナを改変することなしに、Dockerホストの設定だけでログの保存先や保存フォーマットを変更できるようになっている。
独自にコンテナを作成する場合、コンテナ内にログを記録するとコンテナの削除時にそのデータは消滅してしまう。また、コンテナ内からコンテナ外にデータを取り出す際にも一手間かかってしまう。httpdコンテナの場合と同様、標準出力にログを出力し、Dockerのログ機構を利用すればこの問題は容易に解決できる。複数の種類のログを出力するような場合は対応できないため、その場合は別途ログ出力機構を用意する必要はあるが、多くのケースでは単一のログ出力で十分だろう。ぜひ活用してもらいたい。