2007年03月07日 11:00 [Edit]

勝手に添削 - Ajaxでデバッグしよう - @IT

camel

記事そのものはまあいいのだけど、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::SyslogLog::Log4perlの利用を検討してもよいでしょう。

出力

感想

このあたりのことは、すでに4年前に出た拙著にも書いたことばかり。すでにPerlを日常的に使う人にはこのあたりの作法は行き渡ったように思いますが、今回のように「たまにPerlを使う人」にもまでは行き渡っていないのかしらん....

Dan the Perl Monger

追記:

酒日記 はてな支店 - text/plainでも外部からの入力をそのまま出すのは危ない
IE には Content-Type が text/plain でも HTML っぽかったら HTML として扱うという、有名なお節介機能があるので……

さいでした。というわけでちょこっと対応。ありがとうございました。

naoyaグループ - naoyaの日記 - CGI
かなあ

Spiffyが好きならこちらか:)


この記事へのトラックバックURL

この記事へのトラックバック
404 Blog Not Found:勝手に添削 - Ajaxでデバッグしよう - @IT
404 Blog Not Found:勝手に添削 - Ajaxでデバッグしよう - @IT【】at 2012年01月19日 21:10
http://blog.livedoor.jp/dankogai/archives/50780781.html なんか流行ってるらしいのでpythonで。 #!/usr/bin/python from datetime import datetime import os,cgi import fcntl f = cgi.FieldStorage() module = f.getvalue("module") func = f.getvalue("fu
[技術]勝手に添削ーpythonだと【回転と脱線】at 2007年03月10日 01:45
勝手に添削 - Ajaxでデバッグしよう - @ITとnaoyaグループ - n...
勝手に書き換え【ハズレ日記】at 2007年03月08日 21:44
dankogaiさんの404 Blog Not Found:勝手に添削 - Ajaxでデバッグしよう - @ITにてflookが必要なのかが論争になっているようなのでヒントだけ指し示した。 use strict; use warnings; open my $tokuhirom, ’>>/tmp/child’ or die $!; open my $oppaisan, ’>&...
勝手に小作り【jankogaiの日記】at 2007年03月08日 13:12
404 Blog Not Found:勝手に添削 - Ajaxでデバッグしよう - @IT http://blog.livedoor.jp/dankogai/archives/50780781.html naoyaグループ - naoyaの日記 - CGI http://naoya.g.hatena.ne.jp/naoya/20070307/1173255756 Web経由でログを閲覧できるインタラクティブ機能を追...
キミたちの添削例はまだまだだ【ひろ式めもちょう】at 2007年03月07日 18:37
INTER-GATE運営事務局です。 □Ameba News|テレビ局の採用試験...
第86回目はネットのお役立ち情報??替え玉はラーメンくらいにしとく【INTER-WEB】at 2007年03月07日 18:25
この記事へのコメント
お忙しいのでしょうに、素早い修正、素敵です。
IE7がもっとまとも(でがんがん普及する)なら、こういう苦労も減るんでしょうが。現状では主に処理の軽さでIE6(コンポーネント)を使い続ける人は多いんだろうな・・・。
Posted by at 2007年03月16日 20:08
各位、
コメント欄での議論を元に、blockquote部を加筆訂正しました。ありがとうございました。
Dan the CSS Illiterate
Posted by at 2007年03月16日 17:07
ちゃんと見てみると、FireFox でも素では改行されませんね。> blockquote
改行されているのは CSS の white-space:pre; 指定によるもので、これがIE6の後方互換モードでは無視される、と言うことなのですね。(これは別にW3Cの仕様外れではないらしい)
http://www.seo-equation.com/html/css/white-space
勉強になりました。
# でも、仮にも引用ですし、印象操作のように見えてしまうとあれ
# なので、報告のあった時点で、IE6ユーザのシェアもふまえて、
# 「Before」の方に<pre>タグを入れてやるくらいの配慮はあって
# いいかも。これはStrictな対処ですし。
Posted by at 2007年03月16日 11:43
HTMLの blockquote タグですが、どちらがより strict な表示なんでしょう?適当に調べていたら、HTML4.0 Strict では <blockquote></blockquote> の中には(bodyタグと同様に)マークアップされていないテキストは置けない、といった様な記述がちらほら見られました。
これを鵜呑みにすれば、ここにはHTMLを書くべきで、改行されずに表示するIEの方が仕様に忠実、ともいえるのかなと。(<pre></pre>というタグもあるのですし。)この辺り、詳しい人おられないでしょうか。
Posted by at 2007年03月16日 10:39
>>世の中にはuseでエラーになっちゃうレベルのレンタルサーバーだってあるんですよ。
>そんなサーバーは窓から投げ捨てるべき(それ犯罪)
捨てたいけど。。。orz
Posted by AC at 2007年03月11日 22:01
>世の中にはuseでエラーになっちゃうレベルのレンタルサーバーだってあるんですよ。
そんなサーバーは窓から投げ捨てるべき(それ犯罪)
Posted by   at 2007年03月09日 22:46
Keyさん、
あ、ほんとだ。ありがとうございます。
dan the case insensitive
Posted by at 2007年03月08日 13:24
Log4perl の URL がまちがっているようです。

× http://search.cpan.org/perldoc?Log::Log4Perl
○ http://search.cpan.org/perldoc?Log::Log4perl

'p'が小文字です。

Posted by Key at 2007年03月08日 10:51
">>" を指定したら O_APPEND が open(2) に渡されるのではないですか?
だとすると、ファイル末尾に追記されることをカーネルが保証してくれる
わけで、flock や seek は単に遅くなるだけで無駄なのでは?
Posted by soda at 2007年03月08日 08:14
> XYさん
> Beforeのソースはなんで改行がなくなっているのでしょう?

IEでは確かに改行がないのですが、
FireFoxで確認しますと、ちゃんと改行されていますね。

# このあたりのIEの表示が私もいやです。
Posted by Key at 2007年03月07日 21:42
世の中にはuseでエラーになっちゃうレベルのレンタルサーバーだってあるんですよ。
Posted by AC at 2007年03月07日 19:59
小飼先生。
状況説明を。
何故、みんな私をスルーするんでしょ?

弟子にしてくれませんか?
全くの0知識からゲームプログラマーになろうと思うんですが、
何かオススメな本とか、ありませんか?
Posted by 大勲位モーリス・G・茶茶 at 2007年03月07日 14:00
> 一体何を参考にして書いたのか
「perl cgi」でぐぐってみて、トップ10サイトぐらいを見てみるとわかるんでは。こんなのばっかりですよ。これが世間のperlに対する認識の現状。
Posted by とおりすがり at 2007年03月07日 12:38
Beforeのソースはなんで改行がなくなっているのでしょう?
Posted by XY at 2007年03月07日 12:19
そして、「Perl は汚い」と言われるのは、往々にして彼らのコードが原因。
Posted by X at 2007年03月07日 11:36