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 Blade; |
8
|
|
|
use Exception; |
9
|
|
|
use Throwable; |
10
|
|
|
use Understand\UnderstandLaravel5\Facades\UnderstandJsProvider; |
11
|
|
|
|
12
|
|
|
class UnderstandLaravel5ServiceProvider extends ServiceProvider |
13
|
|
|
{ |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Indicates if loading of the provider is deferred. |
17
|
|
|
* |
18
|
|
|
* @var bool |
19
|
|
|
*/ |
20
|
|
|
protected $defer = false; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Bootstrap the application events. |
24
|
|
|
* |
25
|
|
|
* @return void |
26
|
|
|
*/ |
27
|
|
|
public function boot() |
28
|
|
|
{ |
29
|
|
|
$configPath = __DIR__ . '/../../config/understand-laravel.php'; |
30
|
|
|
$this->publishes([$configPath => config_path('understand-laravel.php')], 'config'); |
31
|
|
|
$enabled = $this->app['config']->get('understand-laravel.enabled'); |
32
|
|
|
|
33
|
|
|
if ($enabled) |
34
|
|
|
{ |
35
|
|
|
$this->listenLaravelEvents(); |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
if ($enabled && $this->app['config']->get('understand-laravel.sql_enabled')) |
39
|
|
|
{ |
40
|
|
|
$this->listenQueryEvents(); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
$this->registerBladeDirectives(); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Register Blade directives. |
48
|
|
|
* |
49
|
|
|
* @return void |
50
|
|
|
*/ |
51
|
|
|
protected function registerBladeDirectives() |
52
|
|
|
{ |
53
|
|
|
// L5.0 does not support custom directives |
54
|
|
|
if ($this->detectLaravelVersion(['5.0'])) |
55
|
|
|
{ |
56
|
|
|
Blade::extend(function ($view, $compiler) |
57
|
|
|
{ |
58
|
|
|
$configuration = UnderstandJsProvider::getJsConfig(); |
59
|
|
|
|
60
|
|
|
$pattern = $compiler->createMatcher('understandJsConfig'); |
61
|
|
|
|
62
|
|
|
return preg_replace($pattern, json_encode($configuration), $view); |
63
|
|
|
}); |
64
|
|
|
|
65
|
|
|
Blade::extend(function ($view, $compiler) |
66
|
|
|
{ |
67
|
|
|
$configuration = UnderstandJsProvider::getJsConfig(); |
68
|
|
|
|
69
|
|
|
$pattern = $compiler->createMatcher('understandJs'); |
70
|
|
|
|
71
|
|
|
$out = "<script src=\"" . UnderstandJsProvider::getJsBundleUrl() . "\"></script>\r\n"; |
72
|
|
|
$out .= "<script>\r\n"; |
73
|
|
|
$out .= "Understand.init(" . json_encode($configuration) . ");\r\n"; |
74
|
|
|
$out .= "Understand.catchErrors();\r\n"; |
75
|
|
|
$out .= "</script>"; |
76
|
|
|
|
77
|
|
|
return preg_replace($pattern, $out, $view); |
78
|
|
|
}); |
79
|
|
|
} |
80
|
|
|
else |
81
|
|
|
{ |
82
|
|
|
Blade::directive('understandJsConfig', function () |
83
|
|
|
{ |
84
|
|
|
$configuration = UnderstandJsProvider::getJsConfig(); |
85
|
|
|
|
86
|
|
|
return json_encode($configuration); |
87
|
|
|
}); |
88
|
|
|
|
89
|
|
|
Blade::directive('understandJs', function () |
90
|
|
|
{ |
91
|
|
|
$configuration = UnderstandJsProvider::getJsConfig(); |
92
|
|
|
|
93
|
|
|
$out = "<script src=\"" . UnderstandJsProvider::getJsBundleUrl() . "\"></script>\r\n"; |
94
|
|
|
$out .= "<script>\r\n"; |
95
|
|
|
$out .= "Understand.init(" . json_encode($configuration) . ");\r\n"; |
96
|
|
|
$out .= "Understand.catchErrors();\r\n"; |
97
|
|
|
$out .= "</script>"; |
98
|
|
|
|
99
|
|
|
return $out; |
100
|
|
|
}); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Register the service provider. |
106
|
|
|
* |
107
|
|
|
* @return void |
108
|
|
|
*/ |
109
|
|
|
public function register() |
110
|
|
|
{ |
111
|
|
|
$this->registerConfig(); |
112
|
|
|
$this->registerFieldProvider(); |
113
|
|
|
$this->registerJsProvider(); |
114
|
|
|
$this->registerDataCollector(); |
115
|
|
|
$this->registerTokenProvider(); |
116
|
|
|
$this->registerLogger(); |
117
|
|
|
$this->registerExceptionEncoder(); |
118
|
|
|
$this->registerEventLoggers(); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Register config |
123
|
|
|
* |
124
|
|
|
* @return void |
125
|
|
|
*/ |
126
|
|
|
protected function registerConfig() |
127
|
|
|
{ |
128
|
|
|
$configPath = __DIR__ . '/../../config/understand-laravel.php'; |
129
|
|
|
$this->mergeConfigFrom($configPath, 'understand-laravel'); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Register field provider |
134
|
|
|
* |
135
|
|
|
* @return void |
136
|
|
|
*/ |
137
|
|
|
protected function registerFieldProvider() |
138
|
|
|
{ |
139
|
|
|
$this->app->bind('understand.fieldProvider', function($app) |
140
|
|
|
{ |
141
|
|
|
$fieldProvider = new FieldProvider(); |
142
|
|
|
|
143
|
|
|
if ($app['config']['session.driver']) |
144
|
|
|
{ |
145
|
|
|
$fieldProvider->setSessionStore($app['session.store']); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
$fieldProvider->setRouter($app['router']); |
149
|
|
|
$fieldProvider->setRequest($app['request']); |
150
|
|
|
$fieldProvider->setEnvironment($app->environment()); |
151
|
|
|
$fieldProvider->setTokenProvider($app['understand.tokenProvider']); |
152
|
|
|
$fieldProvider->setDataCollector($app['understand.dataCollector']); |
153
|
|
|
$fieldProvider->setApp($app); |
154
|
|
|
|
155
|
|
|
return $fieldProvider; |
156
|
|
|
}); |
157
|
|
|
|
158
|
|
|
$this->app->booting(function() |
159
|
|
|
{ |
160
|
|
|
$loader = AliasLoader::getInstance(); |
161
|
|
|
$loader->alias('UnderstandFieldProvider', 'Understand\UnderstandLaravel5\Facades\UnderstandFieldProvider'); |
162
|
|
|
}); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Register JS provider |
167
|
|
|
* |
168
|
|
|
* @return void |
169
|
|
|
*/ |
170
|
|
|
protected function registerJsProvider() |
171
|
|
|
{ |
172
|
|
|
$this->app->bind('understand.jsProvider', function($app) |
173
|
|
|
{ |
174
|
|
|
return new JsProvider($app); |
175
|
|
|
}); |
176
|
|
|
|
177
|
|
|
$this->app->booting(function() |
178
|
|
|
{ |
179
|
|
|
$loader = AliasLoader::getInstance(); |
180
|
|
|
$loader->alias('UnderstandJsProvider', 'Understand\UnderstandLaravel5\Facades\UnderstandJsProvider'); |
181
|
|
|
}); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Register token generator class |
186
|
|
|
* |
187
|
|
|
* @return void |
188
|
|
|
*/ |
189
|
|
|
protected function registerTokenProvider() |
190
|
|
|
{ |
191
|
|
|
$this->app->singleton('understand.tokenProvider', function() |
192
|
|
|
{ |
193
|
|
|
return new TokenProvider(); |
194
|
|
|
}); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Register data collector class |
199
|
|
|
* |
200
|
|
|
* @return void |
201
|
|
|
*/ |
202
|
|
|
protected function registerDataCollector() |
203
|
|
|
{ |
204
|
|
|
$this->app->singleton('understand.dataCollector', function() |
205
|
|
|
{ |
206
|
|
|
return new DataCollector(); |
207
|
|
|
}); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Register exception encoder |
212
|
|
|
* |
213
|
|
|
* @return void |
214
|
|
|
*/ |
215
|
|
|
protected function registerExceptionEncoder() |
216
|
|
|
{ |
217
|
|
|
$this->app->bind('understand.exceptionEncoder', function() |
218
|
|
|
{ |
219
|
|
|
return new ExceptionEncoder; |
220
|
|
|
}); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Register exception and event logger |
225
|
|
|
* |
226
|
|
|
* @return void |
227
|
|
|
*/ |
228
|
|
|
protected function registerEventLoggers() |
229
|
|
|
{ |
230
|
|
|
$this->app->bind('understand.eventLogger', function($app) |
231
|
|
|
{ |
232
|
|
|
return new EventLogger($app['understand.logger'], $app['config']); |
233
|
|
|
}); |
234
|
|
|
|
235
|
|
|
$this->app->bind('understand.exceptionLogger', function($app) |
236
|
|
|
{ |
237
|
|
|
return new ExceptionLogger($app['understand.logger'], $app['understand.exceptionEncoder'], $app['config']); |
238
|
|
|
}); |
239
|
|
|
|
240
|
|
|
$this->app->booting(function() |
241
|
|
|
{ |
242
|
|
|
$loader = AliasLoader::getInstance(); |
243
|
|
|
$loader->alias('UnderstandExceptionLogger', 'Understand\UnderstandLaravel5\Facades\UnderstandExceptionLogger'); |
244
|
|
|
}); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Register understand logger |
249
|
|
|
* |
250
|
|
|
* @return void |
251
|
|
|
*/ |
252
|
|
|
protected function registerLogger() |
253
|
|
|
{ |
254
|
|
|
$this->app->singleton('understand.logger', function($app) |
255
|
|
|
{ |
256
|
|
|
$fieldProvider = $app['understand.fieldProvider']; |
257
|
|
|
$handler = $this->resolveHandler($app); |
258
|
|
|
|
259
|
|
|
return new Logger($fieldProvider, $handler); |
260
|
|
|
}); |
261
|
|
|
|
262
|
|
|
$this->app->booting(function() |
263
|
|
|
{ |
264
|
|
|
$loader = AliasLoader::getInstance(); |
265
|
|
|
$loader->alias('UnderstandLogger', 'Understand\UnderstandLaravel5\Facades\UnderstandLogger'); |
266
|
|
|
}); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Return default handler |
271
|
|
|
* |
272
|
|
|
* @param type $app |
273
|
|
|
* @return mixed |
274
|
|
|
* @throws \ErrorException |
275
|
|
|
*/ |
276
|
|
|
protected function resolveHandler($app) |
277
|
|
|
{ |
278
|
|
|
$inputToken = $app['config']->get('understand-laravel.token'); |
279
|
|
|
|
280
|
|
|
$apiUrl = $app['config']->get('understand-laravel.url', 'https://api.understand.io'); |
281
|
|
|
$handlerType = $app['config']->get('understand-laravel.handler'); |
282
|
|
|
$sslBundlePath = $app['config']->get('understand-laravel.ssl_ca_bundle'); |
283
|
|
|
|
284
|
|
|
if ($handlerType == 'async') |
285
|
|
|
{ |
286
|
|
|
return new Handlers\AsyncHandler($inputToken, $apiUrl, $sslBundlePath); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
if ($handlerType == 'sync') |
290
|
|
|
{ |
291
|
|
|
return new Handlers\SyncHandler($inputToken, $apiUrl, $sslBundlePath); |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
throw new \ErrorException('understand-laravel handler misconfiguration:' . $handlerType); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Detect Laravel version |
299
|
|
|
* |
300
|
|
|
* @param array $versions |
301
|
|
|
* @return type |
302
|
|
|
*/ |
303
|
|
|
protected function detectLaravelVersion(array $versions) |
304
|
|
|
{ |
305
|
|
|
return Str::startsWith(Application::VERSION, $versions); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Listen Laravel logs and queue events |
310
|
|
|
* |
311
|
|
|
* @return void |
312
|
|
|
*/ |
313
|
|
|
protected function listenLaravelEvents() |
314
|
|
|
{ |
315
|
|
|
// only Laravel versions below L5.4 supports `illuminate.log` |
316
|
|
|
if ($this->detectLaravelVersion(['5.0', '5.1', '5.2', '5.3'])) |
317
|
|
|
{ |
318
|
|
|
$this->app['events']->listen('illuminate.log', function($level, $message, $context) |
319
|
|
|
{ |
320
|
|
|
$this->handleEvent($level, $message, $context); |
321
|
|
|
}); |
322
|
|
|
} |
323
|
|
|
else |
324
|
|
|
{ |
325
|
|
|
// starting from L5.4 MessageLogged event class was introduced |
326
|
|
|
// https://github.com/laravel/framework/commit/57c82d095c356a0fe0f9381536afec768cdcc072 |
327
|
|
|
$this->app['events']->listen('Illuminate\Log\Events\MessageLogged', function($log) |
328
|
|
|
{ |
329
|
|
|
|
330
|
|
|
$this->handleEvent($log->level, $log->message, $log->context); |
331
|
|
|
}); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
// starting from L5.2 JobProcessing event class was introduced |
335
|
|
|
// https://github.com/illuminate/queue/commit/ce2b5518902b1bcb9ef650c41900fc8c6392eb0c |
336
|
|
|
if ($this->app->runningInConsole()) |
337
|
|
|
{ |
338
|
|
|
if ($this->detectLaravelVersion(['5.0', '5.1'])) |
339
|
|
|
{ |
340
|
|
|
$this->app['events']->listen('illuminate.queue.after', function() |
341
|
|
|
{ |
342
|
|
|
$this->app['understand.tokenProvider']->generate(); |
343
|
|
|
$this->app['understand.dataCollector']->reset(); |
344
|
|
|
}); |
345
|
|
|
|
346
|
|
|
$this->app['events']->listen('illuminate.queue.failed', function() |
347
|
|
|
{ |
348
|
|
|
$this->app['understand.tokenProvider']->generate(); |
349
|
|
|
$this->app['understand.dataCollector']->reset(); |
350
|
|
|
}); |
351
|
|
|
} |
352
|
|
|
else |
353
|
|
|
{ |
354
|
|
|
$this->app['events']->listen('Illuminate\Queue\Events\JobProcessing', function() |
355
|
|
|
{ |
356
|
|
|
$this->app['understand.tokenProvider']->generate(); |
357
|
|
|
$this->app['understand.dataCollector']->reset(); |
358
|
|
|
}); |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Listen Query events |
365
|
|
|
* |
366
|
|
|
* @return void |
367
|
|
|
*/ |
368
|
|
|
protected function listenQueryEvents() |
369
|
|
|
{ |
370
|
|
|
// only Laravel versions below L5.2 supports `illuminate.query` |
371
|
|
|
if ($this->detectLaravelVersion(['5.0', '5.1'])) |
372
|
|
|
{ |
373
|
|
|
// $this->events->fire('illuminate.query', [$query, $bindings, $time, $this->getName()]); |
374
|
|
View Code Duplication |
$this->app['events']->listen('illuminate.query', function($query, $bindings, $time) |
|
|
|
|
375
|
|
|
{ |
376
|
|
|
$this->app['understand.dataCollector']->setInArray('sql_queries', [ |
377
|
|
|
'query' => $query, |
378
|
|
|
'bindings' => $bindings, |
379
|
|
|
'time' => $time, |
380
|
|
|
]); |
381
|
|
|
}); |
382
|
|
|
} |
383
|
|
|
else |
384
|
|
|
{ |
385
|
|
|
// https://laravel.com/api/5.3/Illuminate/Database/Events/QueryExecuted.html |
386
|
|
View Code Duplication |
$this->app['events']->listen('Illuminate\Database\Events\QueryExecuted', function($event) |
|
|
|
|
387
|
|
|
{ |
388
|
|
|
$this->app['understand.dataCollector']->setInArray('sql_queries', [ |
389
|
|
|
'query' => $event->sql, |
390
|
|
|
'bindings' => $event->bindings, |
391
|
|
|
'time' => $event->time, |
392
|
|
|
]); |
393
|
|
|
}); |
394
|
|
|
} |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Handle a new log event |
399
|
|
|
* |
400
|
|
|
* @param string $level |
401
|
|
|
* @param mixed $message |
402
|
|
|
* @param array $context |
403
|
|
|
* @return void |
404
|
|
|
*/ |
405
|
|
|
protected function handleEvent($level, $message, $context) |
406
|
|
|
{ |
407
|
|
|
if ($this->shouldIgnoreEvent($level, $message, $context)) |
408
|
|
|
{ |
409
|
|
|
return; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
// `\Log::info`, `\Log::debug` and NOT `\Exception` or `\Throwable` |
413
|
|
|
if (in_array($level, ['info', 'debug']) && ! ($message instanceof Exception || $message instanceof Throwable)) |
|
|
|
|
414
|
|
|
{ |
415
|
|
|
$this->app['understand.eventLogger']->logEvent($level, $message, $context); |
416
|
|
|
} |
417
|
|
|
// `\Log::notice`, `\Log::warning`, `\Log::error`, `\Log::critical`, `\Log::alert`, `\Log::emergency` and `\Exception`, `\Throwable` |
418
|
|
|
// '5.5', '5.6', '5.7', '5.8' |
419
|
|
|
else if ( ! $this->detectLaravelVersion(['5.0', '5.1', '5.2', '5.3', '5.4']) && isset($context['exception']) && ($context['exception'] instanceof Exception || $context['exception'] instanceof Throwable)) |
|
|
|
|
420
|
|
|
{ |
421
|
|
|
$exception = $context['exception']; |
422
|
|
|
unset($context['exception']); |
423
|
|
|
|
424
|
|
|
$this->app['understand.exceptionLogger']->logError($level, $exception, $context); |
425
|
|
|
} |
426
|
|
|
else |
427
|
|
|
{ |
428
|
|
|
$this->app['understand.exceptionLogger']->logError($level, $message, $context); |
429
|
|
|
} |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* @param $level |
434
|
|
|
* @param $message |
435
|
|
|
* @param $context |
436
|
|
|
* @return bool |
437
|
|
|
*/ |
438
|
|
|
protected function shouldIgnoreEvent($level, $message, $context) |
|
|
|
|
439
|
|
|
{ |
440
|
|
|
$ignoredEventTypes = (array)$this->app['config']->get('understand-laravel.ignored_logs'); |
441
|
|
|
|
442
|
|
|
if ( ! $ignoredEventTypes) |
|
|
|
|
443
|
|
|
{ |
444
|
|
|
return false; |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
return in_array($level, $ignoredEventTypes, true); |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Get the services provided by the provider. |
452
|
|
|
* |
453
|
|
|
* @return array |
454
|
|
|
*/ |
455
|
|
|
public function provides() |
456
|
|
|
{ |
457
|
|
|
return [ |
458
|
|
|
'understand.fieldProvider', |
459
|
|
|
'understand.logger', |
460
|
|
|
'understand.exceptionEncoder', |
461
|
|
|
'understand.exceptionLogger', |
462
|
|
|
'understand.eventLogger', |
463
|
|
|
'understand.tokenProvider', |
464
|
|
|
'understand.dataCollector', |
465
|
|
|
]; |
466
|
|
|
} |
467
|
|
|
} |
468
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.