Perl logging

5,700 views

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,700
On SlideShare
0
From Embeds
0
Number of Embeds
59
Actions
Shares
0
Downloads
9
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Perl logging

  1. 1. Perl ロギング<br />print から Log::Dispatch の拡張まで<br />2010/08/07 Hokkaido.pm #1<br />By keroyon@cpan.org<br />Hokkaido.pm #1<br />
  2. 2. 概略<br />ログのあれこれについて語ります。<br />対象は Perl 入門者です。※ 後半は初級者向けかも<br />何でログなのか<br />必須知識の割には、あまり言及されないみたいなので。<br />※ 定番サイト、定番本、グーグルで全滅<br />ログ吐く方法すら分からないとあっては、Perl採用への道が遠退きそうなので。<br />内容<br />ログ出力のためのPerlの基礎知識<br />モジュールを使ったログ出力<br />業務要件に合わせたモジュールの拡張<br />Hokkaido.pm #1<br />
  3. 3. Perlロギング その1“面倒な”前提知識を再確認<br />Hokkaido.pm #1<br />モジュールを使わないでやってみる<br />
  4. 4. debug レベル[変数の確認とか]<br />Hokkaido.pm #1<br />
  5. 5. プリント文デバッグは永劫不滅です<br />use utf8;<br />BEGIN { binmodeSTDOUT, ':utf8'; }<br />my $hoge = 'hoge';<br />print "hogeは'${hoge}'です ";<br />↓<br />hogeは'hoge'です<br />※デバッガは lvalueに関するバグがあるよ!<br />Hokkaido.pm #1<br />
  6. 6. Data::Dumper はちょっと不便<br />use utf8;<br />use Data::Dumper;<br />BEGIN { binmode STDOUT, ':utf8'; }<br />my $hoge = { key => '値' };<br />print Dumper($hoge);<br />↓<br />$VAR1 = {<br /> 'key' => "x{5024}"<br />};<br />Hokkaido.pm #1<br />
  7. 7. YAML::Syck だといい感じ<br />use utf8;<br />use YAML::Syck;<br />use feature qw(say);<br />BEGIN { <br />$YAML::Syck::ImplicitUnicode = 1;<br />binmode STDOUT, ':utf8';<br />}<br />say Dump($hoge);<br />↓<br />---<br />key: 値<br />※YAML も 0.71でUTF8文字化けしなくなったよ!<br />Hokkaido.pm #1<br />perlcriticrc<br />[Variables::ProhibitPackageVars]<br />add_packages = YAML::Syck<br />
  8. 8. info レベル[進捗の確認とか]<br />Hokkaido.pm #1<br />
  9. 9. 処理の終了と開始とか<br />say '[info] 処理A開始 ' <br /> . Time::Piece::localtime->cdate;<br /> $ ./hoge.pl >> ~/info.log<br />$ cat ~/info.log<br />↓<br />[info] 処理A開始 Sun Aug 1 13:29:31 2010<br />※5.10 から Time::Piece がコア入りしたよ!<br />Hokkaido.pm #1<br />
  10. 10. warning レベル[不穏な動きを警告]<br />Hokkaido.pm #1<br />
  11. 11. STDERR warn carp<br />use utf8;<br />BEGIN { binmode STDERR, ':utf8'; }<br />say STDERR'なんかエラーだYO! ';<br />warn 'なんかエラーだYO! '; # 行番号<br />carp 'なんかエラーだYO! '; # スタックトレース<br />$ ./hoge.pl 2>>warn.log >>debug.log<br />$ ./hoge.pl >>all.log 2>&1<br />※ うるさい時には、$Carp::Internal{'パッケージ名'}++<br />Hokkaido.pm #1<br />
  12. 12. error/critical レベル[断末魔の処理]<br />Hokkaido.pm #1<br />
  13. 13. exit die croak<br />exit 1; <br />erronoを直指定。何を言ってるのか分かりづらい<br />ジョブネット組む時に使ったりする<br />die "うぎゃぁ~。銃でやられた! ";<br />原因が分かりやすい。単に die すると 255 で固定<br />croak "やられた!かくかくしかじか"; <br />死んだ経緯をしゃべる。スタックトレースつき<br />Hokkaido.pm #1<br />
  14. 14. 終了ステータスの変更:$SIG{__DIE__}<br />捕捉してフックする<br />$SIG{__DIE__} = sub {<br /> my $error = shift;<br /> if ($error =~ m{^hello}xms) {<br />$! = 100;<br /> }<br /> die $error;<br />};<br />Hokkaido.pm #1<br />
  15. 15. 終了ステータスの変更:$ERRNO<br />$! = 100;<br />die '死亡…';<br />use Englishqw(-no_match_vars);<br />$ERRNO = 100;<br />die '死亡…';<br />※-no_match_varsをつけないと Damian 先生に怒られるよ!<br />Hokkaido.pm #1<br />
  16. 16. Perlロギング その2Log::Dispatch::* の利用<br />Hokkaido.pm #1<br />
  17. 17. Log::Dispatch<br />Hokkaido.pm #1<br />
  18. 18. Log::Distachとは<br />ログの出力先を、標準エラー/ファイル/メール/DB/Syslog/Twitterなどに自在に切り替えられる<br />簡単に拡張ができるように設計されている<br /> ↓<br />拡張モジュールが豊富<br />$cpanpm ^Log::Dispatch::<br />50 個以上ヒット!<br />miyagawa氏の名前とかもある<br />Hokkaido.pm #1<br />
  19. 19. 簡単な例:とりあえずファイルに<br />use Log::Dispatch;<br />use Log::Dispatch::File;<br />my $log = Log::Dispatch->new;<br />$log->add(<br />Log::Dispatch::File->new(<br /> name => 'file_debug',<br />min_level => 'debug',<br />max_level => 'debug',<br /> filename => 'debug.log',<br /> mode => 'append',<br /> newline => 1,<br /> )<br />);<br />$log->debug('[debug] デバッグ情報 [' . Time::Piece::localtime->cdate . ']');<br />Log::Dispatch::Outputのサブクラスを指定<br />Hokkaido.pm #1<br />
  20. 20. 簡単な例:標準エラーに追加<br />$log->add(<br />Log::Dispatch::Screen->new(<br /> name => 'screen',<br />min_level => 'debug',<br /> newline => 1,<br />stderr => 1,<br /> )<br />);<br />$log->debug('[debug] デバッグ情報 [' . Time::Piece::localtime->cdate . ']');<br />L::D::Screen を継承した<br />L::D::Screen::Color みたいなものもある<br />Hokkaido.pm #1<br />
  21. 21. Log::Dispatch::Config<br />Hokkaido.pm #1<br />
  22. 22. Log::Dispatch::Configとは<br />Log::Dispatch のサブクラス<br />(miyagawaプロダクト)<br />ログ設定を別ファイルに分離できる<br />出力書式を自由に設定できる<br />$log->debug('[debug] デバッグ情報 [' .<br />Time::Piece::localtime->cdate . ']');<br />↓ シンプル!<br />[%p] %m [%d]<br />$log->debug('デバッグ情報')<br />設定ファイルの形式は、YAML、INIファイルなどから選べる<br />Hokkaido.pm #1<br />
  23. 23. コード部分<br />初期化は、new -> add でなく、configure -> instance<br />use utf8;<br />use Log::Dispatch::Config;<br />use Log::Dispatch::Configurator::YAML;<br />BEGIN { binmode STDERR, ':utf8'; }<br />my $log_config=<br />Log::Dispatch::Configurator::YAML->new('./log_conf.yaml');<br />Log::Dispatch::Config->configure($log_config);<br />my $log = Log::Dispatch::Config->instance;#new じゃない<br />$log->debug('デバッグ情報');<br />$log->info('インフォ。');<br />Hokkaido.pm #1<br />
  24. 24. 設定ファイル(YAMLの例)<br />screen_color:<br /> class: Log::Dispatch::Screen::Color<br />min_level: debug<br /> newline: 1<br /> color:<br /> info:<br /> text: green<br /> warning:<br /> text: yellow<br /> error:<br />text: red<br />dispatchers:<br /> - file_debug<br /> - screen_color<br />file_debug:<br /> class: Log::Dispatch::File<br />min_level: debug<br />max_level: debug<br /> filename: debug.log<br /> mode: append<br /> newline: 1<br /> format: [%d] [%p] %m at %F:%L<br />サブクラスの追加属性<br />Hokkaido.pm #1<br />
  25. 25. Log::WarnDie<br />Hokkaido.pm #1<br />
  26. 26. Log::WarnDieとは<br />昔は、Log::Dispatch::WarnDieという名前だった<br />warn|die|STDERRを Log::Dispatch、Log::Log4Perl に統合する<br />warn、carp、cluck を warning レベルに自動出力<br />die、croak、confess を error レベルに自動出力<br />use Log::WarnDie $log; とするだけで有効になり、既存コードの変更は不要<br />Hokkaido.pm #1<br />
  27. 27. 簡単な例<br />use Log::Dispatch;<br />use Log::Dispatch::File;<br />use Log::WarnDie; # 一回 use しといて<br />my $log = Log::Dispatch->new;<br />$log->add( Log::Dispatch::Screen->new(<br /> name => 'screen',<br />min_level => 'debug',<br />));<br />use Log::WarnDie$log; # インスタンスを与えてもう一回 use<br />warn 'warning!';<br />die 'died…';<br />Hokkaido.pm #1<br />
  28. 28. Perlロギング その3Log::Dispatch::* の拡張<br />Hokkaido.pm #1<br />
  29. 29. Log::Dispatch::* の拡張 #1Log::Dispatch::Email:*を拡張してみよう<br />Hokkaido.pm #1<br />
  30. 30. Log::Dispatch::Email とは<br />L::D::Output のサブクラス。さらにその Email を継承したクラスが複数あり、ログをメールに出力してくれる。<br />サブクラス達(多過ぎ!)<br />Log::Dispatch::Email::EmailSend<br />Log::Dispatch::Email::MIMELite<br />Log::Dispatch::Email::MailSend<br />Log::Dispatch::Email::MailSender<br />Log::Dispatch::Email::MailSendmail<br />欠点<br />モダンじゃない<br />日本語メールが文字化けして嫌な目にあう<br />じゃあ、モダンで文字化けしないのを作ってしまおう!<br />Hokkaido.pm #1<br />
  31. 31. Log::Dispatch::Email::EmailSender<br />モダンなメール送信クラス Email::EmailSenderを利用してメールを送る<br />エンコードを指定できるので文字化けしない<br />Email::EmailSender::Transport::Sendmailの他に、Email::EmailSender::Transport::SMTP にも対応させる<br />L::D::Email を継承して、send_emailメソッドをオーバーライドすれば完了<br />Hokkaido.pm #1<br />
  32. 32. コンストラクタ:%p -> $self へ<br />sub new {<br />    my $proto = shift;<br />    my $class = ref $proto || $proto;<br />    my %p = @_;<br />    my $self = $class->SUPER::new(%p);<br />    $self->{use_transport_smtp} = delete $p{use_transport_smtp};<br />    $self->{host} = delete $p{host};<br />    $self->{port} = delete $p{port};<br />    $self->{ssl} = delete $p{ssl};<br />    $self->{sasl_username} = delete $p{sasl_username};<br />    $self->{sasl_password} = delete $p{sasl_password};<br />    return $self;<br />}<br />※ 勝手に拡張したパラメータは、delete で即座に消すのが流儀らしい<br />Hokkaido.pm #1<br />
  33. 33. send_emailメソッド<br />sub send_email {<br />    my $self = shift;<br />    my %p = @_;<br />    my $to_str = join ',', @{ $self->{to} };<br />    my $email = _create_email({<br />            from => $self->{from},  to => $to_str,  subject => $self->{subject},<br />            header_encode => $self->{header_encode} || 'MIME-Header-ISO_2022_JP',<br />            body_encode => $self->{body_encode} || 'iso-2022-jp',<br />            body => $p{message},<br />    });<br />… 省略 …<br />    sendmail( $email, $transport, { to => $self->{to} } );<br />}<br />※$self からパラメータを受け取り、実際にメールを送信するコードを記述します。<br />例外処理とTransport::SMTPは省略してます<br />Hokkaido.pm #1<br />
  34. 34. _create_emailメソッド<br />sub _create_email {<br /> my $opt = shift;<br /> my $enc1 = $opt->{header_encode};<br /> my $enc2 = $opt->{body_encode};<br /> my $email = Email::MIME->create(<br /> header => [<br /> From => encode( $enc1, $opt->{from} ),<br /> To => encode( $enc1, $opt->{to} ),<br /> Subject => encode( $enc1, $opt->{subject} ),<br /> ],<br /> attributes => {<br />content_type => 'text/plain',<br />charset => $enc2,<br /> encoding => '7bit',<br /> },<br /> body => encode( $enc2, $opt->{body} ),<br /> );<br /> return $email;<br />}<br />Email::MIME->create して、return してるだけ。<br />Hokkaido.pm #1<br />
  35. 35. 使い方:Sendmail<br />use Log::Dispatch;<br />use Log::Dispatch::Email::EmailSender;<br />my $log =Log::Dispatch->new;<br />$log->add(<br /> Log::Dispatch::Email::EmailSender->new(<br />min_level => 'emerg',<br />from => 'logger@example.com',<br />to => [ qw( foo@example.com bar@example.org ) ],<br />subject => 'Big error!',<br />header_encode => 'MIME-Header-ISO_2022_JP',<br />body_encode => 'iso-2022-jp‘<br /> )<br />);<br />$log->emerg("Something bad is happening");<br />Hokkaido.pm #1<br />
  36. 36. 使い方:SMTP<br />Log::Dispatch::Email::EmailSender->new(<br />min_level => 'emerg',<br />from => 'logger@example.com',<br />to => [ qw( foo@example.com bar@example.org ) ],<br />subject => 'Big error!',<br />header_encode => 'MIME-Header-ISO_2022_JP',<br />body_encode => 'iso-2022-jp‘,<br />use_transport_smtp => 1,<br />host => [your smtp host],<br /> port => [your smtp port number],<br />sasl_username => [your username],<br />sasl_password => [your password],<br />)<br />Hokkaido.pm #1<br />
  37. 37. 補足など<br />インストール<br />$ cpanf –m Log::Dispatch::Email::EmailSender<br />$cpanf –mdi Log::Dispatch::Email::EmailSender<br />欠点<br />SSL オプションを使えない<br />(gmailアカウント使えない)<br />メール送信エラー時のリトライがない<br />他のEmail::Sender::Transport::* に未対応<br />添付ファイルとかHTMLメールとか送れない<br />バグ報告、パッチ、要望など<br />http://github.com/keroyonn/p5-Log-Dispatch-Email-EmailSender<br />http://rt.cpan.org/<br />keroyon@cpan.org<br />Hokkaido.pm #1<br />
  38. 38. Log::Dispatch::* の拡張 #2Log::Dispatch::Configを拡張してみよう<br />Hokkaido.pm #1<br />
  39. 39. こんなことありませんか?<br />処理速度の傾向を把握したい<br />バッチ処理で、目標とする処理時間がある<br />ランダムなデータでは目標をクリアしているが、実データで実際どうなるか分からない<br />サービスイン当初はいいが、データ増大と共に重くなるため処理時間の監視が必要<br />プロファイルの前に原因箇所にあたりをつけておきたい<br />捕捉し切れていない例外が総合テストで明らかになって、ふと大丈夫か不安になる<br />Perl の警告とプログラムの警告ログが別管理されていて、うっかり片方のチェックを忘れてしまう<br />Hokkaido.pm #1<br />
  40. 40. Log::Dispatch::Config::Watcher<br />特徴<br />関数の実行状態(時間、成否)を監視して、自動でログを出力する。特定の条件で警告ログを追加出力できる<br />フォーマットが拡張されていて、ナノセカンド単位の時間を表示したりできる<br />ひとつのアウトプット内で、条件に応じてフォーマットを切り替え可能。<br />warn と die を監視して、別フォーマットでログ出力できる。<br />欠点<br />Coro なら大丈夫だけど、AnyEventには対応していない。<br />重いかも。いや重い。<br />Hokkaido.pm #1<br />
  41. 41. 利用例:コード部分 #1<br />use Log::Dispatch::Config::Watcher;<br />use Log::Dispatch::Configurator::YAML;<br />my $config = Log::Dispatch::Configurator::YAML->new('/path/to/log.yaml');<br />Log::Dispatch::Config::Watcher->configure($config);<br />my $log = Log::Dispatch::Config::Watcher->instance;<br />$log->info('log message');<br />test1();<br />test2();<br />test3();<br />Hokkaido.pm #1<br />
  42. 42. 利用例:コード部分 #2(つづき)<br />sub test1 { <br /> $log->warn('sample warning.');<br /> sleep 4;<br />}<br />#evalされた die<br />sub test2 { <br />eval { die 'die with expected exception'; };<br /> warn $@ if $@;<br />}<br /># evalされてない die<br />sub test3 { <br /> sleep 5;<br /> die 'die with unexpected exception.';<br />}<br />Hokkaido.pm #1<br />
  43. 43. 利用例:設定ファイル #1<br />dispatchers:<br /> - screen<br />screen:<br /> class: Log::Dispatch::Screen<br />min_level: debug<br /> format: '[%p] %F:%L %m [%D{%F %T.%6N%z}]' <br /># DateTime::Format::Strptime<br /> newline: 1<br />stderr: 1<br />Hokkaido.pm #1<br />
  44. 44. 利用例:設定ファイル #2(つづき)<br />watch:<br />watch_die: 1<br />watch_only_unexpected_die: 1<br />watch_warn: 1<br />die_level: critical<br />warn_level: warning<br /> formats:<br /> start: '[%p] START:%j [%D{%F %T.%6N%z}]'<br /> end: '[%p] END:%j(%t) [%D{%F %T.%6N%z}]'<br />end_with_die: '[%p] ABNORMALLY_ENDED:%j(%e) [%D{%F %T.%6N%z}]'<br /> die: '[%p] UNEXPECTED_DIE (%e) [%D{%F %T.%6N%z}]'<br /> warn: '[%p] PERL_WARNING (%e) [%D{%F %T.%6N%z}]'<br />timeover: '[%p] TIMEOVER:%j(%t)‘<br />%j … 関数の詳細説明<br />%t … 関数の実行時間<br />%e … 警告メッセージ<br />Hokkaido.pm #1<br />
  45. 45. 利用例:設定ファイル #3(つづき)<br />watch:<br />~#2 参照(略)~<br />functions:<br />main::test1# パッケージ名に注意<br /> description: sleep 4 seconds process<br />warn_duration: 5.0 # 秒数を指定<br />notice_duration: 3.5<br />main::test3:<br /> description: sleep 5 seconds process<br />warn_duration: 4.0<br />notice_duration: 2.5<br />Hokkaido.pm #1<br />
  46. 46. 実行結果<br />素の $log->info()<br />[info] ./log.pl:13 log message [2010-07-30 15:34:16.316215+0900]<br />監視対象: test1() warn -> sleep 4;<br />[info] START:"sleep 4 seconds process" [2010-07-30 15:34:16.319123+0900]<br />[warn] ./log.pl:19 sample warning. [2010-07-30 15:34:16.320854+0900]<br />[notice] TIMEOVER:"sleep 4 seconds process"(=>00:00:04.005179167)<br />[info] END:"sleep 4 seconds process"(=>00:00:04.005179167) [2010-07-30 15:34:20.324692+0900]<br />非監視対象: test2() evalされた die<br />[warning] PERL_WARNING (die with expected exception at ./log.pl line 25.)<br />監視対象:test3() evalされない die<br />[info] START:"sleep 5 seconds process" [2010-07-30 15:34:25.328754+0900]<br />[warning] TIMEOVER:"sleep 5 seconds process"(=>00:00:05.00351812)<br />[critical] ABNORMALLY_ENDED:"sleep 5 seconds process"(die with unexpected exception. at ./log.pl line 30.) [2010-07-30 15:34:25.330754+0900]<br />Hokkaido.pm #1<br />
  47. 47. 拡張方法 #1 フック<br />create_instanceのオーバーライド<br />インスタンス生成時、設定ファイルの更新時に実行されるメソッド<br />Hook::LexWrapを利用してメソッドの起動と終了をフックする<br />$SIG{__DIE__}、$SIG{__WARN__} で die、warn をフックする<br />同一Outputクラス内でのフォーマット書き換えを実現するため、フォーマットのバックアップ->出力->フォーマットのリシュームを行う<br />Hokkaido.pm #1<br />
  48. 48. 拡張方法 #2 拡張書式<br />format_to_cbのオーバーライド<br />ログメッセージの生成時に実行されるメソッド<br />DateTime、DateTime::HiRes、DateTime::Format::Strptimeなどを利用して複雑な時刻書式に対応させる<br />正規表現でパースしているので、その部分を拡張して、拡張フォーマットに対応させる<br />ソースをコピーして上書きするという美しくない拡張<br />Hokkaido.pm #1<br />
  49. 49. 補足など<br />インストール<br />$ cpanf –m Log::Dispatch::Config::Watcher<br />$cpanf –mdi Log::Dispatch::Config::Watcher<br />欠点<br />main パッケージのメソッドにも main:: が必要<br />拡張方法が美しくない<br />$SIG{__WARN__} 書き換えによる不具合とか起こりそう<br />(たぶん)重い<br />バグ報告、パッチ、要望など<br />http://github.com/keroyonn/p5-Log-Dispatch-Config-Watcher<br />http://rt.cpan.org/<br />keroyon@cpan.org<br />Hokkaido.pm #1<br />
  50. 50. まとめ<br />Perl は意外に約束事が多い<br />PerlMongerじゃない人には丁寧に教えてあげよう<br />CPANには良いログモジュールがある<br />プロジェクト単位でログモジュールを自作してしまう前に、ちゃんと検討しよう<br />特別な事情がなければ、汎用性のあるCPANモジュールを使おう<br />CPANのモジュールは拡張性が高い<br />業務要件を満たせない場合には、薄いラッパーを作ってみるのもいいけど、モジュールの拡張にもチャレンジしてみよう<br />Hokkaido.pm #1<br />
  51. 51. ご清聴ありがとうございました<br />Hokkaido.pm #1<br />

×