From 543d886d97dd7645fa252455b63b58b74f5adce9 Mon Sep 17 00:00:00 2001 From: Gerald Villorente Date: Fri, 22 Nov 2019 11:33:04 +0800 Subject: [PATCH] Added src/Commands/PhpFpmErrorCommand.php to parse php-fpm-error.php --- src/Commands/PhpFpmErrorCommand.php | 290 ++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 src/Commands/PhpFpmErrorCommand.php diff --git a/src/Commands/PhpFpmErrorCommand.php b/src/Commands/PhpFpmErrorCommand.php new file mode 100644 index 0000000..e32b878 --- /dev/null +++ b/src/Commands/PhpFpmErrorCommand.php @@ -0,0 +1,290 @@ +logPath = getenv('HOME') . '/.terminus/site-logs'; + } + + /** + * Parse php-fpm-error.log. + * + * @command logs:parse:php-fpm + * @aliases lp:php-fpm lp:pfe + * + * @param string $site_env The site name and site environment. Example: foo.dev for Dev environment, foo.test for Test environment, and foo.live for Live environment. + * @option php Parse the logs via PHP. + * @option shell Parse the logs using *nix built-in tools. + * @option newrelic Shows NewRelic summary report. + * @option filter Equivalent to "head -N" where "N" is a numeric value. By default the value is 10 which will return the latest 10 entries. + * + * @usage . --grouped-by="{KEYWORD}" + * + * Search for the latest entries. + * terminus logs:parse:php-fpm . --grouped-by=latest + */ + public function ParsePhpFpmErrorCommand($site_env, $options = ['php' => false, 'shell' => true, 'newrelic' => false, 'grouped-by' => 'latest', 'uri' => '', 'filter' => 20, 'since' => '', 'until' => '', 'method' => '']) + { + // Get the site name and environment. + $this->DefineSiteEnv($site_env); + $site = $this->site->get('name'); + $env = $this->environment->id; + + if ($this->logPath . '/' . $site . '/' . $env) + { + $this->LogParser($site_env, $options); + + if ($options['newrelic']) + { + $this->output()->writeln(''); + $this->output()->writeln('Fetching NewRelic data.....'); + $this->output()->writeln(''); + $this->NewRelicHealthCheck($site_env); + } + exit(); + } + + $this->log()->error("No data found. Please run terminus logs:get $site.$env command."); + } + + /** + * Log parser. + */ + private function LogParser($site_env, $options) + { + // Define site and environment. + $this->DefineSiteEnv($site_env); + $site = $this->site->get('name'); + $env = $this->environment->id; + + //$this->checkSiteHeaders($site_env); + //exit(); + + // Get the logs per environment. + $dirs = array_filter(glob($this->logPath . '/' . $site . '/' . $env . '/*'), 'is_dir'); + + if (!$options['php']) + { + $this->log()->warning('This operation requires *nix commands like grep, cut, sort, uniq, and tail.'); + switch ($options['grouped-by']) + { + case 'ip': + $this->log()->notice('Top visitors by IP.'); + break; + case 'response-code': + $this->log()->notice('Top access by response code.'); + break; + case '403': + $this->log()->notice('Top 403 requests.'); + break; + case '404': + $this->log()->notice('Top 404 requests.'); + break; + case '502': + $this->log()->notice('Top 502 requests.'); + break; + case 'ip-accessing-502': + $this->log()->notice('Top 502 requests by IP.'); + break; + case 'most-requested-urls': + $this->log()->notice('Top most requested urls.'); + break; + case 'php-404': + $this->log()->notice('Top PHP 404 requests.'); + break; + case 'php-404-detailed': + $this->log()->notice('Full details of top PHP 404 requests.'); + break; + case 'request-per-second': + $this->log()->notice("Requests per second. These are the requests was able to bypass Global CDN."); + break; + case 'request-method': + $this->log()->notice('Top requests by HTTP code.'); + break; + case 'time': + $this->log()->notice('Count of queries based on their time of execution. The first column is the total number of queries and the second column is the timestamp.'); + default: + // Do nothing. + } + } + + // Scanner storage. + $container = []; + + foreach ($dirs as $dir) + { + if (!$options['php'] && $options['shell']) + { + if (file_exists($dir . '/php-fpm-error.log')) + { + $this->ParsePhpFpmErrorLog($dir, $options); + } + } + else if ($options['php'] && $options['filter']) + { + $log = $dir . '/php-fpm-error.log'; + + if (file_exists($log)) + { + $handle = fopen($log, 'r'); + + if ($handle) + { + while (!feof($handle)) + { + $buffer = fgets($handle); + + if (!empty($options['since'])) + { + if (strpos($buffer, $options['filter']) !== FALSE && strpos($buffer, $options['since'])) + { + $container[$log][] = $buffer; + } + } + else + { + if (strpos($buffer, $options['filter']) !== FALSE) + { + $container[$log][] = $buffer; + } + } + } + fclose($handle); + } + } + } + else + { + $this->log()->notice("Nothing to process."); + exit(); + } + } + + // Return the matches. + if ($options['php']) + { + if (is_array(@$container)) + { + $count = []; + + foreach ($container as $i => $matches) + { + $this->output()->writeln("From " . $i . " file."); + $this->output()->writeln($this->line('=')); + + foreach ($matches as $match) + { + $count[] = $match; + $this->output()->writeln($match); + $this->output()->writeln($this->line('-')); + } + } + $this->log()->notice(sizeof($count) . " " . ((sizeof($count) > 1) ? 'results' : 'result') . " matched found."); + } + else + { + $this->log()->notice("No matches found."); + } + } + } + + /** + * Parse PHP slow logs. + */ + private function ParsePhpFpmErrorLog($dir, $options) + { + $php_fpm_error_log = $dir . '/php-fpm-error.log'; + $this->output()->writeln("From " . $php_fpm_error_log . " file."); + + if ($this->CheckNixTools()) + { + switch ($options['grouped-by']) + { + case 'debug': + break; + default: + $this->passthru("tail $php_fpm_error_log | head -{$options['filter']}"); + } + } + } + + /** + * Define site environment properties. + * + * @param string $site_env Site and environment in a format of .. + */ + private function DefineSiteEnv($site_env) + { + list($this->site, $this->environment) = $this->getSiteEnv($site_env); + } + + /** + * Passthru command. + */ + protected function passthru($command) + { + $result = 0; + passthru($command, $result); + + if ($result != 0) + { + throw new TerminusException('Command `{command}` failed with exit code {status}', ['command' => $command, 'status' => $result]); + } + } + + /** + * Check if required tools are installed. + */ + public function CheckNixTools() + { + $commands = ['cat', 'grep', 'tail', 'cut', 'uniq', 'sort', 'awk']; + + $results = []; + foreach ($commands as $command) + { + $results[] = shell_exec("command -v $command"); + } + $container = array_search('', $results); + if ($container) + { + $this->log()->notice('Some of the tools required is not installed.'); + return FALSE; + } + else + { + return TRUE; + } + } +} \ No newline at end of file