Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Spatie\LaravelIgnition;
4
 
5
use Illuminate\Contracts\Debug\ExceptionHandler;
6
use Illuminate\Foundation\Application;
7
use Illuminate\Support\Facades\Log;
8
use Illuminate\Support\ServiceProvider;
9
use Illuminate\View\ViewException;
10
use Laravel\Octane\Events\RequestReceived;
11
use Laravel\Octane\Events\RequestTerminated;
12
use Laravel\Octane\Events\TaskReceived;
13
use Laravel\Octane\Events\TickReceived;
14
use Monolog\Logger;
15
use Spatie\FlareClient\Flare;
16
use Spatie\FlareClient\FlareMiddleware\AddSolutions;
17
use Spatie\Ignition\Config\FileConfigManager;
18
use Spatie\Ignition\Config\IgnitionConfig;
19
use Spatie\Ignition\Contracts\ConfigManager;
20
use Spatie\Ignition\Contracts\SolutionProviderRepository as SolutionProviderRepositoryContract;
21
use Spatie\Ignition\Ignition;
22
use Spatie\LaravelIgnition\Commands\SolutionMakeCommand;
23
use Spatie\LaravelIgnition\Commands\SolutionProviderMakeCommand;
24
use Spatie\LaravelIgnition\Commands\TestCommand;
25
use Spatie\LaravelIgnition\ContextProviders\LaravelContextProviderDetector;
26
use Spatie\LaravelIgnition\Exceptions\InvalidConfig;
27
use Spatie\LaravelIgnition\FlareMiddleware\AddJobs;
28
use Spatie\LaravelIgnition\FlareMiddleware\AddLogs;
29
use Spatie\LaravelIgnition\FlareMiddleware\AddQueries;
30
use Spatie\LaravelIgnition\Recorders\DumpRecorder\DumpRecorder;
31
use Spatie\LaravelIgnition\Recorders\JobRecorder\JobRecorder;
32
use Spatie\LaravelIgnition\Recorders\LogRecorder\LogRecorder;
33
use Spatie\LaravelIgnition\Recorders\QueryRecorder\QueryRecorder;
34
use Spatie\LaravelIgnition\Renderers\IgnitionExceptionRenderer;
35
use Spatie\LaravelIgnition\Renderers\IgnitionWhoopsHandler;
36
use Spatie\LaravelIgnition\Solutions\SolutionProviders\SolutionProviderRepository;
37
use Spatie\LaravelIgnition\Support\FlareLogHandler;
38
use Spatie\LaravelIgnition\Support\SentReports;
39
use Spatie\LaravelIgnition\Views\ViewExceptionMapper;
40
 
41
class IgnitionServiceProvider extends ServiceProvider
42
{
43
    public function register(): void
44
    {
45
        $this->registerConfig();
46
        $this->registerFlare();
47
        $this->registerIgnition();
48
        $this->registerRenderer();
49
        $this->registerRecorders();
50
        $this->registerLogHandler();
51
    }
52
 
53
    public function boot()
54
    {
55
        if ($this->app->runningInConsole()) {
56
            $this->registerCommands();
57
            $this->publishConfigs();
58
        }
59
 
60
        $this->registerRoutes();
61
        $this->configureTinker();
62
        $this->configureOctane();
63
        $this->registerViewExceptionMapper();
64
        $this->startRecorders();
65
        $this->configureQueue();
66
    }
67
 
68
    protected function registerConfig(): void
69
    {
70
        $this->mergeConfigFrom(__DIR__ . '/../config/flare.php', 'flare');
71
        $this->mergeConfigFrom(__DIR__ . '/../config/ignition.php', 'ignition');
72
    }
73
 
74
    protected function registerCommands(): void
75
    {
76
        if ($this->app['config']->get('flare.key')) {
77
            $this->commands([
78
                TestCommand::class,
79
            ]);
80
        }
81
 
82
        if ($this->app['config']->get('ignition.register_commands')) {
83
            $this->commands([
84
                SolutionMakeCommand::class,
85
                SolutionProviderMakeCommand::class,
86
            ]);
87
        }
88
    }
89
 
90
    protected function publishConfigs(): void
91
    {
92
        $this->publishes([
93
            __DIR__ . '/../config/ignition.php' => config_path('ignition.php'),
94
        ], 'ignition-config');
95
 
96
        $this->publishes([
97
            __DIR__ . '/../config/flare.php' => config_path('flare.php'),
98
        ], 'flare-config');
99
    }
100
 
101
    protected function registerRenderer(): void
102
    {
103
        if (interface_exists('Whoops\Handler\HandlerInterface')) {
104
            $this->app->bind(
105
                'Whoops\Handler\HandlerInterface',
106
                fn (Application $app) => $app->make(IgnitionWhoopsHandler::class)
107
            );
108
        }
109
 
110
        if (interface_exists('Illuminate\Contracts\Foundation\ExceptionRenderer')) {
111
            $this->app->bind(
112
                'Illuminate\Contracts\Foundation\ExceptionRenderer',
113
                fn (Application $app) => $app->make(IgnitionExceptionRenderer::class)
114
            );
115
        }
116
    }
117
 
118
    protected function registerFlare(): void
119
    {
120
        $this->app->singleton(Flare::class, function () {
121
            return Flare::make()
122
                ->setApiToken(config('flare.key') ?? '')
123
                ->setBaseUrl(config('flare.base_url', 'https://flareapp.io/api'))
124
                ->applicationPath(base_path())
125
                ->setStage(app()->environment())
126
                ->setContextProviderDetector(new LaravelContextProviderDetector())
127
                ->registerMiddleware($this->getFlareMiddleware())
128
                ->registerMiddleware(new AddSolutions(new SolutionProviderRepository($this->getSolutionProviders())));
129
        });
130
 
131
        $this->app->singleton(SentReports::class);
132
    }
133
 
134
    protected function registerIgnition(): void
135
    {
136
        $this->app->singleton(
137
            ConfigManager::class,
138
            fn () => new FileConfigManager(config('ignition.settings_file_path', ''))
139
        );
140
 
141
        $ignitionConfig = (new IgnitionConfig())
142
            ->merge(config('ignition', []))
143
            ->loadConfigFile();
144
 
145
        $solutionProviders = $this->getSolutionProviders();
146
        $solutionProviderRepository = new SolutionProviderRepository($solutionProviders);
147
 
148
        $this->app->singleton(IgnitionConfig::class, fn () => $ignitionConfig);
149
 
150
        $this->app->singleton(SolutionProviderRepositoryContract::class, fn () => $solutionProviderRepository);
151
 
152
        $this->app->singleton(
153
            Ignition::class,
154
            fn () => (new Ignition())
155
                ->applicationPath(base_path())
156
        );
157
    }
158
 
159
    protected function registerRecorders(): void
160
    {
161
        $this->app->singleton(DumpRecorder::class);
162
 
163
        $this->app->singleton(LogRecorder::class, function (Application $app): LogRecorder {
164
            return new LogRecorder(
165
                $app,
166
                config()->get('flare.flare_middleware.' . AddLogs::class . '.maximum_number_of_collected_logs')
167
            );
168
        });
169
 
170
        $this->app->singleton(
171
            QueryRecorder::class,
172
            function (Application $app): QueryRecorder {
173
                return new QueryRecorder(
174
                    $app,
175
                    config('flare.flare_middleware.' . AddQueries::class . '.report_query_bindings', true),
176
                    config('flare.flare_middleware.' . AddQueries::class . '.maximum_number_of_collected_queries', 200)
177
                );
178
            }
179
        );
180
 
181
        $this->app->singleton(JobRecorder::class, function (Application $app): JobRecorder {
182
            return new JobRecorder(
183
                $app,
184
                config('flare.flare_middleware.' . AddJobs::class . '.max_chained_job_reporting_depth', 5)
185
            );
186
        });
187
    }
188
 
189
    public function configureTinker(): void
190
    {
191
        if (! $this->app->runningInConsole()) {
192
            if (isset($_SERVER['argv']) && ['artisan', 'tinker'] === $_SERVER['argv']) {
193
                app(Flare::class)->sendReportsImmediately();
194
            }
195
        }
196
    }
197
 
198
    protected function configureOctane(): void
199
    {
200
        if (isset($_SERVER['LARAVEL_OCTANE'])) {
201
            $this->setupOctane();
202
        }
203
    }
204
 
205
    protected function registerViewExceptionMapper(): void
206
    {
207
        $handler = $this->app->make(ExceptionHandler::class);
208
 
209
        if (! method_exists($handler, 'map')) {
210
            return;
211
        }
212
 
213
        $handler->map(function (ViewException $viewException) {
214
            return $this->app->make(ViewExceptionMapper::class)->map($viewException);
215
        });
216
    }
217
 
218
    protected function registerRoutes(): void
219
    {
220
        $this->loadRoutesFrom(realpath(__DIR__ . '/ignition-routes.php'));
221
    }
222
 
223
    protected function registerLogHandler(): void
224
    {
225
        $this->app->singleton('flare.logger', function ($app) {
226
            $handler = new FlareLogHandler(
227
                $app->make(Flare::class),
228
                $app->make(SentReports::class),
229
            );
230
 
231
            $logLevelString = config('logging.channels.flare.level', 'error');
232
 
233
            $logLevel = $this->getLogLevel($logLevelString);
234
 
235
            $handler->setMinimumReportLogLevel($logLevel);
236
 
237
            return tap(
238
                new Logger('Flare'),
239
                fn (Logger $logger) => $logger->pushHandler($handler)
240
            );
241
        });
242
 
243
        Log::extend('flare', fn ($app) => $app['flare.logger']);
244
    }
245
 
246
    protected function startRecorders(): void
247
    {
248
        foreach ($this->app->config['ignition.recorders'] ?? [] as $recorder) {
249
            $this->app->make($recorder)->start();
250
        }
251
    }
252
 
253
    protected function configureQueue(): void
254
    {
255
        if (! $this->app->bound('queue')) {
256
            return;
257
        }
258
 
259
        $queue = $this->app->get('queue');
260
 
261
        // Reset before executing a queue job to make sure the job's log/query/dump recorders are empty.
262
        // When using a sync queue this also reports the queued reports from previous exceptions.
263
        $queue->before(function () {
264
            $this->resetFlareAndLaravelIgnition();
265
            app(Flare::class)->sendReportsImmediately();
266
        });
267
 
268
        // Send queued reports (and reset) after executing a queue job.
269
        $queue->after(function () {
270
            $this->resetFlareAndLaravelIgnition();
271
        });
272
 
273
        // Note: the $queue->looping() event can't be used because it's not triggered on Vapor
274
    }
275
 
276
    protected function getLogLevel(string $logLevelString): int
277
    {
278
        $logLevel = Logger::getLevels()[strtoupper($logLevelString)] ?? null;
279
 
280
        if (! $logLevel) {
281
            throw InvalidConfig::invalidLogLevel($logLevelString);
282
        }
283
 
284
        return $logLevel;
285
    }
286
 
287
    protected function getFlareMiddleware(): array
288
    {
289
        return collect(config('flare.flare_middleware'))
290
            ->map(function ($value, $key) {
291
                if (is_string($key)) {
292
                    $middlewareClass = $key;
293
                    $parameters = $value ?? [];
294
                } else {
295
                    $middlewareClass = $value;
296
                    $parameters = [];
297
                }
298
 
299
                return new $middlewareClass(...array_values($parameters));
300
            })
301
            ->values()
302
            ->toArray();
303
    }
304
 
305
    protected function getSolutionProviders(): array
306
    {
307
        return collect(config('ignition.solution_providers'))
308
            ->reject(
309
                fn (string $class) => in_array($class, config('ignition.ignored_solution_providers'))
310
            )
311
            ->toArray();
312
    }
313
 
314
    protected function setupOctane(): void
315
    {
316
        $this->app['events']->listen(RequestReceived::class, function () {
317
            $this->resetFlareAndLaravelIgnition();
318
        });
319
 
320
        $this->app['events']->listen(TaskReceived::class, function () {
321
            $this->resetFlareAndLaravelIgnition();
322
        });
323
 
324
        $this->app['events']->listen(TickReceived::class, function () {
325
            $this->resetFlareAndLaravelIgnition();
326
        });
327
 
328
        $this->app['events']->listen(RequestTerminated::class, function () {
329
            $this->resetFlareAndLaravelIgnition();
330
        });
331
    }
332
 
333
    protected function resetFlareAndLaravelIgnition(): void
334
    {
335
        $this->app->get(SentReports::class)->clear();
336
        $this->app->get(Ignition::class)->reset();
337
 
338
        if (config('flare.flare_middleware.' . AddLogs::class)) {
339
            $this->app->make(LogRecorder::class)->reset();
340
        }
341
 
342
        if (config('flare.flare_middleware.' . AddQueries::class)) {
343
            $this->app->make(QueryRecorder::class)->reset();
344
        }
345
 
346
        if (config('flare.flare_middleware.' . AddJobs::class)) {
347
            $this->app->make(JobRecorder::class)->reset();
348
        }
349
 
350
        $this->app->make(DumpRecorder::class)->reset();
351
    }
352
}