Skip to content

Commit

Permalink
Use a different logger and log file if ownCloud is broken
Browse files Browse the repository at this point in the history
The endpoints will return a 599 HTTP status to distinguish app failures,
which shouldn't break ownCloud and can still be reported through normal
means.
It will use a different file to log what happened without messing with
the owncloud.log file and without bothering the web server's log

Include new configuration option in config.sample.php file
  • Loading branch information
jvillafanez committed Nov 13, 2019
1 parent fd67362 commit d84eafc
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 37 deletions.
11 changes: 11 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@
*/
'datadirectory' => '/var/www/owncloud/data',

/**
* Define the directory where the crash logs will be stored.
* By default, this will be the same as the one configured as "datadirectory".
* The directory MUST EXIST and be WRITABLE by the web server.
* Note that crashes are extremely rare (although they can come in burst due to
* multiple requests), so the default location is usually fine.
* Also note that the log can contain sensitive information, but it should be useful
* to pinpoint where is the problem.
*/
'crashdirectory' => '/var/www/owncloud/data',

/**
* Current version number of your ownCloud installation
* This is set up during installation and update, so you shouldn't need to change it.
Expand Down
20 changes: 14 additions & 6 deletions console.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,19 @@
}

function exceptionHandler($exception) {
echo 'An unhandled exception has been thrown:' . PHP_EOL;
echo $exception;
exit(1);
try {
// try to log the exception
\OC::$server->getLogger()->logException($ex, ['app' => 'index']);
} catch (\Throwable $ex) {
// if we can't log normally, use the crashLog
\OC::crashLog($exception);
\OC::crashLog($ex);
} finally {
// always show the exception in the console
echo 'An unhandled exception has been thrown:' . PHP_EOL;
echo $exception;
exit(1);
}
}
try {
require_once __DIR__ . '/lib/base.php';
Expand Down Expand Up @@ -104,8 +114,6 @@ function exceptionHandler($exception) {
$application = new Application(\OC::$server->getConfig(), \OC::$server->getEventDispatcher(), \OC::$server->getRequest());
$application->loadCommands(new ArgvInput(), new ConsoleOutput());
$application->run();
} catch (Exception $ex) {
exceptionHandler($ex);
} catch (Error $ex) {
} catch (\Throwable $ex) {
exceptionHandler($ex);
}
16 changes: 5 additions & 11 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,26 +67,20 @@
} catch (\OCP\Files\ForbiddenException $ex) {
OC_Response::setStatus(OC_Response::STATUS_FORBIDDEN);
OC_Template::printErrorPage($ex->getMessage());
} catch (Exception $ex) {
} catch (\Throwable $ex) {
try {
\OC::$server->getLogger()->logException($ex, ['app' => 'index']);

//show the user a detailed error page
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
OC_Template::printExceptionErrorPage($ex);
} catch (\Exception $ex2) {
} catch (\Throwable $ex2) {
// with some env issues, it can happen that the logger couldn't log properly,
// so print out the exception directly
// NOTE: If we've reached this point, something has gone really wrong because
// we couldn't even get the logger, so don't rely on ownCloud here.
\http_response_code(500);
echo('<html><body>');
echo('Exception occurred while logging exception: ' . $ex->getMessage() . '<br/>');
echo(\str_replace("\n", '<br/>', $ex->getTraceAsString()));
echo('</body></html>');
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
\OC::crashLog($ex);
\OC::crashLog($ex2);
}
} catch (Error $ex) {
\OC::$server->getLogger()->logException($ex, ['app' => 'index']);
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
OC_Template::printExceptionErrorPage($ex);
}
53 changes: 53 additions & 0 deletions lib/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,59 @@ protected static function handleAuthHeaders() {
}
}
}

/**
* Log an ownCloud's crash. This means that ownCloud isn't usable and can't log through
* normal ownCloud's logger facilities.
* This crash log can't use the normal "owncloud.log" file because the ownCloud's logger
* requires additional context that can't be easily replicated. We'll use a different file.
* The crash log will create a "crash-Y-m-d.log" file in the ownCloud's data directory, unless
* the "crashdirectory" is set in the config.php file (make sure the "crashdirectory" exists and
* it's writeable for the web server). The filename will be reused for all the crashes that happen
* during the same day, and a new one will be used the next day.
* The crash file will be created only if needed.
*
* Note: This is mainly for internal purposes. You're encouraged to use the ownCloud's logger
* for anything you need to log.
*/
public static function crashLog(\Throwable $ex) {
$dataDir = self::$config->getValue('datadirectory', self::$SERVERROOT . '/data');
$crashDir = self::$config->getValue('crashdirectory', $dataDir);

$filename = "${crashDir}/crash-" . \date('Y-m-d') . '.log';

$date = \date('c');
$currentEntryId = \uniqid(\md5($date), true);
$entry = [
'date' => $date,
'parentId' => null,
'id' => $currentEntryId,
'class' => \get_class($ex),
'message' => $ex->getMessage(),
'stacktrace' => \array_map(function ($elem) {
unset($elem['args'], $elem['type']);
return $elem;
}, $ex->getTrace()),
];
\file_put_contents($filename, \json_encode($entry, JSON_PRETTY_PRINT) . PHP_EOL, FILE_APPEND | LOCK_EX);

while (($ex = $ex->getPrevious()) !== null) {
$previousEntryId = $currentEntryId;
$currentEntryId = \uniqid(\md5($date), true);
$entry = [
'date' => $date,
'parentId' => $previousEntryId,
'id' => $currentEntryId,
'class' => \get_class($ex),
'message' => $ex->getMessage(),
'stacktrace' => \array_map(function ($elem) {
unset($elem['args'], $elem['type']);
return $elem;
}, $ex->getTrace()),
];
\file_put_contents($filename, \json_encode($entry, JSON_PRETTY_PRINT) . PHP_EOL, FILE_APPEND | LOCK_EX);
}
}
}

OC::init();
28 changes: 15 additions & 13 deletions public.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,20 @@
$baseuri = OC::$WEBROOT . '/public.php/' . $service . '/';

require_once OC_App::getAppPath($app) . '/' . $parts[1];
} catch (Exception $ex) {
if ($ex instanceof \OC\ServiceUnavailableException) {
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
} else {
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
} catch (\Throwable $ex) {
try {
if ($ex instanceof \OC\ServiceUnavailableException) {
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
} else {
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
}
//show the user a detailed error page
\OC::$server->getLogger()->logException($ex, ['app' => 'public']);
OC_Template::printExceptionErrorPage($ex);
} catch (\Throwable $ex2) {
// log through the crashLog
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
\OC::crashLog($ex);
\OC::crashLog($ex2);
}
//show the user a detailed error page
\OC::$server->getLogger()->logException($ex, ['app' => 'public']);
OC_Template::printExceptionErrorPage($ex);
} catch (Error $ex) {
//show the user a detailed error page
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
\OC::$server->getLogger()->logException($ex, ['app' => 'public']);
OC_Template::printExceptionErrorPage($ex);
}
13 changes: 9 additions & 4 deletions remote.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,13 @@ function resolveService($service) {
}
$baseuri = OC::$WEBROOT . '/remote.php/'.$service.'/';
require_once $file;
} catch (Exception $ex) {
handleException($ex);
} catch (Error $e) {
handleException($e);
} catch (\Throwable $ex) {
try {
handleException($ex);
} catch (\Throwable $ex2) {
// log through the crashLog
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
\OC::crashLog($ex);
\OC::crashLog($ex2);
}
}
13 changes: 10 additions & 3 deletions status.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,14 @@
\header('Content-Type: application/json');
echo \json_encode($values);
}
} catch (Exception $ex) {
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
} catch (\Throwable $ex) {
try {
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
} catch (\Throwable $ex2) {
// log through the crashLog
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
\OC::crashLog($ex);
\OC::crashLog($ex2);
}
}

0 comments on commit d84eafc

Please sign in to comment.