1+ <?php
2+ /**
3+ * Created by PhpStorm.
4+ * User: zhanghy<[email protected] > 5+ * Date: 2019-11-04
6+ * Time: 23:49
7+ */
8+
9+ namespace Zanehy \SqlMonitor ;
10+
11+ use Illuminate \Database \Events \QueryExecuted ;
12+ use Illuminate \Http \Request ;
13+ use Illuminate \Support \Facades \Log ;
14+ use Illuminate \Support \ServiceProvider ;
15+
16+ class SqlMonitorServiceProvider extends ServiceProvider
17+ {
18+
19+ /**
20+ * The Laravel application instance.
21+ *
22+ * @var \Illuminate\Foundation\Application
23+ */
24+ public $ app ;
25+
26+
27+ /**
28+ * SqlMonitorServiceProvider constructor.
29+ * @param $app
30+ */
31+ public function __construct ($ app )
32+ {
33+ parent ::__construct ($ app );
34+ $ this ->app = app ();
35+ }
36+
37+
38+ /**
39+ * Register services.
40+ * @return void
41+ */
42+ public function register ()
43+ {
44+ //获取配置文件路径并合并配置
45+ $ configPath = __DIR__ . '/../config/monitor.php ' ;
46+ $ this ->mergeConfigFrom ($ configPath , 'monitor ' );
47+ }
48+
49+
50+ /**
51+ * 合并配置文件
52+ * @auther zhanghy<[email protected] > 53+ * @Date 2019-11-10
54+ * @param string $path
55+ * @param string $key
56+ */
57+ protected function mergeConfigFrom ($ path , $ key )
58+ {
59+ $ config = $ this ->app ['config ' ]->get ($ key , []);
60+ $ this ->app ['config ' ]->set ($ key , array_merge (require $ path , $ config ));
61+ }
62+
63+
64+ /**
65+ * 启动
66+ * @auther zhanghy<[email protected] > 67+ * @Date 2019-11-06
68+ */
69+ public function boot ()
70+ {
71+
72+ if (($ this ->app ['config ' ]->get ('monitor.app ' ) == "local " ) || $ this ->app ['config ' ]->get ('monitor.sql-monitor ' )) {
73+
74+ $ db = $ this ->app ['db ' ];
75+ $ db ->listen (
76+ function ($ query ) {
77+ if ( $ query instanceof \Illuminate \Database \Events \QueryExecuted ) {
78+
79+ //获取当前监听的文件和行数
80+ $ stacks = debug_backtrace (DEBUG_BACKTRACE_IGNORE_ARGS , 50 );
81+
82+ //打印监听日志
83+ $ this ->sqlMonitor ($ query , $ stacks );
84+ } else {
85+ Log::error ("sqlmonitorError: Not listening QueryExecuted " );
86+ }
87+ }
88+ );
89+ }
90+ }
91+
92+
93+
94+ /**
95+ * 监听
96+ * @auther zhanghy<[email protected] > 97+ * @Date 2019-11-05
98+ * @param QueryExecuted $event
99+ */
100+ private function sqlMonitor (QueryExecuted $ event , array $ stacks )
101+ {
102+ $ sql = str_replace ("? " , "'%s' " , $ event ->sql );
103+ $ sql = vsprintf ($ sql , $ event ->bindings );
104+ $ urlPath = Request::capture ()->path ();
105+
106+ //检索执行的文件和行数
107+ $ res = [];
108+ foreach ($ stacks as $ stack ) {
109+ if (isset ($ stack ['class ' ]) && isset ($ stack ['file ' ]) && !$ this ->fileIsInExcludedPath ($ stack ['file ' ])) {
110+ $ stack ['file ' ] = $ this ->normalizeFilename ($ stack ['file ' ]);
111+ $ res = $ stack ;
112+ break ;
113+ }
114+ }
115+
116+ //记录日志
117+ Log::info (json_encode ([
118+ 'action ' => 'sqlmonitor: ' . $ urlPath ,
119+ 'path ' => $ urlPath ,
120+ 'sql ' => $ sql ,
121+ 'time ' => $ event ->time . 'ms ' ,
122+ 'file ' => ($ res ['file ' ] ?? '' ),
123+ 'line ' => ($ res ['line ' ] ?? '' )
124+ ]));
125+ }
126+
127+ /**
128+ * 检查给定文件是否要从分析中排除
129+ * @auther zhanghy<[email protected] > 130+ * @Date 2019-11-05
131+ * @param $file
132+ * @return bool
133+ */
134+ private function fileIsInExcludedPath ($ file )
135+ {
136+ $ excludedPaths = [
137+ '/vendor/laravel/framework/src/Illuminate/Database ' ,
138+ '/vendor/laravel/framework/src/Illuminate/Events ' ,
139+ '/vendor/barryvdh/laravel-debugbar ' ,
140+ ];
141+
142+ $ normalizedPath = str_replace ('\\' , '/ ' , $ file );
143+ foreach ($ excludedPaths as $ excludedPath ) {
144+ if (strpos ($ normalizedPath , $ excludedPath ) !== false ) {
145+ return true ;
146+ }
147+ }
148+ return false ;
149+ }
150+
151+ /**
152+ * 通过删除相对链接和基本目录来缩短路径
153+ * @auther zhanghy<[email protected] > 154+ * @Date 2019-11-05
155+ * @param $path
156+ * @return mixed
157+ */
158+ private function normalizeFilename ($ path )
159+ {
160+ if (file_exists ($ path )) {
161+ $ path = realpath ($ path );
162+ }
163+ return str_replace (base_path (), '' , $ path );
164+ }
165+
166+
167+
168+ }
0 commit comments