ログ クラスは、ログ ファイルへの書き込みごとにログ ファイル ストリームを開いたり閉じたりする必要がありますか?それとも、すべてのログ記録が完了するまで、アプリケーションの有効期間を通じてログ ファイル ストリームを開いたままにしておく必要がありますか?
デスクトップアプリケーションのコンテキストで質問しています。私は人々が両方の方法でそれを行っているのを見てきましたが、どのアプローチがロガーにとって最良の総合的な結果をもたらすのか疑問に思っていました.
ログ クラスは、ログ ファイルへの書き込みごとにログ ファイル ストリームを開いたり閉じたりする必要がありますか?それとも、すべてのログ記録が完了するまで、アプリケーションの有効期間を通じてログ ファイル ストリームを開いたままにしておく必要がありますか?
デスクトップアプリケーションのコンテキストで質問しています。私は人々が両方の方法でそれを行っているのを見てきましたが、どのアプローチがロガーにとって最良の総合的な結果をもたらすのか疑問に思っていました.
頻繁に読み取り/書き込みを行う場合は、 1 回の open/closeでファイルを生涯にわたって開いたままにしておく方が効率的です。
アプリケーションがクラッシュした場合に備えて、すべてのデータがファイルに書き込まれていない可能性がありますが、定期的に、または書き込みごとにフラッシュすることをお勧めします。Unix ベースのシステムでは fflush を、Windows では FlushFileBuffers を使用します。
Windows でも実行している場合は、CreateFile API を FILE_FLAG_NO_BUFFERING と共に使用して、書き込みごとに直接ファイルに移動できます。
また、ファイルが使用中の場合、ファイルを開いたり閉じたりするたびにエラーが発生する可能性があるため、ファイルを生涯にわたって開いたままにしておくことをお勧めします。たとえば、バックアップ中にファイルを実行して開いたり閉じたりするバックアップ アプリケーションがあるとします。これにより、プログラムが自分のファイルにアクセスできなくなる可能性があります。ファイルを常に開いたままにし、Windows で共有フラグ (FILE_SHARE_READ) を指定するのが理想的です。Unix ベースのシステムでは、共有がデフォルトになります。
一般に、他の人が言ったように、パフォーマンスのためにファイルを開いたままにしてください (開く操作は比較的遅い操作です)。ただし、ファイルを開いたままにしておくと、ログ ファイルが削除されたり切り捨てられたりした場合にどうなるかを考える必要があります。そして、それはオープン時に使用されるフラグに依存します。(私は Unix に取り組んでいます - 同様の考慮事項はおそらく Windows にも当てはまりますが、私よりも知識のある人による修正を受け入れます)。
ログ ファイルがたとえば 1 MiB にまで大きくなったのを誰かが見て、それを削除した場合、アプリケーションは賢明ではなく、アプリケーションによってログが閉じられるまで、Unix はログ データを安全に保ちます。さらに、ユーザーはおそらく古いログ ファイルと同じ名前の新しいログ ファイルを作成し、アプリケーションが「ログを停止した」理由について困惑するため、混乱するでしょう。もちろん、そうではありませんでした。他の誰もアクセスできない古いファイルにログを記録しているだけです。
ログ ファイルがたとえば 1 MiB にまで大きくなったことに誰かが気付き、それを切り捨てたとしても、アプリケーションは賢明ではありません。ただし、ログ ファイルが開かれた方法によっては、奇妙な結果が得られる場合があります。ファイルが O_APPEND (POSIX 用語) で開かれなかった場合、プログラムはログ ファイルの現在のオフセットに書き込みを続け、ファイルの最初の 1 MiB はゼロ バイトのストリームとして表示されます。これは適切です。ファイルを見ているプログラムを混乱させる。
これらの問題を回避するには?
fstat()
ファイル記述子で定期的に使用し、st_nlink
がゼロかどうかを確認します。リンク数がゼロになった場合は、誰かがログ ファイルを削除しました。それを閉じて、新しいものを再開する時が来ました。stat()
またはと比較すると、高速である必要がありますopen()
。fstat()
基本的に、すでにメモリにあるものから情報を直接コピーしているため、名前の検索は必要ありません。したがって、おそらく、書き込もうとするたびにこれを行う必要があります。
提案:
日付ではなく時間を出力するアプリケーションに苦しんでいます。今日の初めに、8 月 17 日のエントリ (メッセージの 1 つで時刻の後に誤って日付がメッセージに含まれていました) と今日のエントリを含むメッセージ ファイルがありましたが、それらを作成したため、それしかわかりません。1 週間後にログ ファイルを確認した場合、それらが作成された日付はわかりませんでした (ただし、作成された時刻はわかります)。そういうのは面倒くさい。
また、Apache などのシステムが何を行っているかを確認することもできます。これらのシステムには、ログ ファイルを処理するメカニズムがあり、ログのローテーションを処理するツールがあります。注: アプリケーションが 1 つのファイルを開いたままにし、追加モードを使用せず、ログのローテーションやサイズの制限を計画していない場合、ログ ファイルが大きくなったり、先頭にゼロの塊ができたりすることについてできることはあまりありません。アプリケーションを定期的に再起動するよりも。
ログへのすべての書き込みができるだけ早く完了するようにする必要があります。ファイル記述子を使用する場合は、途中でカーネル バッファリングのみが行われます。これは十分に受け入れられるかもしれませんが、O_SYNC
またはO_DSYNC
のオプションを検討してopen()
ください。ファイル ストリーム I/O を使用する場合は、各書き込みの後に必ずfflush()
. マルチスレッド アプリケーションを使用している場合は、それぞれwrite()
に完全なメッセージが含まれていることを確認してください。メッセージの一部を別々に書こうとしないでください。flockfile()
ファイル ストリーム I/O では、操作をグループ化するために および 相対を使用する必要がある場合があります。ファイル記述子 I/O を使用すると、 を使用して、ファイル記述子に対してフォーマットされた I/O を実行できる場合があります (ただし、 を 1 回呼び出すかどうかはdprintf()
明確ではありません)。dprintf()
write()
writev()
1 回の操作でデータの個別のセグメントを書き込む。
ちなみに、ゼロを「含む」ディスクブロックは、実際にはディスク上に割り当てられていません。それぞれ数GiBのファイルを作成することで、人々のバックアップ戦略を本当に台無しにすることができますが、最後のディスクブロックを除くすべてにゼロが含まれています。基本的に(簡潔にするためにエラーチェックとファイル名の生成は省略されています):
int fd = open("/some/file", O_WRITE|O_CREATE|O_TRUNC, 0444);
lseek(fd, 1024L * 1024L * 1024L, 0);
write(fd, "hi", 2);
close(fd);
これはディスク上の 1 つのディスク ブロックを占有しますが、(非圧縮) バックアップでは 1 GiB (および変更)、復元時には 1 GB (および変更) を占有します。反社会的ですが、可能です。
パフォーマンスのために、開いたままにしてください。安全のため、頻繁に洗い流してください。
これは、ランタイム ライブラリが、大量のデータが存在するまで書き込みをバッファリングしようとしないことを意味します。データが書き込まれる前にクラッシュする可能性があります。
私はそれらを開いたままにしておく傾向がありますが、ファイル共有のアクセス許可を設定して他のリーダーを許可し、すべてのメッセージでログ出力をフラッシュするように設定して開きます。
実行中にログファイルを見ることさえできないプログラムや、ログファイルがフラッシュされず、何が起こっているか遅れるプログラムは嫌いです。
通常は、開いたままにしておくことをお勧めします。
別のプロセスからそれらを読み取ることができるかどうかが心配な場合は、それらを開く/作成するために使用する共有モードで、他のユーザーがそれらを読み取ることができることを確認する必要があります (もちろん、それらに書き込むことはできません)。
クラッシュが発生した場合にデータが失われることが心配な場合は、定期的にバッファーをフラッシュ/コミットする必要があります。
それはトレードオフです。毎回ファイルを開いたり閉じたりすると、プログラムがクラッシュしたときにファイルがディスク上で更新される可能性が高くなります。一方、ファイルを開く、最後までシークする、ファイルにデータを追加するなどの処理には、ある程度のオーバーヘッドが伴います。
Windows では、ファイルが開いている間はファイルを移動/名前変更/削除することはできません。作家。
この種のロギングを行ったほとんどの場合、ファイルを開いたままにし、fflush() を使用して、プログラムがクラッシュした場合にファイルが最新である可能性を高めました。
開いて閉じます。システムがクラッシュした場合に、破損したファイルからあなたを救うことができます。
閉める理由が見当たりません。
一方、閉鎖と再開には少し余分な時間がかかります。
ファイルを開いたままにしたくない理由はいくつか考えられます。
一方、追加モードであっても、ファイルを開くのは遅くなる可能性があります。最終的には、アプリが何をしているかにかかっています。
大規模な集中アプリケーションの場合、私は通常、アプリケーションの実行中はログ ファイルを開いたままにし、メモリ内のログ コンテンツを定期的に HDD にフラッシュする別のスレッドを用意します。ファイルのオープンとクローズの操作にはシステム コールが必要で、下位レベルを調べると大変な作業です。
書き込みごとに(または書き込みのバッチで)開いたり閉じたりします。これを行うとデスクトップ アプリケーションでパフォーマンスの問題が発生する場合は、ログ ファイルへの書き込みが頻繁に行われている可能性があります (ただし、大量の書き込みには正当な理由があると確信しています)。
毎回ファイルを閉じる利点は、新しいメッセージがディスクに書き込まれることを OS が保証することです。ファイルを開いたままにしてプログラムがクラッシュした場合、すべてが書き込まれない可能性があります。fflush() を実行するか、使用している言語で同等のものを実行することで、同じことを達成することもできます。
あなたのアプリケーションのユーザーとして、アプリの実際の要件でない限り、ファイルを開いたままにしないことをお勧めします。システムクラッシュなどが発生した場合に問題が発生する可能性があるもう1つのこと.