スキップしてメイン コンテンツに移動

【PHP】CodeIgniterのログをクラス名やメソッド名や行番号とかも表示してみる

環境

CodeIgniter:Version 2.1.0

前置き

表題にCodeIgniterと書きましたが、概念的にはこのフレームワークだからという話ではないです。前回の続きと言うことで。

デフォルトでログは以下のように吐かれます。

DEBUG - 2012-02-18 23:30:43 --> Controller Class Initialized
INFO - 2012-02-18 23:30:43 --> start
INFO - 2012-02-18 23:30:43 --> valid id:hogehoge
INFO - 2012-02-18 23:30:43 --> end


まあ、ざっくりこのログが何を表しているかというと、土曜の夜だというのにデバッグしてるMVCモデルのControllerの部分に要求がきて、その中のメソッドを開始し、idの値判定して結果をログへ出力。正常にメソッドを抜けてendといった感じを表しています。



自分で作り込んでいれば、上記では割愛した前後のログでおよその見当は付くでしょうが、ぱっと見どこの処理かわからないですね。



これにクラス名やメソッド名、行番号が表示されていれば、前後を確認しなくとも1発でどこの処理か判断する事ができるのではないでしょうか。





クラス名やメソッド名、行番号などの情報を取得する方法



PHPには便利な関数があるようで、




・PHP: debug_backtrace - Manual
http://www.php.net/manual/ja/function.debug-backtrace.php




この関数を叩けば、呼び出し元のクラス名やら行番号が参照できます。という事で、前回社畜仕様にカスタマイズしたファイルに埋め込めばOK。はい解決!と行きたいところですが、少し挙動を見てみましょう。



debug_backtrace関数の戻り値を表示させるには、



 var_dump(debug_backtrace());


という記述をしてやります。これを前回で言うところのMY_Log.phpのwrite_logメソッド内に記述し、



呼び出し元となる、テスト用のControllerを作成してやります。



<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Testcontroller extends CI_Controller {

public function index() {
log_message('debug', 'Hello world');
}
}




ブラウザから呼んで、1回の処理でどれだけ戻り値があるか実験してみます。



WS000424



こんな感じでたくさんの配列の中身が表示されます。これらをコピーしてテキストに保存してサイズを見ると、





WS000422





160KBなかなか大きいサイズじゃないですか。たかだかControllerからHello worldとログに出力するだけでこのサイズの戻り値の文字があるわけです。これが、何行もログを出力し、同時に複数のアクセスがある事を考えると、脇役のログが結構な負荷になるのではないでしょうか?そんなにアクセスないから安心しろ。 とは言え、160KBと言われても、戻り値の配列を文字で表示したサイズですし、PHPの中身がどうなっててメモリをどう使ってるのかわからないので”大きいサイズ”と言いましたが感覚的なものです。。。ブラウザに表示すると重くなるデータサイズはちと見逃せねーなーといった感じです。





パフォーマンス重視で言えば、以下の定数を1個1個Hello worldの部分へ埋め込めば良いでしょう。




・PHP: 自動的に定義される定数 - Manual
http://php.benscom.com/manual/ja/language.constants.predefined.php




しかし、捕らぬアクセスのレスポンス算用を今のうちにして、パフォーマンス重視の見づらいコードになるくらいなら、バグを埋め込まないためにもスッキリさせておきたいので、仲介の関数(ラッパー関数)を間に挟んでみます。



ロガーを仲介する関数を作成する



メソッドからロガーを呼ぶにあたり、以下のようなコードの見栄えでどこからでも直接関数を呼べるよう、



log_message('debug', 'Hello world');


CodeIgniterのヘルパー関数として実装します。




・ヘルパー関数 : CodeIgniter ユーザガイド 日本語版
http://codeigniter.jp/user_guide_ja/general/helpers.html




まず、ヘルパー関数を記述するファイルを作成します。ファイル名の末尾は「_helper.php」となるようにします。




  • application/helpers/mylogwrap_helper.php



<?php
function logger_debug($msg) {
print_r(debug_backtrace());
log_message('debug', $msg);
}
?>


まずは、上記のMY_Log.phpと比較できるよう、print_r(debug_backtrace());を実装しておきます。これをどこからでも呼べるよう、「application/config/autoload.php」に登録します。こちらでは、ファイル名の「_helper.php」は必要ありません。



$autoload['helper'] = array('mylogwrap');


これで先ほどのControllerでこの関数を呼ぶようにして実験をしてみます。



<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Testcontroller extends CI_Controller {

public function index() {
logger_debug('Hello world');
}
}


WS000423



約27KBで仲介させた関数の方が約1/6小さい!ということで明らかに効果がありますね。まあ、フレームワーク自体のログが出なくなる事もあり、当たり前ですが。できればByte単位にしたいところですが、コレでいきたいと思います。



で、これを表題の通りに表示させるべく、debug_backtrace関数の戻り値から必要な情報を取得し、関数を修正・整理してみたのが以下です。ただし、クラス名を表示させるので呼び出し元はクラス限定という縛りで。



<?php
function logger_info($msg) {
logger('info', $msg);
}
function logger_debug($msg) {
logger('debug', $msg);
}
function logger_error($msg) {
logger('error', $msg);
}

function logger($level, $msg) {
$arr = debug_backtrace();
// この関数が直接呼ばれた場合と、同ファイル内から呼ばれた場合でバックトレースの取得位置を分岐。
if(stristr($arr[0]['file'], 'mylogwrap_helper') === FALSE) {
// ヘルパーとして呼び出された場合
$filename = $arr[1]['class'] ."::".$arr[1]['function'];
$msg = $filename."[".$arr[0]['line']."] ".$msg;
}
else {
// 同ファイル内からの呼び出された場合
$filename = $arr[2]['class'] ."::".$arr[2]['function'];
$msg = $filename."[".$arr[1]['line']."] ".$msg;
}
// 本家ロガーへ (;_;)/~~~
log_message($level, $msg);
}
?>


パラメータとして記述していたdebugやerrorというレベル値は、その分対応する関数としました。これならNetBeansとかのIDEなら実行前にエラーだとわかるのではないでしょうか?試してないのでわかりませんが。そして、共通的に処理するloggerを設けてます。本当はloggerはプライベートな関数としたいところですが、そうもいかないので、直接呼ばれた場合と同ファイルから呼ばれた場合で分岐させましました。





これを先ほどのControllerから呼ぶと、



DEBUG - 2012-02-19 01:32:45 Controller Class Initialized
DEBUG - 2012-02-19 01:32:45 Testcontroller::index[6] Hello world


これで、どこのHello worldさんか一目瞭然ですね!

コメント

このブログの人気の投稿

GolangでWindows GUIアプリケーション

GUIアプリ作成の前提 社内ツールとしてexeで配布 開発環境はGoLandを使う 社内ツールとしてexeを配布ということであれば、Visual StudioでC#による開発だと思います。しかしながら、Go言語を習得したいのと、GoLandの補完機能が便利で、Android Studio使っていたこともあり、とっつきやすいという点からGo縛りでGUIアプリケーションを考えたいと思います。 lxn/walk Windows application library kit for Go. Windows向けしか考えていないので、まずこのライブラリなのですが、ボタンやコンボボックスが思ったように並ばなかったり、手軽にイメージボタンを配置したりなど、簡単にレイアウトを変更できない課題に直面しました。レイアウト作成だけで時間を取られてしまいます。そう言えば、Androidアプリ開発のときは、XMLでデザイン部分を切り離してたのを思い出して、今回の調査の運びとなりました。 fyne-io/fyne Cross platform native GUIs designed for Go based on Material Design. Supports: Linux, macOS, Windows, BSD, iOS and Android. walkと比べると、クロスプラットフォームで作成できるのですが、こちらもコードの中にデザインを書いていく形でした。 therecipe/qt therecipe/qt allows you to write Qt applications entirely in Go or JavaScript. Qt Creatorなるものがあるようで、デザイン部分を切り離せる印象はあるものの、最新のPCでもビルドに時間がかかるようで、学習コストもかかるという記事を見かけて断念しました。 どうしたものかとツイートしましたら、下記のようにご助言をいただいた。 同じ課題に対し色々試した結果、PWAのフルキャッシュドに落ち着いた。中身はGo+WASMで。 https://t.co/e60whDTV16 — のぼのぼ📡 (@nobonobo) July 21, 2020 PWA 早速調べてみました。Googleが進めているプロジェクトで、ネイティブアプリのよ

Javaでprivateなfieldやmethodにアクセスする

JUnitでテストしてると、privateなフィールドにアクセスして、値を参照したりセットしたりしたくなるわけですが、よく使うのでメモしておきます。 例えば、次のような対象のクラスがあるとします。 public class ParentClass { private String hoge = "ParentClass!!"; public void dispMsg() { System.out.println("dispMsg:" + hoge); } private void privateDispMsg(String msg) { System.out.println("dispMsg:" + msg); } }

Eclipseで一通り基本的なGitを使ってみる

備忘録的にまとめておきたいと思います。おかしな点があればご指摘いただければ幸いです。 目次 環境 プロジェクト作成~初コミットまで コミットの履歴&差分を見る 元に戻す ブランチについて ブランチを作成してみる ブランチを統合するには マージする Rebaseする ローカルリポジトリからプロジェクトを取り込む コミットしたメッセージの修正 環境 Eclipse IDE for Java Developers:Juno Service Release 1 Git:上記Eclipseのプリインストール版