大量のデータをstdoutに書き込むプロセスがあり、それをログファイルにリダイレクトしています。現在のファイルを時々新しい名前にコピーして切り捨てることにより、ファイルのサイズを制限したいと思います。
ファイルを切り捨てる私の通常のテクニック、例えば
cp /dev/null file
おそらくプロセスがそれを使用しているため、動作しません。
ファイルを切り捨てる方法はありますか?または、それを削除して、何らかの方法でプロセスの標準出力を新しいファイルに関連付けますか?
FWIW、それはロギングモデルを変更するために変更できないサードパーティ製品です。
[〜#〜] edit [〜#〜]ファイルをリダイレクトすると、上記のコピーと同じ問題があるようです-ファイルは次に書き込まれるときに以前のサイズに戻ります:
ls -l sample.log ; echo > sample.log ; ls -l sample.log ; sleep 10 ; ls -l sample.log
-rw-rw-r-- 1 user group 1291999 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1292311 Jun 11 2009 sample.log
GNU Coreutils)の一部であるユーティリティsplit(1)
を見てください。
Coreutils 7.0では、truncate
コマンドがあります。
再成長したファイルの興味深い点は、/dev/null
をコピーしてファイルを切り捨てた後、最初の128 KB程度がすべてゼロになることです。これは、ファイルの長さがゼロに切り捨てられているために発生しますが、アプリケーションのファイル記述子は、最後の書き込みの直後にまだポイントしています。再度書き込むと、ファイルシステムはファイルの先頭をすべてゼロバイトとして扱います-実際にゼロをディスクに書き込むことはありません。
理想的には、アプリケーションのベンダーにO_APPEND
フラグを付けてログファイルを開くように依頼する必要があります。これは、ファイルを切り捨てた後、次の書き込みが暗黙的にファイルの末尾をシークし(オフセットゼロに戻ることを意味する)、新しい情報を書き込むことを意味します。
このコードは、標準出力をリグしてO_APPEND
モードにし、その引数で指定されたコマンドを呼び出します(Nice
のように、ナイスレベルまたはNohup
を調整した後にコマンドを実行します修正後にコマンドを実行し、SIGHUPを無視します)。
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
static char *arg0 = "<unknown>";
static void error(const char *fmt, ...)
{
va_list args;
int errnum = errno;
fprintf(stderr, "%s: ", arg0);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
fflush(0);
exit(1);
}
int main(int argc, char **argv)
{
int attr;
arg0 = argv[0];
if (argc < 2)
error("Usage: %s cmd [arg ...]", arg0);
if ((attr = fcntl(1, F_GETFL, &attr)) < 0)
error("fcntl(F_GETFL) failed");
attr |= O_APPEND;
if (fcntl(1, F_SETFL, attr) != 0)
error("fcntl(F_SETFL) failed");
execvp(argv[1], &argv[1]);
error("failed to exec %s", argv[1]);
return(1);
}
私のテストは幾分カジュアルでしたが、それがうまくいくと私を説得するのにかろうじて十分でした。
Billy 彼のメモ answer '>>
'は追加演算子です-そして実際、Solaris 10では、bash(バージョン3.00.16(1))はO_APPEND
フラグを使用します。これにより、上記のコードが不要になります(「Black JL:」がこのマシンのプロンプトです)。
Black JL: truss -o bash.truss bash -c "echo Hi >> x3.29"
Black JL: grep open bash.truss
open("/var/ld/ld.config", O_RDONLY) Err#2 ENOENT
open("/usr/lib/libcurses.so.1", O_RDONLY) = 3
open("/usr/lib/libsocket.so.1", O_RDONLY) = 3
open("/usr/lib/libnsl.so.1", O_RDONLY) = 3
open("/usr/lib/libdl.so.1", O_RDONLY) = 3
open("/usr/lib/libc.so.1", O_RDONLY) = 3
open("/platform/SUNW,Ultra-4/lib/libc_psr.so.1", O_RDONLY) = 3
open64("/dev/tty", O_RDWR|O_NONBLOCK) = 3
stat64("/usr/openssl/v0.9.8e/bin/bash", 0xFFBFF2A8) Err#2 ENOENT
open64("x3.29", O_WRONLY|O_APPEND|O_CREAT, 0666) = 3
Black JL:
上記のラッパー( ' cantrip ')コードではなく、追加リダイレクトを使用します。これは、ある特定の手法を他の(有効な)目的に使用する場合、それがさらに別の手法に適応することは、たとえ機能していても必ずしも最も単純なメカニズムではないことを示しています。
>ではなく>>を使用して出力をリダイレクトします。これにより、ファイルを元のサイズに戻さずにファイルを切り捨てることができます。また、STDERR(2>&1)をリダイレクトすることを忘れないでください。
したがって、最終結果は次のようになります:myprogram >> myprogram.log 2>&1 &
> file
。
コメントに関する更新:私にとってはうまくいきます:
robert@rm:~> echo "content" > test-file
robert@rm:~> cat test-file
content
robert@rm:~> > test-file
robert@rm:~> cat test-file
Redhat v6でも同様の問題がありました、echo > file
または> file
ログファイルにアクセスできなくなるため、ApacheとTomcatに障害が発生していました。
そして、修正は奇妙でした
echo " " > file
ファイルを消去し、問題は発生しません。
Linux(実際にはすべてのユニシス)では、ファイルが開かれたときに作成され、ファイルへの参照が保持されていないファイルは削除されます。この場合、それを開いたプログラムと「in」で開かれたディレクトリは、ファイルへの参照を保持します。 cpプログラムがファイルに書き込みたい場合、ディレクトリからそのファイルへの参照を取得し、ディレクトリに格納されているメタデータにゼロの長さを書き込み(これは少し簡略化されています)、ハンドルを放棄します。その後、元のファイルハンドルを保持したままの元のプログラムは、さらにデータをファイルに書き込み、長さを考慮して保存します。
ディレクトリからファイルを削除した場合でも、他のプログラムがそれを参照する方法がない場合でも、プログラムはデータを書き込み続けます(そしてディスク容量を使い果たします)。
要するに、プログラムがファイルへの参照(ハンドル)を取得したら、それを変更することはありません。
理論的には、LD_LIBRARY_PATHを設定してすべてのファイルアクセスシステムコールをインターセプトするプログラムを含めることにより、プログラムの動作を変更する方法があります。名前は思い出せませんが、どこかでこのようなものを見たのを思い出します。
ファイルが使用されているため、そのファイルまたはそのようなものを無効にしようとすると、ログファイルに書き込んでいるアプリが「混乱」し、その後何も記録されない場合があります。
私がやろうとしているのは、ファイルにリダイレクトするのではなく、そのログに一種のプロキシ/フィルターを設定することです。プロセスや、入力を取得してローリングファイルに書き込むものにリダイレクトします。
多分それはスクリプトで行うことができます。そうでなければ、そのための簡単なアプリを書くことができます(Javaまたは他の何か)。アプリのパフォーマンスへの影響は非常に小さいはずですが、テスト。
ところで、アプリは、スタンドアロンのWebアプリですか?調査すべき他のオプションがあるかもしれません。
編集: Append Redirection Operator >> もありますが、私は個人的に使用したことはありませんが、ファイルをロックしない可能性があります。
@Hoboはfreopen()を使用します。ストリームを再利用して、filenameで指定されたファイルを開くか、アクセスモードを変更します。新しいファイル名が指定されている場合、関数は最初にストリームに関連付けられているファイル(3番目のパラメーター)を閉じようとし、関連付けを解除します。次に、そのストリームが正常に閉じられたかどうかに関係なく、fopenはfilenameで指定されたファイルを開き、指定されたモードを使用してfopenが行うようにストリームに関連付けます。
サードパーティのバイナリがログを生成している場合、ログをローテーションするラッパーを作成する必要があり、サードパーティは以下のようにproxyrunスレッドで実行されます。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <unistd.h>
#include <string.h>
using namespace std;
extern "C" void * proxyrun(void * pArg){
static int lsiLineNum = 0;
while(1)
{
printf("\nLOGGER: %d",++lsiLineNum);
fflush(stdout);
}
return NULL;
}
int main(int argc, char **argv)
{
pthread_t lThdId;
if(0 != pthread_create(&lThdId, NULL, proxyrun, NULL))
{
return 1;
}
char lpcFileName[256] = {0,};
static int x = 0;
while(1)
{
printf("\n<<<MAIN SLEEP>>>");
fflush(stdout);
sprintf(lpcFileName, "/home/yogesh/C++TestPrograms/std.txt%d",++x);
freopen(lpcFileName,"w",stdout);
sleep(10);
}
return 0;
}
サードパーティ製品へのSIGHUPなどの信号の動作をチェックして、新しいファイルのログ記録が開始されるかどうかを確認しましたか?最初に古いファイルを永続的な名前に移動します。
kill -HUP [プロセスID]
そして、再び書き込みを開始します。
別の方法として(Billyが示唆したように)、アプリケーションからの出力をmultilogなどのロギングプログラムまたはcronologとして知られるApacheで一般的に使用されるロギングプログラムにリダイレクトすることもできます。そうすれば、すべてが最初のファイル記述子(ファイル)に書き込まれる前に、どこに行くかをよりきめ細かく制御できるようになります。
同様の問題があり、cronから実行されたスクリプトの出力で「tail -f」を実行できませんでした。
* * * * * my_script >> /var/log/my_script.log 2>&1
Stderrリダイレクトを変更して修正しました:
* * * * * my_script >> /var/log/my_script.log 2>/var/log/my_script.err
ファイルにリダイレクトする代わりに、ファイルを閉じて移動し、サイズが大きくなりすぎるたびに新しいファイルを開くことで自動的にファイルを回転させるプログラムにパイプすることができます。