From d14e21e4ac3683b812a7930c167ec18d6de1a00a Mon Sep 17 00:00:00 2001 From: Etienne Trimaille Date: Tue, 3 Dec 2024 10:14:58 +0100 Subject: [PATCH 1/3] Admin - Log OGC request if error --- .../modules/lizmap/lib/Request/OGCRequest.php | 55 +++++++++++++++++++ lizmap/modules/lizmap/lib/Request/Proxy.php | 12 ++-- .../en_US/dictionnary.UTF-8.properties | 2 +- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Request/OGCRequest.php b/lizmap/modules/lizmap/lib/Request/OGCRequest.php index 5093b92b3d..a4b8a7c8fe 100644 --- a/lizmap/modules/lizmap/lib/Request/OGCRequest.php +++ b/lizmap/modules/lizmap/lib/Request/OGCRequest.php @@ -185,6 +185,57 @@ protected function constructUrl() return Proxy::constructUrl($this->parameters(), $this->services, $url); } + /** + * Generate a string to identify the target of the HTTP request. + * + * @param array $params The list of HTTP params + * @param int $code The HTTP code of the request + * + * @return string The string to identify the HTTP request, with main OGC parameters first such as MAP, SERVICE... + */ + private function formatHttpErrorString($params, $code) + { + $message = ''; + + $paramsToLog = array('map', 'repository', 'project', 'service', 'request'); + foreach ($paramsToLog as $paramName) { + if (array_key_exists($paramName, $params)) { + $message .= strtoupper($paramName)." '".$params[$paramName]."', "; + } + } + + $message = rtrim($message, ', '); + + return 'HTTP code '.$code.' on '.$message; + } + + /** + * Log if the HTTP code is a 4XX or 5XX error code. + * + * @param int $code The HTTP code of the request + */ + protected function logRequestIfError($code) + { + if ($code < 400) { + return; + } + + $message = 'The HTTP OGC request to QGIS Server ended with an error.'; + + // The master error with MAP parameter + // This user must have an access to QGIS Server logs + $params = $this->parameters(); + \jLog::log($message.' Check logs on QGIS Server. '.$this->formatHttpErrorString($params, $code).' : '.json_encode($params), 'error'); + + // The admin error without the MAP parameter + // but replaced by REPOSITORY and PROJECT parameters + // This user might not have an access to QGIS Server logs + unset($params['map']); + $params['repository'] = $this->project->getRepository()->getKey(); + $params['project'] = $this->project->getKey(); + \jLog::log($message.' '.$this->formatHttpErrorString($params, $code).' : '.json_encode($params), 'lizmapadmin'); + } + /** * Request QGIS Server. * @@ -236,11 +287,15 @@ protected function request($post = false, $stream = false) if ($stream) { $response = \Lizmap\Request\Proxy::getRemoteDataAsStream($querystring, $options); + $this->logRequestIfError($response->getCode()); + return new OGCResponse($response->getCode(), $response->getMime(), $response->getBodyAsStream()); } list($data, $mime, $code) = \Lizmap\Request\Proxy::getRemoteData($querystring, $options); + $this->logRequestIfError($code); + return new OGCResponse($code, $mime, $data); } diff --git a/lizmap/modules/lizmap/lib/Request/Proxy.php b/lizmap/modules/lizmap/lib/Request/Proxy.php index 5a912b72f5..de3d433e1f 100644 --- a/lizmap/modules/lizmap/lib/Request/Proxy.php +++ b/lizmap/modules/lizmap/lib/Request/Proxy.php @@ -489,14 +489,14 @@ protected static function fileProxy($url, $options) * @param int $httpCode The HTTP code of the request * @param string $url The URL of the request, for logging */ - public static function logRequestIfError($httpCode, $url) + protected static function logRequestIfError($httpCode, $url) { - $httpCodeClass = substr($httpCode, 0, 1); - // Change to str_starts_with when PHP 8.1 will be minimum version for all maintained version - if ($httpCodeClass == '4' || $httpCodeClass == '5') { - \jLog::log('An HTTP request ended with an error, please check the main error log. Code '.$httpCode, 'lizmapadmin'); - \jLog::log('The HTTP request below ended with an error. Code '.$httpCode.' → '.$url, 'error'); + if ($httpCode < 400) { + return; } + + \jLog::log('An HTTP request ended with an error, please check the main error log. HTTP code '.$httpCode, 'lizmapadmin'); + \jLog::log('The HTTP request ended with an error. HTTP code '.$httpCode.' → '.$url, 'error'); } /** diff --git a/lizmap/modules/view/locales/en_US/dictionnary.UTF-8.properties b/lizmap/modules/view/locales/en_US/dictionnary.UTF-8.properties index b31e342c28..c6e52b4a84 100644 --- a/lizmap/modules/view/locales/en_US/dictionnary.UTF-8.properties +++ b/lizmap/modules/view/locales/en_US/dictionnary.UTF-8.properties @@ -1,6 +1,6 @@ startup.error=An error occurred while loading this map. Some necessary resources may temporarily be unavailable. Please try again later. startup.error.administrator=Please notify the administrator of this map to connect and visit this map. -startup.error.developer.tools=Maybe you will have a clue by opening your "Developer tools" in your web-browser. It's usually F12. Check "Console" and "Networks". +startup.error.developer.tools=Maybe you will have a clue by opening your "Developer tools" in your web-browser. It's usually F12. Check "Console" and "Networks".\n\nVisit the administration panel and check in the log page if an HTTP request ended with an error related to this project. startup.user_defined_js=This map contains some users additional JavaScript scripts. This can be the cause of this message, try disabling them. startup.goToProject=Home startup.goToRepositoryAdmin=Maps management page From 1c73f74a5256be67a98ea264dfc5a03b9ed8eb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Trimaille?= Date: Tue, 3 Dec 2024 14:38:32 +0100 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: rldhont --- .../modules/lizmap/lib/Request/OGCRequest.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Request/OGCRequest.php b/lizmap/modules/lizmap/lib/Request/OGCRequest.php index a4b8a7c8fe..59cc1eddf9 100644 --- a/lizmap/modules/lizmap/lib/Request/OGCRequest.php +++ b/lizmap/modules/lizmap/lib/Request/OGCRequest.php @@ -195,16 +195,18 @@ protected function constructUrl() */ private function formatHttpErrorString($params, $code) { - $message = ''; - - $paramsToLog = array('map', 'repository', 'project', 'service', 'request'); - foreach ($paramsToLog as $paramName) { + $mainParamsToLog = array('map', 'repository', 'project', 'service', 'request'); + $mainParamValues = array(); + $otherParamValues = array(); + foreach ($mainParamsToLog as $paramName) { if (array_key_exists($paramName, $params)) { - $message .= strtoupper($paramName)." '".$params[$paramName]."', "; + $mainParamValues[] = '"'.strtoupper($paramName).'"'. ' = ' ."'".$params[$paramName]."'"; + } else { + $otherParamValues[] = '"'.strtoupper($paramName).'"'. ' = ' ."'".$params[$paramName]."'"; } } - $message = rtrim($message, ', '); + $message = implode(' & ', $mainParamvalues) .'\n'. implode(' & ', $otherParamvalues); return 'HTTP code '.$code.' on '.$message; } @@ -225,7 +227,7 @@ protected function logRequestIfError($code) // The master error with MAP parameter // This user must have an access to QGIS Server logs $params = $this->parameters(); - \jLog::log($message.' Check logs on QGIS Server. '.$this->formatHttpErrorString($params, $code).' : '.json_encode($params), 'error'); + \jLog::log($message.' Check logs on QGIS Server. '.$this->formatHttpErrorString($params, $code), 'error'); // The admin error without the MAP parameter // but replaced by REPOSITORY and PROJECT parameters @@ -233,7 +235,7 @@ protected function logRequestIfError($code) unset($params['map']); $params['repository'] = $this->project->getRepository()->getKey(); $params['project'] = $this->project->getKey(); - \jLog::log($message.' '.$this->formatHttpErrorString($params, $code).' : '.json_encode($params), 'lizmapadmin'); + \jLog::log($message.' '.$this->formatHttpErrorString($params, $code), 'lizmapadmin'); } /** From 5d222a0568bcc1acda04ad168ff7be137eec0d3b Mon Sep 17 00:00:00 2001 From: Etienne Trimaille Date: Tue, 3 Dec 2024 15:05:42 +0100 Subject: [PATCH 3/3] Fix how to log HTTP request having an error --- .../modules/lizmap/lib/Request/OGCRequest.php | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Request/OGCRequest.php b/lizmap/modules/lizmap/lib/Request/OGCRequest.php index 59cc1eddf9..ee32f39dae 100644 --- a/lizmap/modules/lizmap/lib/Request/OGCRequest.php +++ b/lizmap/modules/lizmap/lib/Request/OGCRequest.php @@ -188,25 +188,44 @@ protected function constructUrl() /** * Generate a string to identify the target of the HTTP request. * - * @param array $params The list of HTTP params - * @param int $code The HTTP code of the request + * @param array $parameters The list of HTTP parameters in the query + * @param int $code The HTTP code of the request * * @return string The string to identify the HTTP request, with main OGC parameters first such as MAP, SERVICE... */ - private function formatHttpErrorString($params, $code) + private function formatHttpErrorString($parameters, $code) { + // Clone parameters array to perform unset without modify it + $params = array_merge(array(), $parameters); + + // Ordered list of params to fetch first $mainParamsToLog = array('map', 'repository', 'project', 'service', 'request'); - $mainParamValues = array(); - $otherParamValues = array(); + + $output = array(); foreach ($mainParamsToLog as $paramName) { if (array_key_exists($paramName, $params)) { - $mainParamValues[] = '"'.strtoupper($paramName).'"'. ' = ' ."'".$params[$paramName]."'"; - } else { - $otherParamValues[] = '"'.strtoupper($paramName).'"'. ' = ' ."'".$params[$paramName]."'"; + $output[] = '"'.strtoupper($paramName).'" = '."'".$params[$paramName]."'"; + unset($params[$paramName]); } } - $message = implode(' & ', $mainParamvalues) .'\n'. implode(' & ', $otherParamvalues); + // First implode with main parameters + $message = implode(' & ', $output); + + if ($params) { + // Ideally, we want two lines, one with main parameters, the second one with secondary parameters + // It does not work in jLog + // $message .= '\n'; + $message .= ' & '; + } + + // For remaining parameters in the array, which are not in the main list + $output = array(); + foreach ($params as $key => $value) { + $output[] = '"'.strtoupper($key).'" = '."'".$value."'"; + } + + $message .= implode(' & ', $output); return 'HTTP code '.$code.' on '.$message; }