前説
前回のCodeIgniter エラーログの出力は、評判が良かったので、続いて、CodeIgniter SQLログの出力についても紹介したいと思います。
CodeIgniter3.xにおいて、last_queryメソッドを使えば、直前に実行されたSQL文を返しますが、数十画面以上のWEBシステム開発において、各SQLを実行した直後に、いちいちlast_queryメソッドを呼んでログにはきだすのは非常にコストが掛かりまし、漏れる可能性も高いと思います。
なので、今回は、CodeIgniterフレームワークを改造し、各画面(機能)は、SQLログの出力を意識せず(コーディングは不要)、フレームワーク側で対応してくれるという方法を紹介します。
■CodeIgniterのlast_queryメソッドを利用してログに出力するサンプル
1 2 3 4 5 6 7 8 9 10 11 |
public function sample($id) { $this->db->flush_cache(); $query = $this->db->get_where($this->_table, array('id' => $id)); $ret = $query->row(); log_message('debug', "【SQL】".$this->db->last_query()); // SQLを実行したたびに、いちいちlast_queryメソッドを呼んでログに出力するのは面倒くさい!! } |
前提
・CodeIgniterバージョン:3.1.3
※バージョン:3.1.6でも使えますので、3.X系はいけそうではないかと思います。す。
要件
お客様からの要件は下記の通りです。
・システム運用時に、実行したSQL文、実行時間(ミリ秒まで)、消費時間(ミリ秒まで)をログに記録し、日単位で管理すること。→ログローテーションのこと
考え方
最初は、last_queryメソッド(場所:system\database\DB_driver.php)を改造すれば済むと思いましたが、フレームワークの実装を読んでたら、ムリと思って、このメソッドの使う$this->queries変数値の設定場所を探したら、ヒントがありました。
それは、同じクラス(DB_driver.php)のqueryメソッドです。queryメソッドは実際にSQL文を実行する場所なので、当然の話で、実行するSQL文は必ずこのメソッドにあるというわけです。
実装(queryメソッド)
queryメソッドをよく読んだら、SQL実行失敗と成功、2ケースがあり、当然ながら、ログに出力する際に、この2ケースを全部出力しなければならないですね。
まずは、SQL実行失敗時の実装です。場所は、DB_driver.phpの670行目あたりで(queryメソッド内)、下記のように、sql文と消費時間をこれから紹介するsql_logメソッドに渡します。
1 2 3 4 5 6 7 8 9 10 |
// Log errors log_message('error', 'Query error: '.$error['message'].' - Invalid query: '.$sql); // ここまではフレームワークの実装 //エラーを起こすSQL文も出力 $this->sql_log( $sql, $time_start, microtime(TRUE) ); //この行を追加 |
簡単なので、説明を割愛し、SQL実行成功時の実装に進めます。場所は、DB_driver.phpの706行目あたりで(queryメソッド内)、失敗時と同じように、sql文と消費時間をsql_logメソッドに渡します。
1 2 3 4 5 6 7 8 9 10 11 12 |
if ($this->save_queries === TRUE) { $this->query_times[] = $time_end - $time_start; // ここまではフレームワークの実装 $this->sql_log( $sql, $time_start, $time_end ); //この行を追加 } |
実装(sql_logメソッド)
sql_logメソッドの実装方法は簡単です。詳しくは、PHP ログ出力機能 実装方法 で紹介しましたので、ご参考まで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//独自のsqlログを出力 private function sql_log($sql, $time_start, $time_end) { date_default_timezone_set('Asia/Tokyo'); $userid = ''; if ( isset($_SESSION['userid']) ){ $userid = $_SESSION['userid']; } $datatime = explode('.' , microtime(true)); if( !isset($datatime[1]) ){ $datatime[1] = "0000"; } $loginfo = 'INFO' . ' ' . date('Y/m/d H:i:s,') . $datatime[1] // ミリ秒まで、デッドロックに役立つ . ' ' . session_id() // セッションID . ' ' . $userid // ユーザーID . ' ' . ($time_end - $time_start) // SQL実行が掛かる時間、実行時間timeoutやパフォーマンスのチューニング時に役立つ . ' ' . $sql; $loginfo = mb_convert_encoding($loginfo,'UTF-8','UTF-8'); $filename = APPPATH . 'logs' . DIRECTORY_SEPARATOR . 'sql-' . date('Y-m-d') . '.log'; error_log( $loginfo . "\n", 3, $filename); } |
サンプル
実装jはここまで、実際に出力したSQLログのサンプルをあげます。
1 2 3 4 5 |
INFO 2019/06/27 09:08:03,28499 9vfeol00ljimpsltt89dja3gdr0c12e0 test-user 0.0002968311309814453 SELECT * FROM "sample_table" WHERE "id" = '123' |
まとめ
いかがでしょうか。意外と簡単にできましたね。CodeIgniter エラーログの出力方法も紹介しましたので、是非一度読んでください。
それでは、以上です。