| Tripletail documentation | Contained in the Tripletail distribution. |
Tripletail::Debug - TL デバッグ用クラス
このクラスは use Tripletail 時に自動的に読み込まれる.
ini で "enable_debug" を0以外にセットすると、 以後デバッグ機能が有効になる。 ini グループ名は "Debug" でなければならない。
公開されているメソッドは存在しない。
flush内部メソッド
print内部メソッド
reset内部メソッド
enable_debug = 1
デバッグ機能を使用するか否か。省略可能。
1の場合、有効。 0の場合、無効。 デフォルトは0。
無効にした場合、全てのデバッグオプションの設定は無効となる。
popup_style = single
デバッグ情報のポップアップ表示の機能選択。可能な値は 'none', 'single', 'multiple' 。省略可能。
ポップアップ表示しない(デフォルト) noneに設定した場合、xxxxxx_popupでポップアップを指定しても無視される。
一つのウインドウに全情報を表示
表示する情報の個数分のウインドウを表示
warn_logging = 1
warnメッセージをログに残すか否か。省略可能。
1の場合、残す。 0の場合、残さない。 デフォルトは1。
warn_popup = 1
warnメッセージをポップアップ表示するように出力を加工するか否か。省略可能。
1の場合、加工する。 0の場合、加工しない。 デフォルトは1。
log_popup = 1
$TL->log されたログをポップアップ表示するように出力を加工するか否か。省略可能。
1の場合、加工する。 0の場合、加工しない。 デフォルトは1。
request_logging = 1
受け取ったリクエストデータの内容と、$CGI への変更履歴をログに残すか否か。省略可能。
1の場合、残す。 0の場合、残さない。 デフォルトは1。
request_logging_max = 100K
1回に出力するログの最大サイズの指定。0で無制限。省略可能。
デフォルトは0。
request_popup = 1
受け取ったリクエストデータの内容と、$CGI への変更履歴を別ウィンドウでポップアップするように、出力を加工するか否か。省略可能。
1の場合、加工する。 0の場合、加工しない。 デフォルトは1。
content_logging = 1
応答コンテンツをログに残すか否か。省略可能。
1の場合、残す。 0の場合、残さない。 デフォルトは1。
content_logging_max = 100K
1回のログの最大サイズ。0で無制限。省略可能。
デフォルトは0。
content_popup = 1
応答コンテンツを別ウィンドウでポップアップするように出力を加工するか否か。省略可能。
1の場合、加工する。 0の場合、加工しない。 デフォルトは1。
content_logging_max = 100K
1回のポップアップで表示する最大サイズ。0で無制限。省略可能。
デフォルトは0
template_logging = 1
使用した テンプレート ファイル名と、展開の内容をログに残すか否か。省略可能。
1の場合、残す。 0の場合、残さない。 デフォルトは1。
template_popup = 1
使用した テンプレートァイル名と、展開の内容を、別ウィンドウでポップアップするように出力を加工するか否か省略可能。
1の場合、加工する。 0の場合、加工しない。 デフォルトは1。
ポップアップで表示するためには、出力するコンテンツに </head> を含む HTML が存在しなければならない。
db_logging = 1
DB へのクエリをログに残すか否か。省略可能。
1の場合、残す。 0の場合、残さない。 デフォルトは1。
db_logging_level及びdb_profileにて残すログの内容を設定可能。
db_popup = 1
DB へのクエリログをポップアップ表示するように出力を加工するか否か。省略可能。
1の場合、加工する。 0の場合、加工しない。 デフォルトは1。
db_logging_level = 2
DB へのクエリをログに残す際、応答内容をどの程度残すかを設定する。省略可能。
1の場合、DBへ発行したクエリ文と実行時間を残す。 2の場合、DBへ発行されたクエリ文と実行時間に加え、応答内容を残す。 デフォルトは1。
db_profile = 1
DB へのクエリをログに残す際、実行計画をどこまで残すかを設定する。省略可能。
1の場合、DBへ発行したクエリ文と実行時間を残す。 2の場合、DBへ発行されたクエリ文と実行時間に加え、実行計画を残す。 デフォルトは1。
location_debug = 1
$TL->location でリダイレクトする際に、Locationヘッダの代わりに、HTML画面を表示する。 利用しない場合、すぐにリダイレクトするため、デバッグ情報を確認する時間が取れない。
1の場合、HTML画面を表示する。 0の場合、HTML画面を表示せず、Locationヘッダを出力する。 デフォルトは0。
[Debug] enable_debug=1 popup_type=single warn_logging=1 warn_popup=1 log_popup=1 request_logging=0 request_logging_max=0 request_popup=1 content_logging=0 content_logging_max=0 content_popup=1 content_popup_max=0 template_logging=0 template_popup=0 db_logging=1 db_popup=1 db_logging_level=1 db_profile=1 location_debug=1
Copyright 2006 YMIRLINK Inc.
This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself
このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。
Address bug reports and comments to: tl@tripletail.jp
HP : http://tripletail.jp/
| Tripletail documentation | Contained in the Tripletail distribution. |
# ----------------------------------------------------------------------------- # Tripletail::Debug - TL ãããã°ç¨ã¯ã©ã¹ # ----------------------------------------------------------------------------- package Tripletail::Debug; use strict; use warnings; use UNIVERSAL qw(isa); use Tripletail; sub _INIT_HOOK_PRIORITY() { 1_000_000_000 }; # é åºã¯åããªãã sub _PRE_REQUEST_HOOK_PRIORITY() { 1_000_000_000 } # æå¾ã§ãªããã°ãªããªãã sub _OUTPUT_FILTER_PRIORITY() { 1_000_000_000 } # æå¾ã§ãªããã°ãªããªãã our $_INSTANCE; 1; sub _getInstance { my $class = shift; if(!$_INSTANCE) { $_INSTANCE = $class->__new(@_); } $_INSTANCE; } sub __new { my $class = shift; my $group = shift; my $this = bless {} => $class; $this->{group} = defined $group ? $group : 'Debug'; $this->{enabled} = $TL->INI->get($this->{group} => 'enable_debug'); $this->{popup_type} = $TL->INI->get($this->{group} => 'popup_type', 'none'); $this->{warn_logging} = $TL->INI->get($this->{group} => 'warn_logging', 1); $this->{warn_popup} = $TL->INI->get($this->{group} => 'warn_popup', 1); $this->{log_popup} = $TL->INI->get($this->{group} => 'log_popup', 1); $this->{request_logging} = $TL->INI->get($this->{group} => 'request_logging', 1); $this->{request_popup} = $TL->INI->get($this->{group} => 'request_popup', 1); $this->{request_logging_max} = $TL->parseQuantity($TL->INI->get($this->{group} => 'request_logging_max', 0)); $this->{content_logging} = $TL->INI->get($this->{group} => 'content_logging', 1); $this->{content_popup} = $TL->INI->get($this->{group} => 'content_popup', 1); $this->{content_logging_max} = $TL->parseQuantity($TL->INI->get($this->{group} => 'content_logging_max', 0)); $this->{content_popup_max} = $TL->parseQuantity($TL->INI->get($this->{group} => 'content_popup_max', 0)); $this->{template_logging} = $TL->INI->get($this->{group} => 'template_logging', 1); $this->{template_popup} = $TL->INI->get($this->{group} => 'template_popup', 1); $this->{db_logging} = $TL->INI->get($this->{group} => 'db_logging', 1); $this->{db_popup} = $TL->INI->get($this->{group} => 'db_popup', 1); $this->{db_logging_level} = $TL->INI->get($this->{group} => 'db_logging_level', 1); $this->{db_profile} = $TL->INI->get($this->{group} => 'db_profile', 1); $this->{location_debug} = $TL->INI->get($this->{group} => 'location_debug', 0); $this->reset; if($this->{enabled}) { # æå¾ã«å¼ã°ããpreRequestãã³ãã©ãç»é²ã $TL->setHook( 'preRequest', _PRE_REQUEST_HOOK_PRIORITY, sub { if($this->{request_logging}) { $this->__log_request; } }, ); # æåã«å¼ã°ããinitãã³ãã©ãç»é²ã $TL->setHook( 'init', _INIT_HOOK_PRIORITY, sub { # èªåèªèº«ãContentFilterã¨ãã¦ç»é²ã $TL->setContentFilter( [__PACKAGE__, _OUTPUT_FILTER_PRIORITY], this => $this ); }, ); if($this->{warn_logging} or $this->{warn_popup}) { $SIG{__WARN__} = sub { my $msg = shift; isa($msg, 'Tripletail::Error') or $msg = $TL->newError(warn => $msg); if($this->{warn_logging}) { $TL->_log(__PACKAGE__, "Warn: $msg"); } if($this->{warn_popup}) { push @{$this->{warn_log}}, $msg; } }; } } $this; } sub reset { my $this = shift; $this->{header} = undef; $this->{header_buf} = ''; $this->{filter_buf} = ''; $this->{db_log} = []; $this->{db_log_data} = {}; $this->{dl_log} = []; $this->{form_log} = []; $this->{tl_log} = []; $this->{warn_log} = []; $this->{template_log} = []; $this->{explain_cache} = {}; $this->{popup} = []; } sub __log_request { my $this = shift; # $TL->CGIã®å 容ã¨%ENVã®å 容ããã°ã«åãã my $log = ''; if($TL->{CGIORIG}) { foreach my $key (sort $TL->{CGIORIG}->getKeys) { foreach my $value ($TL->{CGIORIG}->getValues($key)) { $log .= "[CGI:$key] $value\n"; } } } foreach my $key (sort keys %ENV) { $log .= "[ENV:$key] $ENV{$key}\n"; } my $lim = $this->{request_logging_max}; if($lim and length($log) > $lim) { substr($log, $lim) = '...'; } $TL->_log(__PACKAGE__, "Request Log:\n$log"); } #--------- Tripletail::Templateå°ç¨ sub _templateLog { my $this = shift; # node => Tripletail::Template::Node # type => new / expand / add / setForm / extForm/ addHiddenForm / addSessionCheck / toStr / flush # # ---[new] # 追å 弿°ç¡ã # # ---[expand] # args => HASH Ref # any => çå½å¤ # # ---[setattr] # args => HASH Ref # # ---[add] # 追å 弿°ç¡ã # # ---[setForm] # form => Tripletail::Form # name => ãã©ã¼ã å # # ---[extForm] # name => ãã©ã¼ã å # # ---[addHiddenForm] # form => Tripletail::Form # name => ãã©ã¼ã å # # ---[addSessionCheck] # name => ãã©ã¼ã å # # ---[toStr] # 追å 弿°ç¡ã # # ---[flush] # 追å 弿°ç¡ã if(!$this->{enabled} || (!$this->{template_popup} && !$this->{tamplate_logging}) || !$this->{template_log}) { return $this; } my $opts = { @_ }; my $node = $opts->{node}; # nodeã¯Tripletail::Templateãªãã¸ã§ã¯ãã§ãªããã¼ããã¹ã¨ããã(/foo/barå½¢å¼) $opts->{node} = $node->_nodePath; # "fpath"ã追å ããã®ãã³ãã¬ã¼ãããã¡ã¤ã«ããçæããããªãããã®ãã¡ã¤ã«åã my $root; for($root = $node; $root->{parent}; $root = $root->{parent}) {} $opts->{fpath} = defined $root->{fpath} ? $root->{fpath} : '(file path unknown)'; # formãããã°ãããã®cloneãä¿æã my $form = $opts->{form}; if($form) { $opts->{form} = $form->clone; if(!length($opts->{name})) { $opts->{name} = '(anonymous form)'; } } if($this->{template_popup}) { push @{$this->{template_log}}, $opts; } if($this->{template_logging}) { my $params_dump = ''; if($opts->{type} eq 'expand') { $params_dump .= sprintf("Any: %d\n", $opts->{any}); } if($opts->{type} eq 'setForm' || $opts->{type} eq 'addHiddenForm') { $params_dump .= sprintf("Form Name: %s\n", $opts->{name}); foreach my $key ($opts->{form}->getKeys) { $params_dump .= sprintf(" %s = %s\n", $key, $opts->{form}->get($key)); } } if($opts->{type} eq 'extForm') { $params_dump .= sprintf("Form Name: %s\n", $opts->{name}); } if($opts->{type} eq 'addSessionCheck') { $params_dump .= sprintf("Form Name: %s\n", $opts->{name}); } if($opts->{type} eq 'expand' || $opts->{type} eq 'setAttr') { foreach my $key (sort keys %{$opts->{args}}) { my $val = $opts->{args}{$key}; defined($val) or $val = '(null)'; $params_dump .= sprintf(" %s = %s\n", $key, $val); } } $TL->_log( __PACKAGE__, sprintf( "Template Trace [TYPE:$opts->{type}][FPATH:$opts->{fpath}][NODE:$opts->{node}]\n". "%s", $params_dump, ) ); } $this; } #--------- Tripletail::DBå°ç¨ sub _dbLog { my $this = shift; if (!$this->{enabled} || (!$this->{db_popup} && !$this->{db_logging}) || !$this->{db_log}) { return $this; } my $opts; if( @_ && UNIVERSAL::isa($_[0], 'CODE') ) { my $coderef = shift; $opts = { $coderef->(@_) }; }else { $opts = { @_ }; } local($@); eval 'require Data::Dumper'; $@ and die $@; # ãã©ã¡ã¼ã¿ã¯Data::Dumperã§ã®ãã³ããä¿åãã¦ããã my $params_dump = Data::Dumper->new([$opts->{params}]) ->Indent(0)->Terse(1)->Deepcopy(1)->Dump; if($this->{db_logging} && ($this->{db_profile} >= 1 || $this->{db_logging_level} >= 1)) { $TL->_log( __PACKAGE__, sprintf( "DB Trace [Map: %s][Set: %s][DB: %s][ID: %d]\n". "Elapsed: %s seconds\n". "Query:\n". "%s\n". "Parameters:\n". "%s", $opts->{group}, $opts->{set}, $opts->{db}, $opts->{id}, $opts->{elapsed}, $opts->{query}, $params_dump, ) ); } if ($Tripletail::IN_EXTENT_OF_STARTCGI) { push @{$this->{db_log}}, { group => $opts->{group}, set => $opts->{set}, db => $opts->{db}, id => $opts->{id}, query => $opts->{query}, params => $params_dump, elapsed => $opts->{elapsed}, error => $opts->{error}, }; push(@{$this->{db_log_data}{$opts->{id}}}, $opts->{names}); } $this; } sub _dbLogData { my $this = shift; if (!$this->{enabled} || (!$this->{db_popup} && !$this->{db_logging}) || !$this->{db_log} || ($this->{db_logging_level} <= 1)) { return $this; } my $opts; if( @_ && UNIVERSAL::isa($_[0], 'CODE') ) { my $coderef = shift; $opts = { $coderef->(@_) }; }else { $opts = { @_ }; } my $dump; my $data; if(ref($opts->{data}) eq 'ARRAY') { foreach my $data (@{$opts->{data}}) { $dump = (defined($data) ? $data : '(undef)') . "\n"; } $data = [ @{$opts->{data}} ]; } elsif(ref($opts->{data}) eq 'HASH') { $this->{db_log_data}{$opts->{id}}[0] = [ sort keys %{$opts->{data}} ]; $data = []; foreach my $key (sort keys %{$opts->{data}}) { push(@$data, $opts->{data}{$key}); $dump .= $key . ': ' . (defined($opts->{data}{$key}) ? $opts->{data}{$key} : '(undef)' ) . "\n"; } } if($this->{db_logging} && $this->{db_logging_level} >= 2) { $TL->_log( __PACKAGE__, sprintf("DB Trace [ID: %d]\n%s", $opts->{id}, $dump) ); } push(@{$this->{db_log_data}{$opts->{id}}}, $data); $this; } #--------- Tripletail::Formå°ç¨ sub _formLog { my $this = shift; my $opts = { @_ }; if (!$this->{enabled} || !$this->{request_logging} || !$this->{request_popup}) { return $this; } # ã¹ã¿ãã¯ã辿ããæåã«ç¾ããTripletail::以å¤ã®ããã±ã¼ã¸ãä½ã£ããã¬ã¼ã ãè¦ã¦ã # ãã¡ã¤ã«åã¨è¡çªå·ãå¾ãã my $loc = ''; for(my $i = 0;; $i++) { my ($pkg, $fname, $lineno) = caller $i; if($pkg !~ m/^Tripletail::/) { $fname =~ m!([^/]+)$!; $fname = $1; $loc = sprintf '%s:%d', $fname, $lineno; last; } } my $log = ''; my $data = ''; $log .= "Type: $opts->{type}\n"; $log .= "Loc: $loc\n"; if(exists($opts->{key})) { $data .= "Key: $opts->{key}\n"; } if(exists($opts->{value})) { $data .= "Value: $opts->{value}\n"; } if(exists($opts->{data})) { my $data_dump = Data::Dumper->new([$opts->{data}]) ->Indent(0)->Terse(1)->Deepcopy(1)->Dump; $data .= "Data: $data_dump\n"; } if(exists($opts->{form})) { my $form_dump = Data::Dumper->new([$opts->{form}]) ->Indent(0)->Terse(1)->Deepcopy(1)->Dump; $data .= "Form: $form_dump\n"; } if($this->{request_logging}) { $TL->_log(__PACKAGE__, "Form Log:\n$log$data"); } if($this->{request_popup}) { push @{$this->{form_log}}, { type => $opts->{type}, loc => $loc, data => $data, }; } $this; } #--------- $TL->log sub _tlLog { my $this = shift; my $opts = { @_ }; if (!$this->{enabled} || !$this->{log_popup} || !$this->{tl_log}) { return $this; } my @time = localtime; $time[5] += 1900; $time[4]++; push @{$this->{tl_log}}, { group => $opts->{group}, log => $opts->{log}, time => sprintf( '%04d-%02d-%02d %02d:%02d:%02d', @time[5, 4, 3, 2, 1, 0], ), }; $this; } #--------- Tripletail::startCgi sub _implant_disperror_popup { my $this = shift; $this->__db_explain; if($this->{enabled} && $this->{popup} && $this->{popup_type} ne 'none') { $this->__implant_template_popup; $this->__implant_warn_popup; $this->__implant_db_popup; $this->__implant_log_popup; $this->__implant_request_popup; $this->__implant_content_popup; $TL->dump(POPUP => $this->{popup}); $TL->dump(POPUPOBJ => $this); my $output = $this->__flush_popup; $this->reset; return $output; } $this->reset; ''; } #--------- Tripletail::Filter sub _new { my $class = shift; my $opts = { @_ }; $opts->{this}; } sub print { my $this = shift; my $data = shift; if(!$this->{enabled}) { return $data; } $this->{filter_buf} .= $data; if( $this->{popup_type} eq 'none' ) { return $data; }else { # buffering to rewrite after data gathered. return ''; } } sub flush { my $this = shift; $this->__db_explain; if($this->{content_logging}) { my $content = $this->{filter_buf}; my $lim = $this->{content_logging_max}; if($lim && length($content) > $lim) { substr($content, $lim) = '...'; } $TL->_log(__PACKAGE__, "Content Log:\n$content"); } my $data = $this->{filter_buf}; # ãã¼ã«ã«ãªã³ãã¼ while($data =~ s/^(.*?(?:\r?\n|\r))//) { $this->{header_buf} .= $1; if($this->{header_buf} =~ m/(?:\r?\n|\r){2}$/) { # äºã¤é£ç¶ããæ¹è¡ã³ã¼ãã§çµãã£ã¦ãã => ãããã®çµãã $this->{header} = {}; foreach(split /\r?\n|\r/, $this->{header_buf}) { if(m/^(.+?):\s*(.+)$/) { my $array = $this->{header}{lc $1}; if(!$array) { $array = $this->{header}{lc $1} = []; } push @$array, $2; } } my $ct = $this->{header}{'content-type'}[0]; if(!$ct || $ct !~ m/html/i) { # htmlãåºåãã¦ããã®ã§ãªããã°ãããã¢ããã¯ç¡å¹ã«ã $this->{popup} = undef; } # charsetå¤å® my $charset = 'Shift_JIS'; if($ct && $ct =~ m/charset=([^;]+)/i) { $charset = $1; } $this->{header}{_CHARSET_} = $charset; last; } } if($this->{filter_buf} =~ m|</head>|i && $this->{popup} && $this->{popup_type} ne 'none') { $this->__implant_template_popup; $this->__implant_warn_popup; $this->__implant_db_popup; $this->__implant_log_popup; $this->__implant_request_popup; $this->__implant_content_popup; my $html = $this->__flush_popup; $this->{filter_buf}=~ s|</head>|$html</head>|; } my $result; if( $this->{popup_type} eq 'none' ) { $result = ''; }else { # return buffered and rewritten html. $result = $this->{filter_buf}; } $this->reset; $result; } sub __flush_popup { my $this = shift; if(!@{$this->{popup}} || $this->{popup_type} eq 'none') { return ''; } if($this->{popup_type} eq 'multiple') { my $script = qq~ <script type="text/javascript"><!-- var win, doc; ~; foreach my $popup (@{$this->{popup}}) { # ãããã¨ããã¿ãä»ãã my ($header, $footer) = $this->__tmpl_master_popup; $header->node('anchor')->add( ID => $popup->[1], TITLE => $popup->[0], ); my $header_popup = $this->__implant_popup( funcname => '_tl_debug_HEADER_' . $popup->[1], html => $header->toStr, no_push => 1, ); my $footer_popup = $this->__implant_popup( funcname => '_tl_debug_FOOTER_' . $popup->[1], html => $footer->toStr, no_push => 1, ); $script .= qq~ win = window.open("", "_tl_debug_popup_window_$popup->[1]_", ""); if (win) { doc = win.document; doc.open(); ~; foreach my $parts ($header_popup, $popup, $footer_popup) { $script .= $parts->[2]; # 颿°å®ç¾© $script .= " $parts->[1](doc);\n"; # 颿°å¼åºã } $script .= qq~ doc.close(); } ~; } $script .= qq~ // --></script> ~; } elsif($this->{popup_type} eq 'single') { # ãããã¨ããã¿ãä»ãã my ($header, $footer) = $this->__tmpl_master_popup; foreach my $popup (@{$this->{popup}}) { $header->node('anchor')->add( ID => $popup->[1], TITLE => $popup->[0], ); } unshift @{$this->{popup}}, $this->__implant_popup( funcname => '_tl_debug_HEADER_', html => $header->toStr, no_push => 1, ); push @{$this->{popup}}, $this->__implant_popup( funcname => '_tl_debug_FOOTER_', html => $footer->toStr, no_push => 1, ); my $script = qq~ <script type="text/javascript"><!-- var win = window.open("", "_tl_debug_popup_window_", ""); if (win) { var doc = win.document; doc.open(); ~; foreach my $popup (@{$this->{popup}}) { $script .= $popup->[2]; # 颿°å®ç¾© $script .= " $popup->[1](doc);\n"; # 颿°å¼åºã } $script .= qq~ doc.close(); } // --></script> ~; $script; } else { die "invalid popup_type: [$this->{popup_tipe}]\n"; } } sub __db_explain { my $this = shift; if($this->{db_log} && @{$this->{db_log}}) { my $explain_id = 0; foreach my $entry (@{$this->{db_log}}) { next if($entry->{error}); if($this->__checkIfExplainable($entry->{query})) { my $explain = $this->__explain( $entry->{group}, $entry->{set}, $entry->{db}, $entry->{query}, $entry->{params} ); $this->{explain_log}[$explain_id] = $explain; $explain_id++; my $query = $TL->escapeTag($entry->{query}); $query =~ s~^\s*|\s*$~~g; my $explain_dump = ''; my @maxcolumn; foreach my $row (@$explain) { for(my $i = 0; $i < @$row; $i++) { $maxcolumn[$i] = length($row->[$i]) if(!$maxcolumn[$i] || $maxcolumn[$i] < length($row->[$i])); } } foreach my $row (@$explain) { $explain_dump .= ' | '; for(my $i = 0; $i < @$row; $i++) { $explain_dump .= ' ' x ($maxcolumn[$i] - length($row->[$i])) . $row->[$i] . ' | '; } $explain_dump .= "\n"; } if($this->{db_logging}) { $TL->_log( __PACKAGE__, sprintf( "DB Trace [Map: %s][Set: %s][DB: %s][ID: %d]\n". "Elapsed: %s seconds\n". "Query:\n". "%s\n". "Parameters:\n". "%s\n". "Explain:\n". "%s", $entry->{group}, $entry->{set}, $entry->{db}, $entry->{id}, $entry->{elapsed}, $query, $entry->{params}, $explain_dump ) ); } } } } } sub __implant_db_popup { my $this = shift; if($this->{db_log} and @{$this->{db_log}} && $this->{db_popup}) { my $t = $this->__tmpl_db_popup; local *expand_table = sub { my $node = shift; my $fetched = shift; foreach my $label (@{$fetched->[0]}) { $node->node('label')->add( LABEL => $label, ); } foreach my $row (@$fetched[1 .. @$fetched-1]) { foreach my $column (@$row) { $node->node('rows')->node('column')->add( COLUMN => (defined $column ? $column : '(NULL)'), ); } $node->node('rows')->add; } }; my $explain_id = 0; my $fetch_id = 0; foreach my $entry (@{$this->{db_log}}) { if($this->{db_profile} >= 2 && $this->__checkIfExplainable($entry->{query})) { next if($entry->{error}); $t->node('entry')->node('explain-link')->add( EXPLAIN_ID => $explain_id, ); my $explain = $this->{explain_log}[$explain_id]; expand_table( $t->node('entry')->node('explain-frame'), $explain ); $t->node('entry')->node('explain-frame')->add( EXPLAIN_ID => $explain_id, ); $explain_id++; } if($this->{db_logging_level} >= 2 && $this->__checkIfFetchable($entry->{query})) { $t->node('entry')->node('fetch-link')->add( FETCH_ID => $fetch_id, ); my $fetched = $this->{db_log_data}{$entry->{id}}; expand_table( $t->node('entry')->node('fetch-frame'), $fetched ); $t->node('entry')->node('fetch-frame')->add( FETCH_ID => $fetch_id, ); $fetch_id++; } my $query = $TL->escapeTag($entry->{query}); $query =~ s~^\s*|\s*$~~g; $query =~ s~\n~<br />~g; $query =~ s~(--[^\n]+)~<span class="comment">$1</span>~g; $query =~ s~(/\*.+?\*/)~<span class="comment">$1</span>~sg; my $params = $TL->escapeTag($entry->{params}); $params =~ s~\s~ ~g; $params =~ s~\n~<br />~g; $t->node('entry')->setAttr( QUERY => 'raw', PARAMS => 'raw', ); $t->node('entry')->add( GROUP => $entry->{group}, SET => $entry->{set}, DB => $entry->{db}, QUERY => $query, PARAMS => $params, ELAPSED => "$entry->{elapsed} sec", ); } $this->__implant_popup( html => $t->toStr, title => 'DB使ç¨ãã°', funcname => '_tl_debug_db_popup_', ); } } sub __implant_template_popup { my $this = shift; if($this->{template_log} && @{$this->{template_log}}) { my $log = $this->{template_log}; $this->{template_log} = undef; # ãã以éã¯Tempateãã°ãåããªãã my $t = $this->__tmpl_template_popup; foreach my $entry (@$log) { my %params = %$entry; my $force_defined = sub { my $value = shift; defined $value ? $value : '(null)'; }; if($entry->{type} eq 'expand' || $entry->{type} eq 'setattr') { # ã¤ãã¬ã¼ã¿ããªã»ãããã代ããã«ã³ãã¼ãåãã my %copy = %{$entry->{args}}; while (my ($key, $value) = each %copy) { defined($value) or $value = '(null)'; $t->node('entry')->node($entry->{type})->node('arg')->add( KEY => $key, VALUE => $force_defined->($value), ); } delete $params{args}; } elsif($entry->{type} eq 'setForm' || $entry->{type} eq 'addHiddenForm') { foreach my $key ($entry->{form}->getKeys) { foreach my $value ($entry->{form}->getValues($key)) { $t->node('entry')->node($entry->{type})->node('pair')->add( KEY => $key, VALUE => $force_defined->($value), ); } } delete $params{form}; } my $type = delete $params{type}; my $fpath = delete $params{fpath}; my $node = delete $params{node}; $t->node('entry')->node($entry->{type})->add(%params); $t->node('entry')->add( TYPE => $type, FPATH => $fpath, NODE => $node, ); } $this->__implant_popup( html => $t->toStr, title => 'ãã³ãã¬ã¼ã使ç¨ãã°', funcname => '_tl_debug_template_popup_', ); } $this; } sub __implant_warn_popup { my $this = shift; if($this->{warn_log} && @{$this->{warn_log}}) { my $t = $this->__tmpl_warn_popup; foreach my $entry (@{$this->{warn_log}}) { my $content = $TL->escapeTag($entry); $content =~ s~\n~<br />~g; $content =~ s~\s~ ~g; $t->node('entry')->setAttr( CONTENT => 'raw', ); $t->node('entry')->add( CONTENT => $content, ); } $this->__implant_popup( html => $t->toStr, title => 'warn', funcname => '_tl_debug_warn_popup_', ); } $this; } sub __implant_log_popup { my $this = shift; if($this->{tl_log} && @{$this->{tl_log}}) { # $TL->logã®ãã° my $log = $this->{tl_log}; $this->{tl_log} = undef; # ãã以éã¯$TL->logãã°ãåããªã my $t = $this->__tmpl_log_popup; foreach my $entry (@$log) { my $content = $TL->escapeTag($entry->{log}); $content =~ s~[ \t]~ ~g; $content =~ s~\n~<br />~g; $t->node('entry')->setAttr( CONTENT => 'raw', ); $t->node('entry')->add( GROUP => $entry->{group}, TIME => $entry->{time}, CONTENT => $content, ); } $this->__implant_popup( html => $t->toStr, title => '$TL->logãã°', funcname => '_tl_debug_log_popup_', ); } $this; } sub __implant_request_popup { my $this = shift; if($this->{request_popup} && $TL->{CGIORIG}) { # $TL->CGIã®å 容ã¨%ENVã®å 容 my $t = $this->__tmpl_request_popup; foreach my $key (sort $TL->{CGIORIG}->getKeys) { foreach my $value (sort $TL->{CGIORIG}->getValues($key)) { $t->node('cgi')->add( KEY => $key, VALUE => $value, ); } } $t->node('form')->setAttr(DATA => 'br'); foreach my $data (@{$this->{form_log}}) { $t->node('form')->add($data); } foreach my $key (sort keys %ENV) { foreach my $value ($ENV{$key}) { $t->node('env')->add( KEY => $key, VALUE => $value, ); } } $this->__implant_popup( html => $t->toStr, title => 'ãªã¯ã¨ã¹ãæ å ±', funcname => '_tl_debug_request_popup_', ); } $this; } sub __implant_content_popup { my $this = shift; if($this->{content_popup} && length($this->{filter_buf})) { my $t = $this->__tmpl_content_popup; my $content = $this->{filter_buf}; my $lim = $this->{content_popup_max}; if($lim && length($content) > $lim) { substr($content, $lim) = '...'; } $content = $TL->charconv($content, 'auto' => 'UTF-8'); $t->setAttr(VALUE => 'br'); $t->expand(VALUE => $content); $this->__implant_popup( html => $t->toStr, title => 'ã³ã³ãã³ãæ å ±', funcname => '_tl_debug_content_popup_', ); } $this; } sub __implant_popup { my $this = shift; my $opts = do { my %args = @_; \%args; }; my $funcname = $opts->{funcname}; my $html = $opts->{html}; my $title = $opts->{title}; my $no_push = $opts->{no_push}; my $script = " function $funcname(doc) {\n"; if(defined($title)) { $script .= qq{ doc.writeln(" <h1 id=\\"$funcname\\">$title</h1>");\n}; } my $functions; my $line = 0; foreach(split /\r?\n|\r/, $html) { if( (++$line%1000)==0 ) { if( !$functions ) { $script =~ s/^ function (\w+)/ function $1_001/; $functions = 1; } ++$functions; $script .= " }\n"; my $fname = sprintf('%s_%03d',$funcname,$functions); $script .= " function $fname(doc) {\n"; } # ãããã¢ããã¦ã¤ã³ãã¦ã®HTMLã®å ¨ã¦ã®è¡ãã # doc.writeln("..."); ã¨ããå½¢ã«ããã s/\\/\\\\/g; s/"/\\"/g; s/--/"+"-"+"-"+"/g; s!//!"+"/"+"/"+"!g; s!</script>!"+"<"+"/sc"+"ript>"+"!g; $script .= qq{ doc.writeln("$_");\n}; } $script .= " }\n"; if( $functions ) { $script .= " function $funcname(doc) {\n"; foreach my $i (1..$functions) { my $fname = sprintf('%s_%03d', $funcname, $i); $script .= " $fname(doc);\n"; } $script .= " }\n"; } $script =~ s/\n/\r\n/g; $script = $TL->charconv($script, 'utf8' => $this->{header}{_CHARSET_}); my $popup = [$title, $funcname, $script]; if(!$no_push) { push @{$this->{popup}}, $popup; } $popup; } #--------------- DBé¢é£ sub __checkIfExplainable { my $this = shift; my $sql = shift; $this->__findCommand($sql) eq 'SELECT'; } sub __checkIfFetchable { my $this = shift; my $sql = shift; my $allowed = { SHOW => 1, EXPLAIN => 1, DESC => 1, SELECT => 1, }; $allowed->{$this->__findCommand($sql)}; } sub __findCommand { my $this = shift; my $sql = shift; while(1) { $sql =~ s/^\s*//; if($sql =~ s|^/\*.+?\*/||) { next; } elsif($sql =~ s,--.+?(?:\r?\n|\r),,) { next; } elsif($sql =~ m|^(\w+)|) { return uc $1; } else { die __PACKAGE__."#__findCommand: found no commands in sql [$sql] (SQLããã³ãã³ããè¦ã¤ãããã¨ãã§ãã¾ããã§ãã)\n"; } } } sub __explain { my $this = shift; my $group = shift; my $dbset = shift; my $dbname = shift; # undefå¯ my $query = shift; my $params = shift; if(!$this->{explain_cache}{$group}) { $this->{explain_cache}{$group} = {}; } if(my $cached = $this->{explain_cache}{$group}{$query}) { return $cached; } $this->{explain_cache}{$group}{$query} = $this->__executeSql($group, $dbset, $dbname, "EXPLAIN $query", $params); } sub __executeSql { my $this = shift; my $group = shift; my $dbset = shift; my $dbname = shift; # undefå¯ my $query = shift; my $params = shift; # å é¨ç¨ã®ã¯ã¨ãªã¯ãããã°ãã°ã«æ®ããªã local($this->{db_log}); $this->{db_log} = undef; $params = do { local($@); eval $params; # Dumpãã¦ãã£ããã®ãæ»ã }; my $DB = $TL->getDB($group); my $sth = $DB->execute(\$dbset => $query, @$params); my $result = []; $result->[0] = [ @{$sth->nameArray} ]; # 0çªç®ããã£ã¼ã«ãå while(my $row = $sth->fetchArray) { foreach my $col (@$row) { $col = '(null)' if(!defined($col)); } push @$result, [ @$row ]; } $result; } #------------- CSS åã³ãã³ãã¬ã¼ã sub __popup_css { my $this = shift; my $is_opera = ($ENV{HTTP_USER_AGENT}||'')=~/Opera/i; qq{ <style> * { margin: 0; padding: 0; } body { font-size: 100%; color: black; background-color: white; text-align: left; margin-bottom: 50px; @{[ $is_opera ? ' overflow: scroll;' : '' ]} } a { text-decoration: none; color: #880000; border-color: #660000; border-style: dotted; border-width: 0 0 1px 0; } h1 { font-size: 150%; text-align: left; color: #eecc22; border-color: #eecc22; border-width: 1px; border-style: dashed; padding: 3px; margin-top: 60px; margin-bottom: 0; margin-right: 2%; margin-left: 2%; } h2 { font-size: 130%; color: #888833; background-color: #ffeebb; border-style: solid; border-width: 2px 2px 2px 10px; border-color: #eecc22; padding: 3px; margin-top: 10px; margin-bottom: 10px; margin-right: 2%; margin-left: 2%; } h1 + table { margin-top: 10px; } table { width: 94%; margin-left: 2%; margin-right: 2%; } table table { width: 100%; background-color: #ddddbb; margin: 2px; } td, th { background-color: #ffffdd; border-style: dashed; border-width: 1px; border-color: #ddddbb; padding: 2px; margin: 2px; } th { font-weight: bold; background-color: #eeeecc; } li { margin: 3px 0 2px 0; } dt, dl { margin: 1px; padding: 2px; } dt { font-weight: bold; } dd { margin-left: 30px; } dl dl { margin-left: 30px; } dl dl dt { background-color: #eeeecc; } dl dl dd { background-color: #ffffee; margin-left: 1px; } ol, ul { list-style-type: none; } .comment { font-size: 90%; color: #aa0000; } .initial-hidden { display: none; } .controller { position: fixed; top: 0; width: 100%; color: #888833; background-color: #ffffbb; border-style: solid; border-width: 0 0 2px 0; border-color: #cccc22; padding: 3px; font-size: 120%; font-weight: bold; } .controller a { font-size: 80%; } .footer { position: fixed; bottom: 0; font-size: 80%; color: #888833; background-color: #ffffbb; border-style: solid; border-width: 2px 0 0 0; border-color: #cccc22; padding: 3px; margin: 5px 0 0 0; } </style>}; } sub __iframe_css { my $this = shift; qq{ <style> * { margin: 0; padding: 0; } body { font-size: 100%; color: black; background-color: #ffffdd; text-align: left; } table { width: 100%; } td, th { background-color: #ffffdd; border-style: dashed; border-width: 1px; border-color: #ddddbb; padding: 2px; margin: 2px; } </style>}; } sub __tmpl_master_popup { my $this = shift; my $charset = $this->{header}{_CHARSET_} || 'UTF-8'; my $css = $this->__popup_css; my $group = $this->{group}; my @result; push @result, $TL->newTemplate->setTemplate( # ããã qq{<?xml version="1.0" encoding="$charset"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11.dtd"> <html xml:lang="ja" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=$charset" /> <meta http-equiv="Content-Style-Type" content="text/css" /> <title>[Tripletail::Debug] ãããã°æ å ±</title> $css <script type="text/javascript"><!-- function scroll_to_anchor(id) { var elem = document.getElementById(id); window.scroll(0, elem.offsetTop - 60); } // --></script> </head> <body> <div class="controller"> <p>[Tripletail::Debug] ãããã°æ å ±</p> <p> <!begin:anchor> <a href="javascript:scroll_to_anchor('<&ID>')"><&TITLE></a> <!end:anchor> </p> </div> }); push @result, $TL->newTemplate->setTemplate( # ããã¿ qq{ <p class="footer"> ãã®ã¦ã¤ã³ãã¦ã¯Tripletail::Debugã表示ãã¦ãã¾ãã 表示ããããã«ã¯ã ini ãã¡ã¤ã« [$group] ã»ã¯ã·ã§ã³ã® enable_debug ãç¡å¹ã«ãã¦ä¸ããã </p> </body> </html> }); @result; } sub __tmpl_template_popup { my $this = shift; $TL->newTemplate->setTemplate(qq{ <table> <tr> <th>æä½</th><th>ãã¡ã¤ã«</th><th>ãã¼ã</th><th>ãã©ã¡ã¼ã¿</th> </tr> <!begin:entry> <tr> <td><&TYPE></td><td><&FPATH></td><td><&NODE></td> <td> <!begin:new><!end:new> <!begin:set><!end:set> <!begin:load><!end:load> <!begin:setattr> <dl> <dt>args</dt> <dl> <!begin:arg> <dt><&KEY></dt><dd><&VALUE></dd> <!end:arg> </dl> </dl> <!end:setattr> <!begin:expand> <dl> <dt>any</dt><dd><&ANY></dd> <dt>args</dt> <dl> <!begin:arg> <dt><&KEY></dt><dd><&VALUE></dd> <!end:arg> </dl> </dl> <!end:expand> <!begin:add><!end:add> <!begin:setForm> <dl> <dt>name of form</dt><dd><&NAME></dd> <dt>content</dt> <dl> <!begin:pair> <dt><&KEY></dt><dd><&VALUE></dd> <!end:pair> </dl> </dl> <!end:setForm> <!begin:extForm> <dl> <dt>name of form</dt><dd><&NAME></dd> </dl> <!end:extForm> <!begin:addHiddenForm> <dl> <dt>name of form</dt><dd><&NAME></dd> <dt>content</dt> <dl> <!begin:pair> <dt><&KEY></dt><dd><&VALUE></dd> <!end:pair> </dl> </dl> <!end:addHiddenForm> <!begin:addSessionCheck> <dl> <dt>name of form</dt><dd><&NAME></dd> </dl> <!end:addSessionCheck> <!begin:toStr><!end:toStr> <!begin:flush><!end:flush> </td> </tr> <!end:entry> </table> }); } sub __tmpl_request_popup { my $this = shift; $TL->newTemplate->setTemplate(qq{ <h2>CGI</h2> <table> <!begin:cgi> <tr><th><&KEY></th><td><&VALUE></td></tr> <!end:cgi> </table> <table> <!begin:form> <tr><td><&LOC></td><th><&TYPE></th><td><&DATA></td></tr> <!end:form> </table> <h2>ENV</h2> <table> <!begin:env> <tr><th><&KEY></th><td><&VALUE></td></tr> <!end:env> </table> }); } sub __tmpl_content_popup { my $this = shift; $TL->newTemplate->setTemplate(qq{ <h2>Content</h2> <table> <tr><td><&VALUE></td></tr> </table> }); } sub __tmpl_log_popup { my $this = shift; $TL->newTemplate->setTemplate(qq{ <table> <tr><th>ã°ã«ã¼ã</th><th rowspan="2">å 容</th></tr> <tr><th>æå»</th></tr> <!begin:entry> <tr><td><&GROUP></td><td rowspan="2"><&CONTENT></td></tr> <tr><td><&TIME></td></tr> <!end:entry> </table> }); } sub __tmpl_warn_popup { my $this = shift; $TL->newTemplate->setTemplate(qq{ <table> <tr><th>warnã¡ãã»ã¼ã¸</th></tr> <!begin:entry> <tr><td><&CONTENT></td></tr> <!end:entry> </table> }); } sub __tmpl_db_popup { my $this = shift; $TL->newTemplate->setTemplate(qq{ <table> <tr> <th>ã°ã«ã¼ã</th> <th>ã»ãã</th> <th>ã³ãã¯ã·ã§ã³</th> <th>ã¯ã¨ãª</th> <th>ãã©ã¡ã¼ã¿</th> <th>executeå®è¡æé</th> <th>詳細</th> </tr> <!begin:entry> <tr> <td><&GROUP></td> <td><&SET></td> <td><&DB></td> <td><&QUERY></td> <td><&PARAMS></td> <td><&ELAPSED></td> <td> <ul> <!begin:explain-link> <li> <a href="javascript:tl_debug_toggle_display('TL_EXPLAIN_<&EXPLAIN_ID>')"> Explain </a> </li> <!end:explain-link> <!begin:fetch-link> <li> <a href="javascript:tl_debug_toggle_display('TL_FETCH_<&FETCH_ID>')"> çµæ </a> </li> <!end:fetch-link> </ul> </td> </tr> <!begin:explain-frame> <tr> <td colspan="7"> <div class="initial-hidden" id="TL_EXPLAIN_<&EXPLAIN_ID>"> <table> <tr><!begin:label><th><&LABEL></th><!end:label></tr> <!begin:rows> <tr><!begin:column><td><&COLUMN></td><!end:column></tr> <!end:rows> </table> </div> </td> </tr> <!end:explain-frame> <!begin:fetch-frame> <tr> <td colspan="7"> <div class="initial-hidden" id="TL_FETCH_<&FETCH_ID>"> <table> <tr><!begin:label><th><&LABEL></th><!end:label></tr> <!begin:rows> <tr><!begin:column><td><&COLUMN></td><!end:column></tr> <!end:rows> </table> </div> </td> </tr> <!end:fetch-frame> <!end:entry> </table> <script type="text/javascript"><!-- function tl_debug_toggle_display(id) { var elem = document.getElementById(id); if (elem.style.display == "block") { elem.style.display = "none"; } else { elem.style.display = "block"; } } // --></script> }); } __END__