之前 Lumen
框架从 5.6
升级到 5.7
。发现 laravel-sql-logger
包不能正常记录日志了。进行排查,发现是 Lumen
框架没有对 DB
类型注入 event
对象,导致不能正常对其进行SQL监听。
那么解决方案也非常简单。
1 2 3 $app ["db" ]->connection ()->setEventDispatcher ($app ["events" ]); $app ->register (Mnabialek\LaravelSqlLogger\Providers\ServiceProvider ::class );
但是这也让我对如何实现SQL记录产生了兴趣。接下来,我们就具体了解一下如何实现SQL监听。
我们知道在Larvel
上非常简单。只需要如下方法即可对其进行SQL监听:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 namespace App \Providers ;use Illuminate \Support \Facades \DB ;use Illuminate \Support \ServiceProvider ;class AppServiceProvider extends ServiceProvider { public function boot ( ) { DB::listen (function ($query ) { }); } }
但是在 Lumen
上这种办法是没有办法使用的。Lumen
有一些自己的调试SQL的方法,但是这些并不是我们想要的。所以我们只能自己写监听事件。
具体的解决方案是,我们首先创建一个Listener文件。
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 namespace App \Listeners ;use Illuminate \Database \Events \QueryExecuted ;class QueryListener { public function __construct ( ) { } public function handle (QueryExecuted $event ) { dd ($event ); } }
接下来我们直接对其进行注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 namespace App \Providers ;use App \Listeners \QueryListener ;use Illuminate \Database \Events \QueryExecuted ;use Laravel \Lumen \Providers \EventServiceProvider as ServiceProvider ;class EventServiceProvider extends ServiceProvider { protected $listen = [ QueryExecuted ::class => [ QueryListener ::class , ], ]; }
别忘了注册 Service Providers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $app ->register (App\Providers\EventServiceProvider ::class );
接下来就是实操了。首先我们创建一个控制器
1 2 3 4 5 6 7 8 9 10 11 namespace App \Http \Controllers ;use App \User ;class UserController extends Controller { public function one ( ) { return User ::where ("id" , 1 )->first (); } }
注册路由
1 2 $router ->get ('/one' , "UserController@one" );
最后别忘了开启DB功能以及填写数据库配置(这一段大家肯定都会,我就不贴代码了)。
那么我们来尝试运行一下:
1 2 3 4 5 6 7 QueryExecuted { +sql: "select * from `users` where `id` = ? limit 1" +bindings: array :1 [▶] +time: 2.06 +connection: MySqlConnection { +connectionName: "mysql" }
发现已经成功了。那么就可以执行日志记录了。
我们修改一下代码:
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 32 33 namespace App \Listeners ;use Illuminate \Database \Events \QueryExecuted ;use Illuminate \Support \Facades \Log ;class QueryListener { public function __construct ( ) { } public function handle (QueryExecuted $event ) { $query = $event ->sql; foreach ($event ->bindings as $bind ) { $query = preg_replace ('/\?/' , (is_numeric ($bind ) ? $bind : '\'' . $bind . '\'' ), $query , 1 ); } Log ::info ("query: {$query} time: {$event->time} ms" ); } }
虽然已经实现了SQL记录,但是这并不是我们想要的,因为将SQL和错误日志放在一起。阅读起来非常难受。那么我就需要放到一个单独的文件里面去就好了。
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 32 33 34 35 36 37 38 39 40 41 namespace App \Listeners ;use Illuminate \Database \Events \QueryExecuted ;use Illuminate \Http \Request ;class QueryListener { protected $writeFile ; public function __construct ( ) { $this ->writeFile = storage_path () . DIRECTORY_SEPARATOR . "logs" . DIRECTORY_SEPARATOR . "sql-" . date ("Ymd" ) . ".log" ; } public function handle (QueryExecuted $event ) { $query = $event ->sql; foreach ($event ->bindings as $bind ) { $query = preg_replace ('/\?/' , (is_numeric ($bind ) ? $bind : '\'' . $bind . '\'' ), $query , 1 ); } file_put_contents ($this ->writeFile, "query: {$query} time: {$event->time} ms" , FILE_APPEND); } }
接下来继续改进,就是说我们目前是在所有的环境中进行打印的,但是我们仅仅需要在开发环境进行调试。所以我们可以进行如下修改:
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 namespace App \Providers ;use App \Listeners \QueryListener ;use Illuminate \Database \Events \QueryExecuted ;use Laravel \Lumen \Providers \EventServiceProvider as ServiceProvider ;class EventServiceProvider extends ServiceProvider { protected $listen = []; public function register ( ) { if (env ("APP_ENV" ) == "local" ) { $events = app ('events' ); $events ->listen (QueryExecuted ::class , QueryListener ::class ); } } }
那么SQL监听功能就实现了。
其实 laravel-sql-logger
还有一些高级的显示功能。比如说打印日志的时候会顺带着打印请求URL。打印请求时间等。这些我就不这里具体完善了。如果大家有兴趣,可以自己想办法实现。很简单的。