The error log of my CakePHP app is full of 404 errors. Can I exclude these MissingControllerException
s from appearing in the error log? Using Cake 2.3.
Simply redirecting or removing these URLs is not going to cut it.
A busy site will get hit by hundreds of "random" 404s every day, most from far east countries checking for exploits or URLs such as "/wp-admin".
Logging these with a full stack trace is completely unnecessary
Solution
You can override the default error handler in CakePHP, and log to app/tmp/logs/404.log
instead.
In your app/Config/core.php
file, define the class you want to handle exceptions:
Configure::write('Error', array(
'handler' => 'MyCustomErrorHandler::handleError',
'level' => E_ALL & ~E_DEPRECATED,
'trace' => true
));
Create the class within app/Lib/Error
and include using App::uses
in your app/Config/bootstrap.php
file:
App::uses('MyCustomErrorHandler', 'Lib/Error');
Make an exact copy of the original ErrorHandler class, just change the class name, and somewhere within the handleException method check which exception is being thrown, and log somewhere else. It will look a bit like this;
App::uses('ErrorHandler', 'Error');
class MyCustomErrorHandler {
public static function handleException(Exception $exception) {
// some code...
if (in_array(get_class($exception), array('MissingControllerException', 'MissingActionException', 'PrivateActionException', 'NotFoundException'))) {
$log = '404';
$message = sprintf("[%s]", get_class($exception));
}
// more code...
}
}
versions above 2.4 (as mentioned by null) have an undocumented feature for this purpose.
just modify your exception config:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'ExceptionRenderer',
'log' => true,
'skipLog'=>array(
'MissingControllerException'
)
));
skipLog takes names of exception classes to be excluded from log.
New configuration option skipLog has been added, to allow skipping certain Exception types to be logged. Configure::write('Exception.skipLog', array('NotFoundException', 'ForbiddenException')); will avoid these exceptions and the ones extending them to be be logged when 'Exception.log' config is true
–
Conjuration Simply redirecting or removing these URLs is not going to cut it.
A busy site will get hit by hundreds of "random" 404s every day, most from far east countries checking for exploits or URLs such as "/wp-admin".
Logging these with a full stack trace is completely unnecessary
Solution
You can override the default error handler in CakePHP, and log to app/tmp/logs/404.log
instead.
In your app/Config/core.php
file, define the class you want to handle exceptions:
Configure::write('Error', array(
'handler' => 'MyCustomErrorHandler::handleError',
'level' => E_ALL & ~E_DEPRECATED,
'trace' => true
));
Create the class within app/Lib/Error
and include using App::uses
in your app/Config/bootstrap.php
file:
App::uses('MyCustomErrorHandler', 'Lib/Error');
Make an exact copy of the original ErrorHandler class, just change the class name, and somewhere within the handleException method check which exception is being thrown, and log somewhere else. It will look a bit like this;
App::uses('ErrorHandler', 'Error');
class MyCustomErrorHandler {
public static function handleException(Exception $exception) {
// some code...
if (in_array(get_class($exception), array('MissingControllerException', 'MissingActionException', 'PrivateActionException', 'NotFoundException'))) {
$log = '404';
$message = sprintf("[%s]", get_class($exception));
}
// more code...
}
}
Building on robmcvey's solution, here is what I had to do for CakePHP 2.6.
Configuration
In app/Config/core.php
update the Exception configuration:
Configure::write('Exception', array(
'handler' => 'AppErrorHandler::handleException',
'renderer' => 'ExceptionRenderer',
'log' => true
));
In app/Config/bootstrap.php
add a CakeLog configuration:
CakeLog::config('not_found', array(
'engine' => 'FileLog',
'types' => array('404'),
'file' => '404',
));
The type
of 404
is the logging level it will listen in on for anything being written to logs e.g. CakeLog::write('404', 'That was not found.');
Extend ErrorHandler
Create the file app/Lib/Error/AppErrorHandler.php
. Here we will extend Cake's ErrorHandler
, overwritting three methods; handleException()
, _getMessage()
, and _log()
.
<?php
class AppErrorHandler extends ErrorHandler {
/**
* List of Cake Exception classes to record to specified log level.
*
* @var array
*/
protected static $_exceptionClasses = array(
'MissingControllerException' => '404',
'MissingActionException' => '404',
'PrivateActionException' => '404',
'NotFoundException' => '404'
);
public static function handleException(Exception $exception) {
$config = Configure::read('Exception');
self::_log($exception, $config);
$renderer = isset($config['renderer']) ? $config['renderer'] : 'ExceptionRenderer';
if ($renderer !== 'ExceptionRenderer') {
list($plugin, $renderer) = pluginSplit($renderer, true);
App::uses($renderer, $plugin . 'Error');
}
try {
$error = new $renderer($exception);
$error->render();
} catch (Exception $e) {
set_error_handler(Configure::read('Error.handler')); // Should be using configured ErrorHandler
Configure::write('Error.trace', false); // trace is useless here since it's internal
$message = sprintf("[%s] %s\n%s", // Keeping same message format
get_class($e),
$e->getMessage(),
$e->getTraceAsString()
);
self::$_bailExceptionRendering = true;
trigger_error($message, E_USER_ERROR);
}
}
/**
* Generates a formatted error message
*
* @param Exception $exception Exception instance
* @return string Formatted message
*/
protected static function _getMessage($exception) {
$message = '';
if (php_sapi_name() !== 'cli') {
$request = Router::getRequest();
if ($request) {
$message .= $request->here() . " Not Found";
}
}
$message .= "\nStack Trace:\n" . $exception->getTraceAsString() . "\n";
return $message;
}
/**
* Handles exception logging
*
* @param Exception $exception The exception to render.
* @param array $config An array of configuration for logging.
* @return bool
*/
protected static function _log(Exception $exception, $config) {
if (!empty(self::$_exceptionClasses)) {
foreach ((array)self::$_exceptionClasses as $class => $level) {
if ($exception instanceof $class) {
return CakeLog::write($level, self::_getMessage($exception));
}
}
}
return parent::_log();
}
}
You can customize the $_exceptionClasses
array to catch which exceptions you want and send them to different logs. The _getMessage()
method has been simplified to remove the attributes.
Result
Random URLs like /exploitable-plugin
will now log to tmp/logs/404.log
.
2015-04-01 16:37:54 404: /exploitable-plugin Not Found
Stack Trace:
#0 /var/example.com/app/index.php(146): Dispatcher->dispatch(Object(CakeRequest), Object(CakeResponse))
#1 {main}
App::uses('MyCustomErrorHandler', 'Lib/Error');
inside core.php above where we defined the Exception configuration. –
Sekofski AppErrorHandler
as your new handler, you should add App::uses('AppErrorHandler', 'Lib/Error');
-- not "MyCustomErrorHandler" –
Unpopular I assume that your viewers hit those pages from results of search engines. Since you're a webmaster of this page, you could try to use something like Google removal tools https://www.google.com/webmasters/tools/removals . So there won't be any more liks to these pages. It is a little costly in time, but I don't see a way to exclude missing controller actions form logging.
Anyway here you have everything on logging in CakePHP: https://www.google.com/webmasters/tools/removals?pli=1
Edit:
Actually I'd found a way, but it might make you trouble in the future if you forgot about it. Add this to the end of bootstrap.php :
$matches = array();
preg_match('/^\/(.+?)\//', $_SERVER['REQUEST_URI'],$matches);
if(!App::import('Controller', $matches[1])){
die('404 Error![some_random_chars_so_you_can_easy_find_it_in_the_future]') ;
}
© 2022 - 2024 — McMap. All rights reserved.
MissingControllerException
. Either way, to catch any of the "old" URLs, you should use Routes to rewrite them to their new location (or a custom 404 page informing the user they're using outdated links) instead of surpressing exceptions. – Wheeledapp/webroot/.htaccess
, before the Cake "overall" rewrite, just add:RewriteRule ^images/.* - [L,R=404]
. That will instantly throw a 404 from Apache (so Cake isn't doing anything with it). – Wheeled