• 締切済み

Perl:大容量ログを正常に読み込む方法が知りたい

ある動作のログを出力するシステムの機能改修の立場にある者です。 言語はperlです。 大容量ログを正常に読み込む方法が知りたいのですが、急いでおります。 ログが出力されているファイルが大容量で、 ログ読み込みの始めのうちは問題なく扱えていますが、 ログをどんどん読み込んでいくと 「Internal Server Error」となります。 スクリプトは下記の通りです。 open LOGFILE,○○ while($** = <LOGFILE>){ ログの文字列をいろいろ切り出して、配列に格納。 必要なログ情報を取り出せたらループから出る。 } close(LOGFILE); 上記の配列を扱い、ログを多様な表示スタイルで出力します。 ブラウザでのログ表示で検索条件で指定されたログを抽出できたら 途中でループから逃げているので問題なく動作しております。 しかし、ページを切り替え、ログをどんどん読み込んでいくと (おそらく)配列に格納するメモリが大容量のため エラーになってしまいます。 ※ログ表示で設置している検索条件機能 (1)何ページ目表示指定 (2)1ページあたりのログ表示行数指定 (100行表示で3ページ目を見る場合、201行目から300行が表示されます。) そのためページ切り替え毎にログを格納している配列を破棄?するなど メモリを解放することが必要になる?かと思っています。 しかし、ページ切り替えごとにログの一番初めから読み込み直す必要もあるのでは?とも考えています。 ※ログの一文目からカウンタで順番を数えていて、後でログを格納した配列でキーを指定してログを取り出しています。 検索条件を変えながらでも、かつメモリを解放?しながらログを配列に格納して扱う方法はあるでしょうか。 つまづいてしまって進めません。。 ※メモリを増やすといったパワープレーではなく、 プログラムの書き方でメモリの負荷を少なくして実現させたいです。 もしいい方法をご存知の方がいらっしゃれば、よろしくお願いいたします。 長文失礼いたしました。

  • Perl
  • 回答数5
  • ありがとう数5

みんなの回答

回答No.5

自分だったらその条件を見たときにDBMSの使用を考えます。 ログそのものをDBMSに保存されるようにするか、ログを定期的にDBMSにコンバートします。 テキストファイルでやっている程度のことなのでsqliteを使うだけでもかなりの速度向上を実感できると思います。nページ目を見るというのはSELECT文のOFFSETを使って前のところを飛ばすようにして実現します。無駄にメモリーに読み込まないようにするためにLIMITも使うと良いでしょう。 元のプログラムのままやるとしたら、nページ目をやるときはn-1ページ目までを配列に保存することなく無視すると言うことで実装するしかないでしょうね。

  • Wap58
  • ベストアンサー率33% (29/87)
回答No.4

おそらくテキストなどに書き出さない限り プログラム終了で変数の内容は全て破棄されます ブロック内に重い処理があるんじゃないですか 出来るだけ関数を使わない処理を考えて下さい

  • Gotthold
  • ベストアンサー率47% (396/832)
回答No.3

> 201行目以上 "または" 300行目未満 だと、結局 全行対象になるので正しくは > if ( 200 <= $i && $i < 300 ) { > じゃないですかね。 ですね。適当に書いてたら間違えてしまいました。 > このwhileとifの間で使用した配列にはログを読み込めば読み込むほど、 > メモリ使用量が増えていくかと思います。 いや、加工結果をどこか格納したりせずに捨てていればメモリ使用量は増えないです。 書き方がまずくてメモリを食っている可能性もありますが。 そもそもエラーの原因がメモリ不足であることはちゃんと確認しましたか? どちらにしても、使わないデータを加工してしまっているのは無駄なので直した方が良いですね。

回答No.2

> if( 200 <= i || i < 300 ){ # 201行目から300行目だけ 201行目以上 "または" 300行目未満 だと、結局 全行対象になるので正しくは if ( 200 <= $i && $i < 300 ) { じゃないですかね。 で、本題ですが、 $i = 0; while($** = <LOGFILE>){  ログを切ったり、いじったりして、表示用に改変したログをつくる。 <<★  if ( 行番号を使った表示範囲の絞り込み ) {    表示処理  }  $i++; } としているということですよね。 なら、★のところで、 メモリを使うアルゴリズムになってしまっているんじゃないですかね。 使わないデータを加工しても意味がないので、加工処理も行で限定すればよいだけでは? つまり、 $i = 0; while($** = <LOGFILE>){  if ( 行番号を使った表示範囲の絞り込み ) {    ログを切ったり、いじったりして、表示用に改変したログをつくる。    表示処理  }  $i++; } で。 もしくは、加工部のアルゴリズムを見なおして、メモリ使わない処理に書き換えるか。

  • Gotthold
  • ベストアンサー率47% (396/832)
回答No.1

> そのためページ切り替え毎にログを格納している配列を破棄?するなど > メモリを解放することが必要になる?かと思っています。 > しかし、ページ切り替えごとにログの一番初めから読み込み直す必要もあるのでは?とも > 考えています。 Internal Server Errorが出るって事は、 サーバ側でCGI経由とかで実行してるんですよね? だったら、ページ切り替えのたびに Perlスクリプトは1から実行されて終了するので、 メモリの内容なんて残りませんよ。 毎回最初からやり直しです。 > 検索条件を変えながらでも、かつメモリを解放?しながら > ログを配列に格納して扱う方法はあるでしょうか。 ちゃんと必要な分だけ配列に格納すれば メモリなんてそんなに食わないのでは? こんな感じで open LOGFILE,○○ i = 0; while($** = <LOGFILE>){  if( 200 <= i || i < 300 ){ # 201行目から300行目だけ   #切り出し処理とかはここで   push(@配列, $**); #配列に格納  }  i++; } close(LOGFILE);

horiten
質問者

補足

ご回答ありがとうございます! >Internal Server Errorが出るって事は、 >サーバ側でCGI経由とかで実行してるんですよね? その通りです! >ちゃんと必要な分だけ配列に格納すれば すみません、説明が足りませんでした。 表示する行数指定というものは、 すでにもともとのログをいじって改変したものを ブラウザに表示する段階でのログの行数になります。 つまり、 open LOGFILE,○○ i = 0; while($** = <LOGFILE>){ ログを切ったり、いじったりして、 表示用に改変したログをつくる。  if( 200 <= i || i < 300 ){ # 201行目から300行目だけ      push(@配列, $**); #配列に格納  }  i++; } close(LOGFILE); という流れになっています。 このwhileとifの間で使用した配列にはログを読み込めば読み込むほど、 メモリ使用量が増えていくかと思います。 当方、初心者なもので理解が弱くてすみません。。

関連するQ&A

  • ログの解析プログラム

    perlにてログの解析プログラムを作成しております。 ログは各データの区切り文字がスペースで出力されます。 その為、例えば文字列で「プログラムの異常が発生しました (発生ノード)」が出力されている場合split関数によって配列に格納しようとすると「プログラムの異常が発生しました」と「(発生ノード)」に分解されて格納されてしまい後ほどの処理に影響が出てしまいます。ログの仕様として文字列を出力するときには直前に文字列の長さを提示しております。例えば「43 プログラムの異常が発生しました (発生ノード)」のような形で出力されます。これらの条件で文字列を一つの配列に格納するにはどのようなプログラムを組めばよろしいでしょうか。 現時点で考えているのは文字列の長さが出ているのでその回数分文字を1文字づつ読み込むことを考えております。ただし全角と半角の区別が自動でついてしまうと厄介なので全部半角として取り込めないかなどを検討しております。 なにか他にいいアイデアがありましたら教えてください。

  • VBA 2次元配列のセル出力

    2次元配列に格納されているデータをセルに出力したいのですが、90000行もあるデータを扱っているので、ループでまわすととても遅いです。 早く出力できるテクニックがあれば、教えていただけないでしょうか? 素人質問で申し訳ありませんが、よろしくお願いします。

  • PHPでCSVファイルから行指定してループする方法

    こんにちは。 PHPでループ処理をしているのですが、ふと疑問にあたりました。 CSVファイルをオープンしそれを一行づつ読み込みたいのです。 「次のページ」などのよくある方法のように30行あったら次のページをクリック。 次ページでは31行目から60行目までを表示、その繰り返しのようなループを考えています。 Perlの場合foreach(1 .. 30){} のように配列の始点終点の指定ができたとおもうのですが、PHPでもこのような処理は可能なのでしょうか? 恐れ入りますがご教授お願いします。

    • ベストアンサー
    • PHP
  • Perlでのファイルの扱いでつまづいております。

    やりたいことは、"ファイルAを読み取り、その内容に処理を施したものをファイルBに書き込むという処理"です。 その上で、"ファイルBに書き込んだ内容を書き込みと同時に読み取り、 その上である条件にマッチした文を書き換える"といったことを実現したいです。 もともとのファイルの容量が非常に大きいので、何度もループを使うといったことはなるべく避けたいため、 ファイルBの書き込み・読み込み・書き換えを同時に行いたいのですが、 そもそもそういったことは可能なのでしょうか。 ※また、もともとのファイルの容量が非常に大きいので配列を使わず行う、 ということが前提条件としてあります。 現在のスクリプトの簡略化したものが以下となります。 open LOGFILE,"< /○○/ファイルA" || die("die"); open NEWLOG,"+< /△△/ファイルB" || die("die"); $new = <NEWLOG>; while($yomitori = <LOGFILE>){ if(ある条件1){ print NEWLOG "$kakikomi\n"; } if(ある条件2){ ファイルBの書き換えの処理 $new =~s/\n/ $kakikae\n/; print NEWLOG "$new"; } } close(NEWLOG); close(LOGFILE); 上記スクリプトで、ファイルAからファイルBへの書き込み、ファイルBの読み取りまではできておりますが、 ファイルBの書き換えは実現しておりません。 以上、お詳しい方がいらっしゃいましたら宜しくお願い致します。

    • ベストアンサー
    • Perl
  • Perlでニュースプログラム

    お世話になります。looserと申します。 Perlで、ニュースプログラムを作っているのですが、どうしてもやり方がわからなくて、教えていただきたいのです。 データファイルがあり、年度ごとの一覧をCGIからHTMLで出力し、また最初のページには、今年度の記事の一覧があり、それぞれの記事をクリックするとその記事の詳細が出てきます。 それぞれの記事には、全部の年の目次があって、その年の目次はクリックできないようにするというようなものを作りたいのです。 --data.csv-- 1,2002,記事1 2,2002,記事2 3,2003,記事3 4,2003,記事4 5,2003,記事5 6,2004,記事6 7,2005,記事7 -------プログラム--------- @data = &ReadData();#data.csvの読み込み #年一覧の配列の作成 foreach (@data){ ($no,$year) = split(/,/); unless($year == $last){push(@year_list,$year);} else{$last = $year;} } foreach $yr (@year_list){#年ごとのループ chomp($yr); foreach $data (@data){#データループ ($no,$year,$message) = split(/,/,$data); if($year == $yr){ # #この辺で配列格納?それともページ出力? # } } } -------------------------- これでやると、それぞれの年の一覧ページの出力が出来ないのです。 どなたか分かる方、また、サンプルになるスクリプトなどをご存知の方お願いいたします。 やりたい事 1.年度ごとの記事一覧の出力 2.記事ごとのページの出力 3.年のインデックス(目次)の出力→全ページに付く

    • ベストアンサー
    • Perl
  • AWSTATSのログ表示について

    Windows2000上でIISのログをAWSTATS6.2で表示しています。 手動で、アップデートをかけているのですが、土日の分を月曜日にアップデートをかけると表示が土日のアクセス数が0になってしまいます。 confファイルは LogFile = "C:\log\ex%YY%MM%DD.log" と設定されており、土日以外は正常に動いているようです。 また、月曜日に土曜日のex041211.logと指定しても既に登録されているレコードと表示されます。

  • Perlで一覧表示

    お世話になっております。 Perl初心者で、Java等は少し経験があります。 「検索を行い一覧を表示する」という画面をPerlで作成する際の 作り方について質問させてください。 現在は以下のような構成で画面を作っています。(DBアクセスには DBI を使用しています) ------------------------------ 【DAO的な pm】  引数として検索条件を受け取り、  DB にアクセスし、  $sth->fetchrow_arrayref で取り出した検索結果を  ハッシュや配列に詰めて返す 【画面の pl】 上記DAO的な pm を利用して検索結果を受け取り、 それをループで回して一覧として表示する ------------------------------ ただ、これだと配列にデータをため込むので 検索結果が多くなるとメモリをくいそうで気になっています。 (あと後述の方法と比べるとやっぱり遅い気がする…) 他の方が Perl で作った画面を見たところ、以下のような作り方を見かけました。 ------------------------------ 【画面の pl】 画面内で SQL を発行し、 $sth->fetchrow_arrayref で取り出した検索結果を 逐次出力していく ------------------------------ こちらのほうだと、一覧に出すものが多くても 上からパラパラ表示されていくので 閲覧者側のストレスも少ないと思ったのですが、 Perlの作りとしてはこういうほうが一般的なのでしょうか。 なんとなくDBアクセスは汎用的なクラスにしてしまいたい気持ちになるのですが Perlではあんまりそういうことはしない、とかPerlでもするよ、とか ご存じの方がいらっしゃいましたら教えてください。 あとは関数の戻り値として検索結果をごそっと返すのではなくて、 関数の引数として「一件分のデータを受け取るDelegate」を渡してみたらどうだろうかとか思っているのですが 変でしょうか… よろしくお願いします。

  • 容量の大きいテキストファイルを開くには?

    ホームページのアクセスログをテキストファイルとして開きたいのですが、容量が1Gを超えていて開くことができません。 秀丸エディタを使用していたのですが、限界の1000万行を超えていて開けません。 WORDではメモリが足りないと表示されます。 テキストファイルのまま分割するなどして開きたいのですが、やり方をご存知の方はおられませんか?

  • 大容量のテキストファイルの内容を解析について

    10000行1GBほどの大容量のテキストファイルの内容を解析しようとしています。 このテキストファイルは約20行で一塊のデータが入っており、次の20行からまた一塊のデータがはいっています。現在、全行を1行ごとにArrayListに格納してから 各行にキーワードが含まれていないかチェックし、含まれていたらそのデータの塊の中にある2行目を行を出力しようとしています。 しかし、10000行のArrayListを宣言しようとしたところ、途中でOutOfMemoryになり、メモリ不足になります。そこで、最初の20行を読み込み、処理をし、次の20行を読み込み処理をする・・・と考えているのですが、このようなことをJAVAのソースコードで実現することは可能でしょうか?? 皆さんは大容量ファイルを解析するときにどのような手法をとっていますか? ご教示いただければ幸いです。

    • ベストアンサー
    • Java
  • Excel VBA 配列の分割について

    Excel VBAでコーディングしていますが 行き詰っているのでお助け下さい。 (1)二次元配列に格納されている値の中から 特定の値が格納されている位置をループを使わず 取得したいのですがその方法が分かりません。 <例>  Dim x(2,2) As Valiant   x(0,0) = "あああ"   x(0,1) = "いいい"   x(0,2) = "ううう"   x(1,1) = "えええ"      ・      ・      ・ この配列から"えええ"が格納されている位置をループを使わず 取得する方法を教えてください。 ⇒ 1, 1 (2)二次元配列の指定した列(?)を一次元配列に 格納する方法も重ねて教えてください。 以上、よろしくお願いします。

専門家に質問してみよう