├── resources └── views │ └── .gitkeep ├── src ├── helpers.php ├── Facades │ └── CappadociaViewer.php ├── Enums │ ├── ViewerType.php │ └── BadgeType.php ├── CappadociaViewerClient.php ├── Watchers │ ├── Watcher.php │ ├── LogWatcher.php │ ├── QueryWatcher.php │ ├── JobWatcher.php │ └── RequestWatcher.php ├── FormatModel.php ├── DataTransferObjects │ └── ViewerDto.php ├── ExtractProperties.php ├── CappadociaViewerServiceProvider.php └── CappadociaViewer.php ├── config └── cappadocia-viewer.php ├── CHANGELOG.md ├── LICENSE.md ├── composer.json ├── pint.json └── README.md /resources/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | 'log', 18 | self::JOB => 'job', 19 | self::QUERY => 'query', 20 | self::REQUEST => 'request', 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /config/cappadocia-viewer.php: -------------------------------------------------------------------------------- 1 | env('CAPPADOCIA_VIEWER_SERVER_URL', 'http://127.0.0.1:9091'), 7 | 'timeout' => env('CAPPADOCIA_VIEWER_TIMEOUT', 3), 8 | 'enabled' => env('CAPPADOCIA_VIEWER_ENABLED', true), 9 | 'watch_logs' => env('CAPPADOCIA_VIEWER_WATCH_LOGS', true), 10 | 'watch_jobs' => env('CAPPADOCIA_VIEWER_WATCH_JOBS', false), 11 | 'watch_queries' => env('CAPPADOCIA_VIEWER_WATCH_QUERIES', false), 12 | 'watch_requests' => env('CAPPADOCIA_VIEWER_WATCH_REQUESTS', false), 13 | ]; 14 | -------------------------------------------------------------------------------- /src/CappadociaViewerClient.php: -------------------------------------------------------------------------------- 1 | client->post('/viewers', [ 24 | 'json' => $data, 25 | ]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `cappadocia-viewer-for-laravel` will be documented in this file. 4 | 5 | ## 1.0.1 - 2023-08-02 6 | 7 | - [docs: add image and detailed usage instructions](https://github.com/hsndmr/cappadocia-viewer-for-laravel) 8 | - [docs: add app image for readme](https://github.com/hsndmr/cappadocia-viewer-for-laravel) 9 | - [chore: include helpers.php in autoload configuration](https://github.com/hsndmr/cappadocia-viewer-for-laravel) 10 | - [feat: add cappadocia function to initiate SenderViewerBuilder](https://github.com/hsndmr/cappadocia-viewer-for-laravel) 11 | - [feat: add SenderViewerBuilder for handling viewer data](https://github.com/hsndmr/cappadocia-viewer-for-laravel) 12 | - [docs: add --dev tag](https://github.com/hsndmr/cappadocia-viewer-for-laravel) 13 | 14 | ## 1.0.0 - 2023-07-31 15 | 16 | - it includes only log watcher 17 | -------------------------------------------------------------------------------- /src/Watchers/Watcher.php: -------------------------------------------------------------------------------- 1 | getConfigName(), false)) { 18 | return true; 19 | } 20 | 21 | if ($this->isWatching) { 22 | return true; 23 | } 24 | 25 | return false; 26 | } 27 | 28 | public function watch(): self 29 | { 30 | $this->isWatching = true; 31 | 32 | return $this; 33 | } 34 | 35 | public function stopWatching(): self 36 | { 37 | $this->isWatching = false; 38 | 39 | return $this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/FormatModel.php: -------------------------------------------------------------------------------- 1 | incrementing) { 19 | $keys = [ 20 | $model->getAttribute($model->getForeignKey()), 21 | $model->getAttribute($model->getRelatedKey()), 22 | ]; 23 | } else { 24 | $keys = $model->getKey(); 25 | } 26 | 27 | return get_class($model).':'.implode('_', array_map(function ($value) { 28 | return $value instanceof BackedEnum ? $value->value : $value; 29 | }, Arr::wrap($keys))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Enums/BadgeType.php: -------------------------------------------------------------------------------- 1 | 'success', 19 | self::WARNING => 'warning', 20 | self::ERROR => 'error', 21 | self::INFO => 'info', 22 | default => 'primary', 23 | }; 24 | } 25 | 26 | public static function fromLogLevel(string $level): BadgeType 27 | { 28 | return match ($level) { 29 | 'debug', 'notice', 'info' => self::INFO, 30 | 'warning' => self::WARNING, 31 | 'error', 'critical', 'alert', 'emergency' => self::ERROR, 32 | default => self::PRIMARY, 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DataTransferObjects/ViewerDto.php: -------------------------------------------------------------------------------- 1 | $this->type->type(), 25 | 'message' => $this->message ?? '', 26 | 'badge' => $this->badge ?? '', 27 | 'badgeType' => $this->badgeType?->type() ?? '', 28 | 'context' => $this->context ?? '', 29 | 'timestamp' => now()->timestamp * 1000, 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) hsndmr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ExtractProperties.php: -------------------------------------------------------------------------------- 1 | getProperties()) 18 | ->mapWithKeys(function ($property) use ($target) { 19 | $property->setAccessible(true); 20 | 21 | if (PHP_VERSION_ID >= 70400 && !$property->isInitialized($target)) { 22 | return []; 23 | } 24 | 25 | if (($value = $property->getValue($target)) instanceof Model) { 26 | return [$property->getName() => FormatModel::given($value)]; 27 | } elseif (is_object($value)) { 28 | return [ 29 | $property->getName() => [ 30 | 'class' => get_class($value), 31 | 'properties' => json_decode(json_encode($value), true), 32 | ], 33 | ]; 34 | } else { 35 | return [$property->getName() => json_decode(json_encode($value), true)]; 36 | } 37 | })->toArray(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Watchers/LogWatcher.php: -------------------------------------------------------------------------------- 1 | shouldHandleLog($event)) { 24 | return; 25 | } 26 | 27 | CappadociaViewer::setMessage($event->message) 28 | ->setBadge($event->level) 29 | ->setBadgeType(BadgeType::fromLogLevel($event->level)) 30 | ->setType(ViewerType::LOG) 31 | ->send($event->context); 32 | } 33 | 34 | public function shouldHandleLog(MessageLogged $event): bool 35 | { 36 | if (!$this->isWatching()) { 37 | return false; 38 | } 39 | 40 | if (isset($event->context['exception']) && $event->context['exception'] instanceof Throwable) { 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | protected function getConfigName(): string 48 | { 49 | return 'logs'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Watchers/QueryWatcher.php: -------------------------------------------------------------------------------- 1 | isWatching()) { 22 | return; 23 | } 24 | 25 | [$sql, $bindings] = $this->getSqlAndBindings($event); 26 | 27 | CappadociaViewer::setMessage($sql) 28 | ->setType(ViewerType::QUERY) 29 | ->setBadge('query') 30 | ->send([ 31 | 'bindings' => [ 32 | 'bindings' => $bindings, 33 | ], 34 | 'time' => [ 35 | number_format($event->time, 2, '.', '').' ms', 36 | ], 37 | ]); 38 | } 39 | 40 | protected function getSqlAndBindings(QueryExecuted $event): array 41 | { 42 | $bindings = $event->bindings; 43 | 44 | $sql = $event->connection->getQueryGrammar()->substituteBindingsIntoRawSql( 45 | $event->sql, 46 | $event->connection->prepareBindings($bindings) 47 | ); 48 | 49 | return [$sql, $bindings]; 50 | } 51 | 52 | protected function getConfigName(): string 53 | { 54 | return 'queries'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/CappadociaViewerServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('cappadocia-viewer') 34 | ->hasConfigFile(); 35 | } 36 | 37 | public function packageRegistered(): void 38 | { 39 | $this->registerClient(); 40 | $this->registerWatchers(); 41 | } 42 | 43 | public function packageBooted(): void 44 | { 45 | $this->bootWatchers(); 46 | } 47 | 48 | protected function registerWatchers(): void 49 | { 50 | foreach (self::WATCHERS as $watcherClass) { 51 | $this->app->singleton($watcherClass); 52 | } 53 | } 54 | 55 | protected function registerClient(): void 56 | { 57 | $this->app->singleton(CappadociaViewerClient::class, function () { 58 | return new CappadociaViewerClient( 59 | new Client([ 60 | 'base_uri' => config('cappadocia-viewer.server_url'), 61 | 'timeout' => config('cappadocia-viewer.timeout'), 62 | ]) 63 | ); 64 | }); 65 | } 66 | 67 | protected function bootWatchers(): void 68 | { 69 | if (!config('cappadocia-viewer.enabled')) { 70 | return; 71 | } 72 | 73 | foreach (self::WATCHERS as $watcherClass) { 74 | /** @var Watcher $watcher */ 75 | $watcher = app($watcherClass); 76 | 77 | $watcher->register(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hsndmr/cappadocia-viewer-for-laravel", 3 | "description": "Cappadocia Viewer for Laravel", 4 | "keywords": [ 5 | "hsndmr", 6 | "laravel", 7 | "cappadocia-viewer-for-laravel" 8 | ], 9 | "homepage": "https://github.com/hsndmr/cappadocia-viewer-for-laravel", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Hasan Demir", 14 | "email": "demirhasanjs@gmail.com", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1", 20 | "spatie/laravel-package-tools": "^1.14.0", 21 | "illuminate/contracts": "^10.0" 22 | }, 23 | "require-dev": { 24 | "laravel/pint": "^1.0", 25 | "nunomaduro/collision": "^7.9", 26 | "nunomaduro/larastan": "^2.0.1", 27 | "orchestra/testbench": "^8.0", 28 | "pestphp/pest": "^2.0", 29 | "pestphp/pest-plugin-arch": "^2.0", 30 | "pestphp/pest-plugin-laravel": "^2.0", 31 | "phpstan/extension-installer": "^1.1", 32 | "phpstan/phpstan-deprecation-rules": "^1.0", 33 | "phpstan/phpstan-phpunit": "^1.0", 34 | "guzzlehttp/guzzle": "^7.6" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Hsndmr\\CappadociaViewer\\": "src/", 39 | "Hsndmr\\CappadociaViewer\\Database\\Factories\\": "database/factories/" 40 | }, 41 | "files": [ 42 | "src/helpers.php" 43 | ] 44 | }, 45 | "autoload-dev": { 46 | "psr-4": { 47 | "Hsndmr\\CappadociaViewer\\Tests\\": "tests/" 48 | } 49 | }, 50 | "scripts": { 51 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 52 | "analyse": "vendor/bin/phpstan analyse", 53 | "test": "vendor/bin/pest", 54 | "test-coverage": "vendor/bin/pest --coverage", 55 | "format": "vendor/bin/pint" 56 | }, 57 | "config": { 58 | "sort-packages": true, 59 | "allow-plugins": { 60 | "pestphp/pest-plugin": true, 61 | "phpstan/extension-installer": true 62 | } 63 | }, 64 | "extra": { 65 | "laravel": { 66 | "providers": [ 67 | "Hsndmr\\CappadociaViewer\\CappadociaViewerServiceProvider" 68 | ], 69 | "aliases": { 70 | "CappadociaViewer": "Hsndmr\\CappadociaViewer\\Facades\\CappadociaViewer" 71 | } 72 | } 73 | }, 74 | "minimum-stability": "dev", 75 | "prefer-stable": true 76 | } 77 | -------------------------------------------------------------------------------- /src/CappadociaViewer.php: -------------------------------------------------------------------------------- 1 | type = $type; 25 | 26 | return $this; 27 | } 28 | 29 | public function setBadgeType(?BadgeType $badgeType): self 30 | { 31 | $this->badgeType = $badgeType; 32 | 33 | return $this; 34 | } 35 | 36 | public function setBadge(?string $badge): self 37 | { 38 | $this->badge = $badge; 39 | 40 | return $this; 41 | } 42 | 43 | public function setMessage(string $message): self 44 | { 45 | $this->message = $message; 46 | 47 | return $this; 48 | } 49 | 50 | protected function resetData(): void 51 | { 52 | $this->type = ViewerType::LOG; 53 | $this->badgeType = null; 54 | $this->badge = null; 55 | $this->message = ''; 56 | } 57 | 58 | public function watchQueries(): self 59 | { 60 | app(QueryWatcher::class)->watch(); 61 | 62 | return $this; 63 | } 64 | 65 | public function stopWatchingQueries(): self 66 | { 67 | app(QueryWatcher::class)->stopWatching(); 68 | 69 | return $this; 70 | } 71 | 72 | public function watchJobs(): self 73 | { 74 | app(JobWatcher::class)->watch(); 75 | 76 | return $this; 77 | } 78 | 79 | public function stopWatchingJobs(): self 80 | { 81 | app(JobWatcher::class)->stopWatching(); 82 | 83 | return $this; 84 | } 85 | 86 | public function send(array $context = []): void 87 | { 88 | $viewerDto = new ViewerDto( 89 | type: $this->type, 90 | message: $this->message, 91 | badge: $this->badge, 92 | badgeType: $this->badgeType, 93 | context: !empty($context) ? $context : null, 94 | ); 95 | 96 | try { 97 | app(CappadociaViewerClient::class)->send($viewerDto->toArray()); 98 | } catch (Throwable) { 99 | $this->isServerAvailable = false; 100 | } 101 | 102 | $this->resetData(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "rules": { 4 | "declare_strict_types": true, 5 | "not_operator_with_successor_space": false, 6 | "binary_operator_spaces": { 7 | "default": "single_space", 8 | "operators": { 9 | "=>": "align_single_space_minimal", 10 | "=": "align_single_space_minimal" 11 | } 12 | }, 13 | "ordered_imports": { 14 | "sort_algorithm": "length" 15 | }, 16 | "phpdoc_summary": true, 17 | "phpdoc_to_comment": { 18 | "ignored_tags": [ 19 | "see" 20 | ] 21 | }, 22 | "phpdoc_separation": { 23 | "groups": [ 24 | [ 25 | "deprecated", 26 | "link", 27 | "see", 28 | "since" 29 | ], 30 | [ 31 | "author", 32 | "copyright", 33 | "license" 34 | ], 35 | [ 36 | "category", 37 | "package", 38 | "subpackage" 39 | ], 40 | [ 41 | "property", 42 | "property-read", 43 | "property-write" 44 | ], 45 | [ 46 | "param" 47 | ], 48 | [ 49 | "return" 50 | ], 51 | [ 52 | "throws" 53 | ] 54 | ] 55 | }, 56 | "phpdoc_line_span": { 57 | "const": "single", 58 | "property": "single" 59 | }, 60 | "phpdoc_var_annotation_correct_order": true, 61 | "php_unit_fqcn_annotation": true, 62 | "php_unit_method_casing": { 63 | "case": "snake_case" 64 | }, 65 | "void_return": true, 66 | "explicit_string_variable": true, 67 | "method_chaining_indentation": true, 68 | "curly_braces_position": { 69 | "control_structures_opening_brace": "same_line", 70 | "functions_opening_brace": "next_line_unless_newline_at_signature_end", 71 | "anonymous_functions_opening_brace": "same_line", 72 | "classes_opening_brace": "next_line_unless_newline_at_signature_end", 73 | "anonymous_classes_opening_brace": "same_line", 74 | "allow_single_line_empty_anonymous_classes": true, 75 | "allow_single_line_anonymous_functions": true 76 | }, 77 | "class_attributes_separation": { 78 | "elements": { 79 | "const": "one", 80 | "method": "one", 81 | "property": "none", 82 | "trait_import": "none" 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Watchers/JobWatcher.php: -------------------------------------------------------------------------------- 1 | shouldHandleJob($event->job)) { 30 | return; 31 | } 32 | 33 | CappadociaViewer::setMessage($event->job->resolveName()) 34 | ->setType(ViewerType::JOB) 35 | ->setBadge('processing') 36 | ->send([ 37 | 'data' => $this->getJobData($event->job), 38 | ]); 39 | 40 | } 41 | 42 | public function handleJobProcessed(JobProcessed $event): void 43 | { 44 | if (!$this->shouldHandleJob($event->job)) { 45 | return; 46 | } 47 | 48 | CappadociaViewer::setMessage($event->job->resolveName()) 49 | ->setType(ViewerType::JOB) 50 | ->setBadgeType(BadgeType::SUCCESS) 51 | ->setBadge('processed') 52 | ->send([ 53 | 'data' => $this->getJobData($event->job), 54 | ]); 55 | } 56 | 57 | public function handleJobFailed(JobFailed $event): void 58 | { 59 | if (!$this->shouldHandleJob($event->job)) { 60 | return; 61 | } 62 | 63 | CappadociaViewer::setMessage($event->job->resolveName()) 64 | ->setType(ViewerType::JOB) 65 | ->setBadgeType(BadgeType::ERROR) 66 | ->setBadge('failed') 67 | ->send([ 68 | 'data' => $this->getJobData($event->job), 69 | 'exception_message' => $event->exception->getMessage(), 70 | 'exception_file' => $event->exception->getFile().':'.$event->exception->getLine(), 71 | 'exception_trace' => $event->exception->getTraceAsString(), 72 | ]); 73 | } 74 | 75 | protected function isTelescopeJob($jobName): bool 76 | { 77 | return Str::startsWith($jobName, 'Laravel\Telescope'); 78 | } 79 | 80 | protected function getJobData(Job $job): array 81 | { 82 | return ExtractProperties::from(unserialize($job->payload()['data']['command'])); 83 | } 84 | 85 | protected function shouldHandleJob(Job $job): bool 86 | { 87 | if (!$this->isWatching()) { 88 | return false; 89 | } 90 | 91 | if ($this->isTelescopeJob($this->getJobName($job))) { 92 | return false; 93 | } 94 | 95 | return true; 96 | } 97 | 98 | protected function getJobName(Job $job): string 99 | { 100 | return $job->resolveName(); 101 | } 102 | 103 | protected function getConfigName(): string 104 | { 105 | return 'jobs'; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Watchers/RequestWatcher.php: -------------------------------------------------------------------------------- 1 | isWatching()) { 30 | return; 31 | } 32 | 33 | $data = $this->getData($event); 34 | 35 | CappadociaViewer::setMessage($data['uri']) 36 | ->setBadge($data['method']) 37 | ->setType(ViewerType::REQUEST) 38 | ->send($data); 39 | } 40 | 41 | protected function getData(RequestHandled $event): array 42 | { 43 | $startTime = defined('LARAVEL_START') ? LARAVEL_START : $event->request->server('REQUEST_TIME_FLOAT'); 44 | 45 | return [ 46 | 'status' => [ 47 | (string) $event->response->getStatusCode(), 48 | ], 49 | 'response' => $this->formatResponse($event->response), 50 | 'headers' => $event->request->headers->all(), 51 | 'payload' => $this->extractInput($event->request), 52 | 'duration' => $startTime ? floor((microtime(true) - $startTime) * 1000).' ms' : null, 53 | 'ip' => $event->request->ip(), 54 | 'session' => $this->getSessions($event->request), 55 | 'controller' => optional($event->request->route())->getActionName(), 56 | 'middleware' => array_values(optional($event->request->route())->gatherMiddleware() ?? []), 57 | 'memory' => round(memory_get_peak_usage(true) / 1024 / 1024, 1), 58 | 'uri' => str_replace($event->request->root(), '', $event->request->fullUrl()) ?: '/', 59 | 'method' => $event->request->method(), 60 | ]; 61 | } 62 | 63 | protected function extractInput(Request $request): array 64 | { 65 | $files = $request->files->all(); 66 | 67 | array_walk_recursive($files, function (&$file): void { 68 | $file = [ 69 | 'name' => $file->getClientOriginalName(), 70 | 'size' => $file->isFile() ? ($file->getSize() / 1000).'KB' : '0', 71 | ]; 72 | }); 73 | 74 | return array_replace_recursive($request->input(), $files); 75 | } 76 | 77 | protected function formatResponse(Response $response): array|string 78 | { 79 | $content = $response->getContent(); 80 | 81 | if (is_string($content)) { 82 | $contentJson = json_decode($content, true); 83 | 84 | if (is_array($contentJson) && json_last_error() === JSON_ERROR_NONE) { 85 | return $contentJson; 86 | } 87 | 88 | if (Str::startsWith(strtolower($response->headers->get('Content-Type') ?? ''), 'text/plain')) { 89 | return $content; 90 | } 91 | } 92 | 93 | if ($response instanceof RedirectResponse) { 94 | return 'Redirected to '.$response->getTargetUrl(); 95 | } 96 | 97 | if ($response instanceof IlluminateResponse && $response->getOriginalContent() instanceof View) { 98 | return [ 99 | 'view' => $response->getOriginalContent()->getPath(), 100 | 'data' => $this->extractDataFromView($response->getOriginalContent()), 101 | ]; 102 | } 103 | 104 | if (is_string($content) && empty($content)) { 105 | return 'Empty Response'; 106 | } 107 | 108 | return 'HTML Response'; 109 | } 110 | 111 | protected function extractDataFromView($view): array 112 | { 113 | return collect($view->getData())->map(function ($value) { 114 | if ($value instanceof Model) { 115 | return FormatModel::given($value); 116 | } elseif (is_object($value)) { 117 | return [ 118 | 'class' => get_class($value), 119 | 'properties' => json_decode(json_encode($value), true), 120 | ]; 121 | } else { 122 | return json_decode(json_encode($value), true); 123 | } 124 | })->toArray(); 125 | } 126 | 127 | protected function getSessions(Request $request): array 128 | { 129 | return $request->hasSession() ? $request->session()->all() : []; 130 | } 131 | 132 | protected function getConfigName(): string 133 | { 134 | return 'requests'; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cappadocia Viewer for Laravel 2 | 3 | Cappadocia Viewer for Laravel 4 | 5 | 6 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/hsndmr/cappadocia-viewer-for-laravel.svg?style=flat-square)](https://packagist.org/packages/hsndmr/cappadocia-viewer-for-laravel) 7 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/hsndmr/cappadocia-viewer-for-laravel/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/hsndmr/cappadocia-viewer-for-laravel/actions?query=workflow%3Arun-tests+branch%3Amain) 8 | [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/hsndmr/cappadocia-viewer-for-laravel/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/hsndmr/cappadocia-viewer-for-laravel/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) 9 | [![Total Downloads](https://img.shields.io/packagist/dt/hsndmr/cappadocia-viewer-for-laravel.svg?style=flat-square)](https://packagist.org/packages/hsndmr/cappadocia-viewer-for-laravel) 10 | 11 | 12 | ## Installation 13 | 14 | Prior to installing this package, ensure that you have already installed [Cappadocia Viewer](https://github.com/hsndmr/cappadocia-viewer/releases/tag/0.1.0) 15 | 16 | You can install the package via composer: 17 | 18 | ```bash 19 | composer require hsndmr/cappadocia-viewer-for-laravel --dev 20 | ``` 21 | 22 | You can publish the config file with: 23 | 24 | ```bash 25 | php artisan vendor:publish --tag="cappadocia-viewer" 26 | ``` 27 | 28 | This is the contents of the published config file: 29 | 30 | ```php 31 | return [ 32 | 'server_url' => env('CAPPADOCIA_VIEWER_SERVER_URL', 'http://127.0.0.1:9091'), 33 | 'timeout' => env('CAPPADOCIA_VIEWER_TIMEOUT', 3), 34 | 'enabled' => env('CAPPADOCIA_VIEWER_ENABLED', true), 35 | 'watch_logs' => env('CAPPADOCIA_VIEWER_WATCH_LOGS', true), 36 | 'watch_jobs' => env('CAPPADOCIA_VIEWER_WATCH_JOBS', false), 37 | 'watch_queries' => env('CAPPADOCIA_VIEWER_WATCH_QUERIES', false), 38 | ]; 39 | ``` 40 | 41 | ## Usage 42 | 43 | ### Showing Queries 44 | 45 | You can show queries by using `cappadocia` helper function. 46 | 47 | ````php 48 | cappadocia()->watchQueries(); 49 | User::latest()->first(); 50 | cappadocia()->stopWatchingQueries(); 51 | 52 | // This query will not be shown in the viewer 53 | User::first()->first(); 54 | ```` 55 | 56 | 57 | Cappadocia Viewer for Laravel 58 | 59 | 60 | If you want to show all queries, you can add `CAPPADOCIA_VIEWER_WATCH_QUERIES=true` to your .env file. 61 | 62 | ### Showing Jobs 63 | You can show jobs by using `cappadocia` helper function. 64 | 65 | ```` php 66 | cappadocia()->watchJobs(); 67 | CappadociaViewerJob::dispatchSync('viewer'); 68 | cappadocia()->stopWatchingJobs(); 69 | 70 | // This job will not be shown in the viewer 71 | CappadociaViewerJob::dispatchSync('another viewer'); 72 | ```` 73 | 74 | 75 | Cappadocia Viewer for Laravel 76 | 77 | 78 | If you are utilizing Laravel Horizon, you can insert `CAPPADOCIA_VIEWER_WATCH_JOBS=true` into your .env file. This will enable you to view all jobs in the viewer 79 | ```` php 80 | CappadociaViewerJob::dispatch('viewer'); 81 | ```` 82 | 83 | ### Showing Logs 84 | 85 | Logs are shown by default. If you want to disable it, you can add `CAPPADOCIA_VIEWER_WATCH_LOGS=false` to your .env file. 86 | ```` php 87 | Log::info('This log will be shown in the viewer'); 88 | ```` 89 | 90 | 91 | Cappadocia Viewer for Laravel 92 | 93 | 94 | 95 | ### Showing Requests 96 | 97 | If you want to show requests, you can add `CAPPADOCIA_VIEWER_WATCH_REQUESTS=true` to your .env file. After that, you can see all requests in the viewer. 98 | 99 | 100 | Cappadocia Viewer for Laravel 101 | 102 | 103 | ### Custom Messages 104 | 105 | If you want to show custom messages, you can use `cappadocia` helper function. 106 | 107 | ```` php 108 | cappadocia('This is custom message') 109 | ->send([ 110 | 'custom' => 'data', 111 | ]); 112 | 113 | cappadocia('This is custom message with Badge') 114 | ->setBadge('Badge') 115 | ->send([ 116 | 'custom' => 'data', 117 | ]); 118 | ```` 119 | 120 | 121 | Cappadocia Viewer for Laravel 122 | 123 | 124 | ### Disabling Cappadocia Viewer 125 | To disable the Cappadocia Viewer, you can include `CAPPADOCIA_VIEWER_ENABLED=false` in your .env file. This could be particularly useful if you wish to disable it within a testing environment. 126 | 127 | ## Testing 128 | 129 | ```bash 130 | composer test 131 | ``` 132 | 133 | ## Changelog 134 | 135 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 136 | 137 | ## Contributing 138 | 139 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 140 | 141 | ## Security Vulnerabilities 142 | 143 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 144 | 145 | ## Credits 146 | 147 | - [Hasan Demir](https://github.com/hsndmr) 148 | - [All Contributors](../../contributors) 149 | 150 | ## License 151 | 152 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 153 | --------------------------------------------------------------------------------