2007年03月07日 11:00 [Edit]
勝手に添削 - Ajaxでデバッグしよう - @IT
記事そのものはまあいいのだけど、CGIの部分があまりにひどいので。
Ajaxでデバッグしよう - @IT私の仕事の現場ではJavaを開発言語として使用することが多いので、JSPやServletとして実装していますが、今回はレンタルサーバでも利用しやすいようにPerlで作成しました。以下のソースを参考にしてください。
Before
まあご覧下さい。前世紀末においてすら[これはひどい]レベルです。著者はJava屋さんとのことなのでその点を甘く見ても、一体何を参考にして書いたのか是非お聞かせください。
local %params; # エージェントはGETメソッドだけを使用するのでPOSTは考慮しない # リモートログエージェントから送られてたログ情報を環境変数から取得する。 @params = split('&', $ENV{'QUERY_STRING'}); # ログ情報はURLエンコードされているのでデコード(エンコード前の情報に)する。 foreach $param (@params) { # メッセージ内に'='が使用されている場合があるので、最初の'='を':='に変換する $param =~ s/=/:=/; # ':='にてパラメータ名と値を分離する ($name, $val) = split(':=', $param); $name =~ tr/+/ /; $val =~ tr/+/ /; $name =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg; $val =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg; $params{$name} = $val; } # 複数ユーザにて同時使用するため、利用ユーザを区別するために # 利用者のアドレスを取得する。 $remote_addr = $ENV{'REMOTE_ADDR'};ソース4:Remote Log サービス − 引数処理部# 複数ユーザにて同時使用するため、利用ユーザを区別するために # 利用者のアドレスを取得する。 $remote_addr = $ENV{'REMOTE_ADDR'}; # # ログ記録日付時間をシステムから取得する。 ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);(本来は1行) # ログファイル名を作成する。日付毎にファイルが別になるようする。 $logfilename = sprintf "logfile%4.4d%2.2d%2.2d.log", ($year+1900),($mon+1),$mday;(本来は1行) # ログ記録日時からタイムスタンプ文字列を作る。 $logtime = sprintf "%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d", ($year+1900),($mon+1),$mday,$hour,$min,$sec;(本来は1行) # ファイルが存在しない場合は新規に作成する。 # ファイルがある場合は、追記モードにてオープンする。 open logfile, ">>${logfilename}" or die "Can not open logfile\n";(本来は1行)ソース5:Remote Log サービス − ログ記録処理部# 取得した情報を元にログをファイルに記録する。 print logfile "${logtime} C ${remote_addr} "; print logfile "${params{'module'}} ${params{'func'}} \"${params{'message'}}\"\n";(本来は1行) # 複数ユーザーにて同時使用するため、利用ユーザーを区別するために # 利用者のアドレスを取得する。 $remote_addr = $ENV{'REMOTE_ADDR'}; # 取得した情報を元にログをファイルに記録する。 print logfile "${logtime} C ${remote_addr} ";ソース6:サーバ側サービス・プログラムのポイント
After
書き直すとこうなります。
#!/usr/local/bin/perl -T
use strict;
use warnings;
use CGI;
use CGI::Carp qw/fatalsToBrowser/;
use Fcntl ':flock';
use POSIX qw/strftime/;
my $q = CGI->new();
my $module = $q->param('module');
my $func = $q->param('func');
my $message = $q->param('message');
my @now = localtime();
my $logfilename = strftime( "logfile%Y%m%d.log", @now );
my $logtime = strftime( "%Y/%m/%d %T", @now );
my $logline = "$logtime C $ENV{REMOTE_ADDR} $module $func $message\n";
open my $wfh, ">>", $logfilename or die "$logfilename:$!";
flock $wfh, LOCK_EX; # let no one else write till I'm finished
seek $wfh, 0, 2; # we really make sure we are appending
print $wfh $logline;
flock $wfh, LOCK_UN; # just for sure
close $wfh;
# print $q->header("text/plain"), $logline;
print $q->header("text/plain"), $logtime; # 4 IE's sake!
三種の神器
-T, strict, warningsはCGIであれば絶対に使いましょう。
出力
最低でもContent-Type:ヘッダーだけでもSTDOUTに出力しておかないと、たとえプログラムが正常終了したとしてもStatus 500として扱われます。ここではtext/plainでログと同じ出力ログ時刻をを返すようにしています。
use CGI; #後生だから
今時自分でquery stringをparseするというのはPerlに限らず信じ難いことです。CGIモジュールを使いましょう。標準装備です。
POSIX::strftime
上記二点があればたいていの及第点が取れるのですが、ここからはtips。
時刻をきれいに文字列にしたい場合、POSIXモジュールにあるstrftime()を使うことが出来ます。覚えておいても損はないでしょう。こちらも標準装備。
flock
CGIに限らず、Webサーバー側のプログラムは同時に複数走ることを想定しなければなりません。ログファイルのように同一のファイルに書き込む可能性がある場合は、flock()を使ってロックしておくのが賢明です。さらに慎重をきたすなら、Sys::SyslogやLog::Log4perlの利用を検討してもよいでしょう。
出力
感想
このあたりのことは、すでに4年前に出た拙著にも書いたことばかり。すでにPerlを日常的に使う人にはこのあたりの作法は行き渡ったように思いますが、今回のように「たまにPerlを使う人」にもまでは行き渡っていないのかしらん....
Dan the Perl Monger
追記:
酒日記 はてな支店 - text/plainでも外部からの入力をそのまま出すのは危ないIE には Content-Type が text/plain でも HTML っぽかったら HTML として扱うという、有名なお節介機能があるので……
さいでした。というわけでちょこっと対応。ありがとうございました。
naoyaグループ - naoyaの日記 - CGIかなあ
Spiffyが好きならこちらか:)
この記事へのトラックバックURL
「perl cgi」でぐぐってみて、トップ10サイトぐらいを見てみるとわかるんでは。こんなのばっかりですよ。これが世間のperlに対する認識の現状。
状況説明を。
何故、みんな私をスルーするんでしょ?
弟子にしてくれませんか?
全くの0知識からゲームプログラマーになろうと思うんですが、
何かオススメな本とか、ありませんか?
> Beforeのソースはなんで改行がなくなっているのでしょう?
IEでは確かに改行がないのですが、
FireFoxで確認しますと、ちゃんと改行されていますね。
# このあたりのIEの表示が私もいやです。
だとすると、ファイル末尾に追記されることをカーネルが保証してくれる
わけで、flock や seek は単に遅くなるだけで無駄なのでは?
× http://search.cpan.org/perldoc?Log::Log4Perl
○ http://search.cpan.org/perldoc?Log::Log4perl
'p'が小文字です。
あ、ほんとだ。ありがとうございます。
dan the case insensitive
そんなサーバーは窓から投げ捨てるべき(それ犯罪)
>そんなサーバーは窓から投げ捨てるべき(それ犯罪)
捨てたいけど。。。orz
これを鵜呑みにすれば、ここにはHTMLを書くべきで、改行されずに表示するIEの方が仕様に忠実、ともいえるのかなと。(<pre></pre>というタグもあるのですし。)この辺り、詳しい人おられないでしょうか。
改行されているのは CSS の white-space:pre; 指定によるもので、これがIE6の後方互換モードでは無視される、と言うことなのですね。(これは別にW3Cの仕様外れではないらしい)
http://www.seo-equation.com/html/css/white-space
勉強になりました。
# でも、仮にも引用ですし、印象操作のように見えてしまうとあれ
# なので、報告のあった時点で、IE6ユーザのシェアもふまえて、
# 「Before」の方に<pre>タグを入れてやるくらいの配慮はあって
# いいかも。これはStrictな対処ですし。
コメント欄での議論を元に、blockquote部を加筆訂正しました。ありがとうございました。
Dan the CSS Illiterate
IE7がもっとまとも(でがんがん普及する)なら、こういう苦労も減るんでしょうが。現状では主に処理の軽さでIE6(コンポーネント)を使い続ける人は多いんだろうな・・・。