├── .github ├── FUNDING.yml └── dependabot.yml ├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── config └── laravel-query-monitor.php ├── demo.gif ├── phpunit.xml ├── src ├── Commands │ └── MonitorCommand.php ├── DispatchQueries.php ├── ListenQueries.php └── ServiceProvider.php └── tests └── LaravelQueryMonitorTest.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: supliutech 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "composer" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | .DS_Store 4 | .phpunit.result.cache -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | - 7.4 6 | 7 | before_script: 8 | - travis_retry composer self-update 9 | - travis_retry composer install --dev -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Query Monitor 2 | ![Packagist Downloads](https://img.shields.io/packagist/dt/supliu/laravel-query-monitor) 3 | ![Packagist Version](https://img.shields.io/packagist/v/supliu/laravel-query-monitor) 4 | 5 | Supliu Laravel Query Monitor is library to monitoring Queries in real-time using Laravel Artisan Command. Basically it opens a socket listening and displays (on terminal) the queries executed in your Laravel application. 6 | 7 |

8 | 9 |

10 | 11 | 12 | ## How to install 13 | 14 | 1) Use composer to install this package 15 | 16 | ``` 17 | composer require --dev supliu/laravel-query-monitor 18 | ``` 19 | 20 | 2) Run publish command 21 | 22 | ```ssh 23 | php artisan vendor:publish --provider="Supliu\LaravelQueryMonitor\ServiceProvider" 24 | ``` 25 | 26 | ## How to use 27 | 28 | Open you terminal and execute: 29 | 30 | ```ssh 31 | php artisan laravel-query-monitor 32 | ``` 33 | 34 | Now just perform some action in your application that performs some interaction with the database. 35 | 36 | ## Customize 37 | 38 | By default, the query listening service will run on host 0.0.0.0 and port 8081. You can customize both the host and the port by providing the optional arguments: 39 | 40 | ```ssh 41 | php artisan laravel-query-monitor --host="192.168.0.2" --port=8082 42 | ``` 43 | 44 | If you change the host and port parameters, you will also need to change the configuration file `config/laravel-query-monitor.php`. 45 | 46 | ## License 47 | 48 | The Laravel Query Monitor is open-sourced project licensed under the [MIT license](https://opensource.org/licenses/MIT). 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supliu/laravel-query-monitor", 3 | "type": "library", 4 | "description": "Laravel Query Monitor", 5 | "keywords": [ 6 | "laravel", "eloquent", "query", "sql", "monitor" 7 | ], 8 | "authors": [ 9 | { 10 | "name": "Jansen Felipe", 11 | "email": "jansen.felipe@gmail.com" 12 | } 13 | ], 14 | "license": "MIT", 15 | "autoload": { 16 | "psr-4": { 17 | "Supliu\\LaravelQueryMonitor\\": "src/" 18 | } 19 | }, 20 | "require": { 21 | "laravel/framework": "^5.6 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0", 22 | "php": "^7.3 || ^7.4 || ^8.0 || ^8.1 || ^8.2 || ^8.3", 23 | "react/socket": "^1.4" 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.0-dev" 28 | }, 29 | "laravel": { 30 | "providers": [ 31 | "Supliu\\LaravelQueryMonitor\\ServiceProvider" 32 | ] 33 | } 34 | }, 35 | "minimum-stability": "dev", 36 | "require-dev": { 37 | "phpunit/phpunit": "^9.2@dev", 38 | "orchestra/testbench": "^3.6 || ^4.0 || ^5.0 || ^6.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /config/laravel-query-monitor.php: -------------------------------------------------------------------------------- 1 | env('LARAVEL_QUERY_MONITOR', true), 5 | 'host' => env('LARAVEL_QUERY_MONITOR_HOST', '0.0.0.0'), 6 | 'port' => env('LARAVEL_QUERY_MONITOR_PORT', 8081), 7 | 8 | // The following options are used to filter the queries that are monitored. 9 | // Useful to ignore queries from Laravel Pulse, Telescope, etc. 10 | 'ignore_query_match' => [ 11 | '/pulse_entries|pulse_aggregates|pulse_values/i', 12 | '/telescope_entries|telescope_entries_tags|telescope_monitoring/i', 13 | ], 14 | ]; 15 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supliu/laravel-query-monitor/8acfd5aebe23e68295bda186bff20741e6822a04/demo.gif -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Commands/MonitorCommand.php: -------------------------------------------------------------------------------- 1 | option('host') ?? '0.0.0.0'; 42 | $port = $this->option('port') ?? '8081'; 43 | $debug = $this->option('debug') ?? false; 44 | $moreThanMiliseconds = $this->option('moreThanMiliseconds') ?? 0; 45 | 46 | $listenQueries = new ListenQueries($host, $port, $moreThanMiliseconds); 47 | 48 | $listenQueries->setInfo(function($message){ 49 | $this->info($message); 50 | }); 51 | 52 | $listenQueries->setWarn(function($message){ 53 | $this->warn($message); 54 | }); 55 | 56 | $listenQueries->setDebug($debug); 57 | 58 | $listenQueries->run(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/DispatchQueries.php: -------------------------------------------------------------------------------- 1 | host = $host; 34 | $this->port = $port; 35 | $this->loop = Factory::create(); 36 | $this->connector = new Connector($this->loop); 37 | } 38 | 39 | public function send($query): void 40 | { 41 | $this->connector 42 | ->connect($this->host . ':' . $this->port) 43 | ->then(function (ConnectionInterface $connection) use ($query) { 44 | $connection->write(json_encode($query)); 45 | }); 46 | 47 | $this->loop->run(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ListenQueries.php: -------------------------------------------------------------------------------- 1 | host = $host; 31 | $this->port = $port; 32 | $this->moreThanMiliseconds = $moreThanMiliseconds; 33 | $this->loop = Factory::create(); 34 | $this->socket = new Server($host.':'.$port, $this->loop); 35 | } 36 | 37 | public function setInfo(Closure $info) 38 | { 39 | $this->info = $info; 40 | } 41 | 42 | public function setWarn(Closure $warn) 43 | { 44 | $this->warn = $warn; 45 | } 46 | 47 | public function setDebug(bool $debug) 48 | { 49 | $this->debug = $debug; 50 | } 51 | 52 | public function run() 53 | { 54 | call_user_func($this->info, 'Listen SQL queries on '.$this->host.':'.$this->port . PHP_EOL . PHP_EOL); 55 | 56 | $this->socket->on('connection', function (ConnectionInterface $connection) { 57 | 58 | $connection->on('data', function ($data) use ($connection) { 59 | 60 | if($this->debug) 61 | call_user_func($this->warn, '# Debug:' . $data); 62 | 63 | $query = json_decode($data, true); 64 | 65 | 66 | if ($query === null) { 67 | call_user_func($this->warn, '# Something wrong happened with JSON data received: '); 68 | call_user_func($this->info, $data); 69 | } else { 70 | 71 | if($query['time'] > $this->moreThanMiliseconds) { 72 | 73 | call_user_func($this->warn, '# Query received:'); 74 | 75 | $bindings = $query['bindings'] ?? []; 76 | 77 | $normalizedBindings = array_map(function($i){ 78 | return is_string($i) ? '"'.$i.'"' : $i; 79 | }, $bindings); 80 | 81 | $sql = Str::replaceArray('?', $normalizedBindings, $query['sql']); 82 | 83 | call_user_func($this->info, '# SQL: ' . $sql); 84 | call_user_func($this->info, '# Miliseconds: ' . $query['time']); 85 | call_user_func($this->info, '# Seconds: ' . $query['time'] / 1000); 86 | call_user_func($this->info, PHP_EOL); 87 | } 88 | 89 | } 90 | 91 | 92 | $connection->close(); 93 | 94 | }); 95 | }); 96 | 97 | $this->loop->run(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 19 | $this->commands([ 20 | MonitorCommand::class, 21 | ]); 22 | } 23 | 24 | $this->publishes([ 25 | __DIR__.'/../config/laravel-query-monitor.php' => config_path('laravel-query-monitor.php'), 26 | ]); 27 | 28 | /* 29 | * Setting 30 | */ 31 | $host = config('laravel-query-monitor.host'); 32 | $port = config('laravel-query-monitor.port'); 33 | $enable = config('laravel-query-monitor.enable'); 34 | $ignore_query_match = config('laravel-query-monitor.ignore_query_match', []); 35 | 36 | if ($host && $port && $enable) { 37 | $dispatchQueries = new DispatchQueries($host, (int) $port); 38 | 39 | DB::listen(function ($query) use ($dispatchQueries, $ignore_query_match) { 40 | if (is_array($ignore_query_match) && !empty($ignore_query_match)) { 41 | foreach ($ignore_query_match as $ignore) { 42 | if (preg_match($ignore, $query->sql)) { 43 | return; 44 | } 45 | } 46 | } 47 | $dispatchQueries->send($query); 48 | }); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/LaravelQueryMonitorTest.php: -------------------------------------------------------------------------------- 1 | set('laravel-query-monitor.enable', true); 23 | $app['config']->set('laravel-query-monitor.host', '0.0.0.0'); 24 | $app['config']->set('laravel-query-monitor.port', 8082); 25 | } 26 | 27 | /** 28 | * @test 29 | */ 30 | public function runCommand() 31 | { 32 | // TODO 33 | } 34 | } --------------------------------------------------------------------------------