Browse Source

Add logging to error rendering failures

When error rendering fails it can be tedious to diagnose what the
problem is. Having some logging when rendering fails could help triage
the problem and get to a solution quicker.

Refs #17603
Mark Story 2 years ago
parent
commit
bcacc667cb

+ 19 - 0
src/Error/Renderer/WebExceptionRenderer.php

@@ -35,6 +35,7 @@ use Cake\Http\Response;
 use Cake\Http\ResponseEmitter;
 use Cake\Http\ServerRequest;
 use Cake\Http\ServerRequestFactory;
+use Cake\Log\Log;
 use Cake\Routing\Exception\MissingRouteException;
 use Cake\Routing\Router;
 use Cake\Utility\Inflector;
@@ -161,6 +162,7 @@ class WebExceptionRenderer implements ExceptionRendererInterface
             $request = $request->withAttribute('params', $routerRequest->getAttribute('params'));
         }
 
+        $class = "";
         try {
             $params = $request->getAttribute('params');
             $params['controller'] = 'Error';
@@ -185,6 +187,11 @@ class WebExceptionRenderer implements ExceptionRendererInterface
             $controller = new $class($request);
             $controller->startupProcess();
         } catch (Throwable $e) {
+            Log::warning(
+                "Failed to construct or call startup() on the resolved controller class of `$class`. " .
+                    "Using Fallback Controller instead. Error {$e->getMessage()}",
+                'cake.error'
+            );
             $controller = null;
         }
 
@@ -412,6 +419,10 @@ class WebExceptionRenderer implements ExceptionRendererInterface
 
             return $this->_shutdown();
         } catch (MissingTemplateException $e) {
+            Log::warning(
+                "MissingTemplateException - Failed to render error template `{$template}`. Error: {$e->getMessage()}",
+                'cake.error'
+            );
             $attributes = $e->getAttributes();
             if (
                 $e instanceof MissingLayoutException ||
@@ -422,6 +433,10 @@ class WebExceptionRenderer implements ExceptionRendererInterface
 
             return $this->_outputMessage('error500');
         } catch (MissingPluginException $e) {
+            Log::warning(
+                "MissingPluginException - Failed to render error template `{$template}`. Error: {$e->getMessage()}",
+                'cake.error'
+            );
             $attributes = $e->getAttributes();
             if (isset($attributes['plugin']) && $attributes['plugin'] === $this->controller->getPlugin()) {
                 $this->controller->setPlugin(null);
@@ -429,6 +444,10 @@ class WebExceptionRenderer implements ExceptionRendererInterface
 
             return $this->_outputMessageSafe('error500');
         } catch (Throwable $outer) {
+            Log::warning(
+                "Throwable - Failed to render error template `{$template}`. Error: {$outer->getMessage()}",
+                'cake.error'
+            );
             try {
                 return $this->_outputMessageSafe('error500');
             } catch (Throwable $inner) {

+ 1 - 0
templates/Error/missing_controller.php

@@ -22,6 +22,7 @@ $pluginDot = empty($plugin) ? null : $plugin . '.';
 $namespace = Configure::read('App.namespace');
 $prefixNs = $prefixPath = '';
 
+$controller = (string)$controller;
 $incompleteInflection = (str_contains($controller, '_') || str_contains($controller, '-'));
 $originalClass = $controller;
 

+ 2 - 1
tests/TestCase/Error/ExceptionTrapTest.php

@@ -260,7 +260,8 @@ class ExceptionTrapTest extends TestCase
         ob_get_clean();
 
         $logs = Log::engine('test_error')->read();
-        $this->assertEmpty($logs);
+        $this->assertCount(1, $logs);
+        $this->assertStringContainsString('MissingTemplateException - Failed to render', $logs[0]);
         $this->assertTrue($this->triggered, 'Should have triggered event when skipping logging.');
     }
 

+ 2 - 2
tests/TestCase/Error/Middleware/ErrorHandlerMiddlewareTest.php

@@ -318,7 +318,7 @@ class ErrorHandlerMiddlewareTest extends TestCase
         $request = ServerRequestFactory::fromGlobals();
         $middleware = new ErrorHandlerMiddleware(['log' => true]);
         $handler = new TestRequestHandler(function (): void {
-            throw new MissingControllerException(['class' => 'Articles']);
+            throw new MissingControllerException(['controller' => 'Articles']);
         });
         $result = $middleware->process($request, $handler);
         $this->assertSame(404, $result->getStatusCode());
@@ -329,7 +329,7 @@ class ErrorHandlerMiddlewareTest extends TestCase
             $logs[0]
         );
         $this->assertStringContainsString('Exception Attributes:', $logs[0]);
-        $this->assertStringContainsString("'class' => 'Articles'", $logs[0]);
+        $this->assertStringContainsString("'controller' => 'Articles'", $logs[0]);
         $this->assertStringContainsString('Request URL:', $logs[0]);
     }