1
|
|
|
<?php namespace Understand\UnderstandLaravel5; |
2
|
|
|
|
3
|
|
|
use Illuminate\Support\ServiceProvider; |
4
|
|
|
use Illuminate\Foundation\AliasLoader; |
5
|
|
|
use Illuminate\Support\Str; |
6
|
|
|
use Illuminate\Foundation\Application; |
7
|
|
|
use Exception; |
8
|
|
|
use Throwable; |
9
|
|
|
|
10
|
|
|
class UnderstandLaravel5ServiceProvider extends ServiceProvider |
11
|
|
|
{ |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Indicates if loading of the provider is deferred. |
15
|
|
|
* |
16
|
|
|
* @var bool |
17
|
|
|
*/ |
18
|
|
|
protected $defer = false; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Bootstrap the application events. |
22
|
|
|
* |
23
|
|
|
* @return void |
24
|
|
|
*/ |
25
|
|
|
public function boot() |
26
|
|
|
{ |
27
|
|
|
$configPath = __DIR__ . '/../../config/understand-laravel.php'; |
28
|
|
|
$this->publishes([$configPath => config_path('understand-laravel.php')], 'config'); |
29
|
|
|
$enabled = $this->app['config']->get('understand-laravel.enabled'); |
30
|
|
|
|
31
|
|
|
if ($enabled) |
32
|
|
|
{ |
33
|
|
|
$this->listenLaravelEvents(); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
if ($enabled && $this->app['config']->get('understand-laravel.sql_enabled')) |
37
|
|
|
{ |
38
|
|
|
$this->listenQueryEvents(); |
39
|
|
|
} |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Register the service provider. |
44
|
|
|
* |
45
|
|
|
* @return void |
46
|
|
|
*/ |
47
|
|
|
public function register() |
48
|
|
|
{ |
49
|
|
|
$this->registerConfig(); |
50
|
|
|
$this->registerFieldProvider(); |
51
|
|
|
$this->registerDataCollector(); |
52
|
|
|
$this->registerTokenProvider(); |
53
|
|
|
$this->registerLogger(); |
54
|
|
|
$this->registerExceptionEncoder(); |
55
|
|
|
$this->registerEventLoggers(); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Register config |
60
|
|
|
* |
61
|
|
|
* @return void |
62
|
|
|
*/ |
63
|
|
|
protected function registerConfig() |
64
|
|
|
{ |
65
|
|
|
$configPath = __DIR__ . '/../../config/understand-laravel.php'; |
66
|
|
|
$this->mergeConfigFrom($configPath, 'understand-laravel'); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Register field provider |
71
|
|
|
* |
72
|
|
|
* @return void |
73
|
|
|
*/ |
74
|
|
|
protected function registerFieldProvider() |
75
|
|
|
{ |
76
|
|
|
$this->app->bind('understand.fieldProvider', function($app) |
77
|
|
|
{ |
78
|
|
|
$fieldProvider = new FieldProvider(); |
79
|
|
|
|
80
|
|
|
if ($app['config']['session.driver']) |
81
|
|
|
{ |
82
|
|
|
$fieldProvider->setSessionStore($app['session.store']); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
$fieldProvider->setRouter($app['router']); |
86
|
|
|
$fieldProvider->setRequest($app['request']); |
87
|
|
|
$fieldProvider->setEnvironment($app->environment()); |
88
|
|
|
$fieldProvider->setTokenProvider($app['understand.tokenProvider']); |
89
|
|
|
$fieldProvider->setDataCollector($app['understand.dataCollector']); |
90
|
|
|
$fieldProvider->setApp($app); |
91
|
|
|
|
92
|
|
|
return $fieldProvider; |
93
|
|
|
}); |
94
|
|
|
|
95
|
|
|
$this->app->booting(function() |
96
|
|
|
{ |
97
|
|
|
$loader = AliasLoader::getInstance(); |
98
|
|
|
$loader->alias('UnderstandFieldProvider', 'Understand\UnderstandLaravel5\Facades\UnderstandFieldProvider'); |
99
|
|
|
}); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Register token generator class |
104
|
|
|
* |
105
|
|
|
* @return void |
106
|
|
|
*/ |
107
|
|
|
protected function registerTokenProvider() |
108
|
|
|
{ |
109
|
|
|
$this->app->singleton('understand.tokenProvider', function() |
110
|
|
|
{ |
111
|
|
|
return new TokenProvider(); |
112
|
|
|
}); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Register data collector class |
117
|
|
|
* |
118
|
|
|
* @return void |
119
|
|
|
*/ |
120
|
|
|
protected function registerDataCollector() |
121
|
|
|
{ |
122
|
|
|
$this->app->singleton('understand.dataCollector', function() |
123
|
|
|
{ |
124
|
|
|
return new DataCollector(); |
125
|
|
|
}); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Register exception encoder |
130
|
|
|
* |
131
|
|
|
* @return void |
132
|
|
|
*/ |
133
|
|
|
protected function registerExceptionEncoder() |
134
|
|
|
{ |
135
|
|
|
$this->app->bind('understand.exceptionEncoder', function() |
136
|
|
|
{ |
137
|
|
|
return new ExceptionEncoder; |
138
|
|
|
}); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Register exception and event logger |
143
|
|
|
* |
144
|
|
|
* @return void |
145
|
|
|
*/ |
146
|
|
|
protected function registerEventLoggers() |
147
|
|
|
{ |
148
|
|
|
$this->app->bind('understand.eventLogger', function($app) |
149
|
|
|
{ |
150
|
|
|
return new EventLogger($app['understand.logger'], $app['config']); |
151
|
|
|
}); |
152
|
|
|
|
153
|
|
|
$this->app->bind('understand.exceptionLogger', function($app) |
154
|
|
|
{ |
155
|
|
|
return new ExceptionLogger($app['understand.logger'], $app['understand.exceptionEncoder'], $app['config']); |
156
|
|
|
}); |
157
|
|
|
|
158
|
|
|
$this->app->booting(function() |
159
|
|
|
{ |
160
|
|
|
$loader = AliasLoader::getInstance(); |
161
|
|
|
$loader->alias('UnderstandExceptionLogger', 'Understand\UnderstandLaravel5\Facades\UnderstandExceptionLogger'); |
162
|
|
|
}); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Register understand logger |
167
|
|
|
* |
168
|
|
|
* @return void |
169
|
|
|
*/ |
170
|
|
|
protected function registerLogger() |
171
|
|
|
{ |
172
|
|
|
$this->app->singleton('understand.logger', function($app) |
173
|
|
|
{ |
174
|
|
|
$fieldProvider = $app['understand.fieldProvider']; |
175
|
|
|
$handler = $this->resolveHandler($app); |
176
|
|
|
|
177
|
|
|
return new Logger($fieldProvider, $handler); |
178
|
|
|
}); |
179
|
|
|
|
180
|
|
|
$this->app->booting(function() |
181
|
|
|
{ |
182
|
|
|
$loader = AliasLoader::getInstance(); |
183
|
|
|
$loader->alias('UnderstandLogger', 'Understand\UnderstandLaravel5\Facades\UnderstandLogger'); |
184
|
|
|
}); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Return default handler |
189
|
|
|
* |
190
|
|
|
* @param type $app |
191
|
|
|
* @return mixed |
192
|
|
|
* @throws \ErrorException |
193
|
|
|
*/ |
194
|
|
|
protected function resolveHandler($app) |
195
|
|
|
{ |
196
|
|
|
$inputToken = $app['config']->get('understand-laravel.token'); |
197
|
|
|
|
198
|
|
|
$apiUrl = $app['config']->get('understand-laravel.url', 'https://api.understand.io'); |
199
|
|
|
$handlerType = $app['config']->get('understand-laravel.handler'); |
200
|
|
|
$sslBundlePath = $app['config']->get('understand-laravel.ssl_ca_bundle'); |
201
|
|
|
|
202
|
|
|
if ($handlerType == 'async') |
203
|
|
|
{ |
204
|
|
|
return new Handlers\AsyncHandler($inputToken, $apiUrl, $sslBundlePath); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
if ($handlerType == 'sync') |
208
|
|
|
{ |
209
|
|
|
return new Handlers\SyncHandler($inputToken, $apiUrl, $sslBundlePath); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
throw new \ErrorException('understand-laravel handler misconfiguration:' . $handlerType); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Detect Laravel version |
217
|
|
|
* |
218
|
|
|
* @param array $versions |
219
|
|
|
* @return type |
220
|
|
|
*/ |
221
|
|
|
protected function detectLaravelVersion(array $versions) |
222
|
|
|
{ |
223
|
|
|
return Str::startsWith(Application::VERSION, $versions); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Listen Laravel logs |
228
|
|
|
* |
229
|
|
|
* @return void |
230
|
|
|
*/ |
231
|
|
|
protected function listenLaravelEvents() |
232
|
|
|
{ |
233
|
|
|
// only Laravel versions below L5.4 supports `illuminate.log` |
234
|
|
|
if ($this->detectLaravelVersion(['5.0', '5.1', '5.2', '5.3'])) |
235
|
|
|
{ |
236
|
|
|
$this->app['events']->listen('illuminate.log', function($level, $message, $context) |
237
|
|
|
{ |
238
|
|
|
$this->handleEvent($level, $message, $context); |
239
|
|
|
}); |
240
|
|
|
} |
241
|
|
|
else |
242
|
|
|
{ |
243
|
|
|
// starting from L5.4 MessageLogged event class was introduced |
244
|
|
|
// https://github.com/laravel/framework/commit/57c82d095c356a0fe0f9381536afec768cdcc072 |
245
|
|
|
$this->app['events']->listen('Illuminate\Log\Events\MessageLogged', function($log) |
246
|
|
|
{ |
247
|
|
|
|
248
|
|
|
$this->handleEvent($log->level, $log->message, $log->context); |
249
|
|
|
}); |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Listen Query events |
255
|
|
|
* |
256
|
|
|
* @return void |
257
|
|
|
*/ |
258
|
|
|
protected function listenQueryEvents() |
259
|
|
|
{ |
260
|
|
|
// only Laravel versions below L5.2 supports `illuminate.query` |
261
|
|
|
if ($this->detectLaravelVersion(['5.0', '5.1'])) |
262
|
|
|
{ |
263
|
|
|
// $this->events->fire('illuminate.query', [$query, $bindings, $time, $this->getName()]); |
|
|
|
|
264
|
|
|
$this->app['events']->listen('illuminate.query', function($query, $bindings, $time) |
265
|
|
|
{ |
266
|
|
|
$this->app['understand.dataCollector']->setInArray('sql_queries', [ |
267
|
|
|
'query' => $query, |
268
|
|
|
'time' => $time, |
269
|
|
|
]); |
270
|
|
|
}); |
271
|
|
|
} |
272
|
|
|
else |
273
|
|
|
{ |
274
|
|
|
// https://laravel.com/api/5.3/Illuminate/Database/Events/QueryExecuted.html |
275
|
|
|
$this->app['events']->listen('Illuminate\Database\Events\QueryExecuted', function($event) |
276
|
|
|
{ |
277
|
|
|
$this->app['understand.dataCollector']->setInArray('sql_queries', [ |
278
|
|
|
'query' => $event->sql, |
279
|
|
|
'time' => $event->time, |
280
|
|
|
]); |
281
|
|
|
}); |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Handle a new log event |
287
|
|
|
* |
288
|
|
|
* @param string $level |
289
|
|
|
* @param mixed $message |
290
|
|
|
* @param array $context |
291
|
|
|
* @return void |
292
|
|
|
*/ |
293
|
|
|
protected function handleEvent($level, $message, $context) |
294
|
|
|
{ |
295
|
|
|
if ($this->shouldIgnoreEvent($level, $message, $context)) |
296
|
|
|
{ |
297
|
|
|
return; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
// `\Log::info`, `\Log::debug` and NOT `\Exception` or `\Throwable` |
|
|
|
|
301
|
|
|
if (in_array($level, ['info', 'debug']) && ! ($message instanceof Exception || $message instanceof Throwable)) |
|
|
|
|
302
|
|
|
{ |
303
|
|
|
$this->app['understand.eventLogger']->logEvent($level, $message, $context); |
304
|
|
|
} |
305
|
|
|
// `\Log::notice`, `\Log::warning`, `\Log::error`, `\Log::critical`, `\Log::alert`, `\Log::emergency` and `\Exception`, `\Throwable` |
|
|
|
|
306
|
|
|
else if ($this->detectLaravelVersion(['5.6']) && isset($context['exception']) && ($context['exception'] instanceof Exception || $context['exception'] instanceof Throwable)) |
|
|
|
|
307
|
|
|
{ |
308
|
|
|
$exception = $context['exception']; |
309
|
|
|
unset($context['exception']); |
310
|
|
|
|
311
|
|
|
$this->app['understand.exceptionLogger']->logError($level, $exception, $context); |
312
|
|
|
} |
313
|
|
|
else |
314
|
|
|
{ |
315
|
|
|
$this->app['understand.exceptionLogger']->logError($level, $message, $context); |
316
|
|
|
} |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* @param $level |
321
|
|
|
* @param $message |
322
|
|
|
* @param $context |
323
|
|
|
* @return bool |
324
|
|
|
*/ |
325
|
|
|
protected function shouldIgnoreEvent($level, $message, $context) |
|
|
|
|
326
|
|
|
{ |
327
|
|
|
$ignoredEventTypes = (array)$this->app['config']->get('understand-laravel.ignored_logs'); |
328
|
|
|
|
329
|
|
|
if ( ! $ignoredEventTypes) |
|
|
|
|
330
|
|
|
{ |
331
|
|
|
return false; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
return in_array($level, $ignoredEventTypes, true); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Get the services provided by the provider. |
339
|
|
|
* |
340
|
|
|
* @return array |
341
|
|
|
*/ |
342
|
|
|
public function provides() |
343
|
|
|
{ |
344
|
|
|
return [ |
345
|
|
|
'understand.fieldProvider', |
346
|
|
|
'understand.logger', |
347
|
|
|
'understand.exceptionEncoder', |
348
|
|
|
'understand.exceptionLogger', |
349
|
|
|
'understand.eventLogger', |
350
|
|
|
'understand.tokenProvider', |
351
|
|
|
'understand.dataCollector', |
352
|
|
|
]; |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.