Skip to content

Commit 9e1b300

Browse files
committed
Use a different logger and log file if ownCloud is broken
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
1 parent faddb89 commit 9e1b300

File tree

7 files changed

+117
-37
lines changed

7 files changed

+117
-37
lines changed

config/config.sample.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@
8282
*/
8383
'datadirectory' => '/var/www/owncloud/data',
8484

85+
/**
86+
* Define the directory where the crash logs will be stored.
87+
* By default, this will be the same as the one configured as "datadirectory".
88+
* The directory MUST EXIST and be WRITABLE by the web server.
89+
* Note that crashes are extremely rare (although they can come in burst due to
90+
* multiple requests), so the default location is usually fine.
91+
* Also note that the log can contain sensitive information, but it should be useful
92+
* to pinpoint where is the problem.
93+
*/
94+
'crashdirectory' => '/var/www/owncloud/data',
95+
8596
/**
8697
* Current version number of your ownCloud installation
8798
* This is set up during installation and update, so you shouldn't need to change it.

console.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,19 @@
5656
}
5757

5858
function exceptionHandler($exception) {
59-
echo 'An unhandled exception has been thrown:' . PHP_EOL;
60-
echo $exception;
61-
exit(1);
59+
try {
60+
// try to log the exception
61+
\OC::$server->getLogger()->logException($ex, ['app' => 'index']);
62+
} catch (\Throwable $ex) {
63+
// if we can't log normally, use the crashLog
64+
\OC::crashLog($exception);
65+
\OC::crashLog($ex);
66+
} finally {
67+
// always show the exception in the console
68+
echo 'An unhandled exception has been thrown:' . PHP_EOL;
69+
echo $exception;
70+
exit(1);
71+
}
6272
}
6373
try {
6474
require_once __DIR__ . '/lib/base.php';
@@ -104,8 +114,6 @@ function exceptionHandler($exception) {
104114
$application = new Application(\OC::$server->getConfig(), \OC::$server->getEventDispatcher(), \OC::$server->getRequest());
105115
$application->loadCommands(new ArgvInput(), new ConsoleOutput());
106116
$application->run();
107-
} catch (Exception $ex) {
108-
exceptionHandler($ex);
109-
} catch (Error $ex) {
117+
} catch (\Throwable $ex) {
110118
exceptionHandler($ex);
111119
}

index.php

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,20 @@
6767
} catch (\OCP\Files\ForbiddenException $ex) {
6868
OC_Response::setStatus(OC_Response::STATUS_FORBIDDEN);
6969
OC_Template::printErrorPage($ex->getMessage());
70-
} catch (Exception $ex) {
70+
} catch (\Throwable $ex) {
7171
try {
7272
\OC::$server->getLogger()->logException($ex, ['app' => 'index']);
7373

7474
//show the user a detailed error page
7575
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
7676
OC_Template::printExceptionErrorPage($ex);
77-
} catch (\Exception $ex2) {
77+
} catch (\Throwable $ex2) {
7878
// with some env issues, it can happen that the logger couldn't log properly,
7979
// so print out the exception directly
8080
// NOTE: If we've reached this point, something has gone really wrong because
8181
// we couldn't even get the logger, so don't rely on ownCloud here.
82-
\http_response_code(500);
83-
echo('<html><body>');
84-
echo('Exception occurred while logging exception: ' . $ex->getMessage() . '<br/>');
85-
echo(\str_replace("\n", '<br/>', $ex->getTraceAsString()));
86-
echo('</body></html>');
82+
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
83+
\OC::crashLog($ex);
84+
\OC::crashLog($ex2);
8785
}
88-
} catch (Error $ex) {
89-
\OC::$server->getLogger()->logException($ex, ['app' => 'index']);
90-
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
91-
OC_Template::printExceptionErrorPage($ex);
9286
}

lib/base.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,59 @@ protected static function handleAuthHeaders() {
10001000
}
10011001
}
10021002
}
1003+
1004+
/**
1005+
* Log an ownCloud's crash. This means that ownCloud isn't usable and can't log through
1006+
* normal ownCloud's logger facilities.
1007+
* This crash log can't use the normal "owncloud.log" file because the ownCloud's logger
1008+
* requires additional context that can't be easily replicated. We'll use a different file.
1009+
* The crash log will create a "crash-Y-m-d.log" file in the ownCloud's data directory, unless
1010+
* the "crashdirectory" is set in the config.php file (make sure the "crashdirectory" exists and
1011+
* it's writeable for the web server). The filename will be reused for all the crashes that happen
1012+
* during the same day, and a new one will be used the next day.
1013+
* The crash file will be created only if needed.
1014+
*
1015+
* Note: This is mainly for internal purposes. You're encouraged to use the ownCloud's logger
1016+
* for anything you need to log.
1017+
*/
1018+
public static function crashLog(\Throwable $ex) {
1019+
$dataDir = self::$config->getValue('datadirectory', self::$SERVERROOT . '/data');
1020+
$crashDir = self::$config->getValue('crashdirectory', $dataDir);
1021+
1022+
$filename = "${crashDir}/crash-" . \date('Y-m-d') . '.log';
1023+
1024+
$date = \date('c');
1025+
$currentEntryId = \uniqid(\md5($date), true);
1026+
$entry = [
1027+
'date' => $date,
1028+
'parentId' => null,
1029+
'id' => $currentEntryId,
1030+
'class' => \get_class($ex),
1031+
'message' => $ex->getMessage(),
1032+
'stacktrace' => \array_map(function ($elem) {
1033+
unset($elem['args'], $elem['type']);
1034+
return $elem;
1035+
}, $ex->getTrace()),
1036+
];
1037+
\file_put_contents($filename, \json_encode($entry, JSON_PRETTY_PRINT) . PHP_EOL, FILE_APPEND | LOCK_EX);
1038+
1039+
while (($ex = $ex->getPrevious()) !== null) {
1040+
$previousEntryId = $currentEntryId;
1041+
$currentEntryId = \uniqid(\md5($date), true);
1042+
$entry = [
1043+
'date' => $date,
1044+
'parentId' => $previousEntryId,
1045+
'id' => $currentEntryId,
1046+
'class' => \get_class($ex),
1047+
'message' => $ex->getMessage(),
1048+
'stacktrace' => \array_map(function ($elem) {
1049+
unset($elem['args'], $elem['type']);
1050+
return $elem;
1051+
}, $ex->getTrace()),
1052+
];
1053+
\file_put_contents($filename, \json_encode($entry, JSON_PRETTY_PRINT) . PHP_EOL, FILE_APPEND | LOCK_EX);
1054+
}
1055+
}
10031056
}
10041057

10051058
OC::init();

public.php

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,20 @@
7373
$baseuri = OC::$WEBROOT . '/public.php/' . $service . '/';
7474

7575
require_once OC_App::getAppPath($app) . '/' . $parts[1];
76-
} catch (Exception $ex) {
77-
if ($ex instanceof \OC\ServiceUnavailableException) {
78-
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
79-
} else {
80-
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
76+
} catch (\Throwable $ex) {
77+
try {
78+
if ($ex instanceof \OC\ServiceUnavailableException) {
79+
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
80+
} else {
81+
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
82+
}
83+
//show the user a detailed error page
84+
\OC::$server->getLogger()->logException($ex, ['app' => 'public']);
85+
OC_Template::printExceptionErrorPage($ex);
86+
} catch (\Throwable $ex2) {
87+
// log through the crashLog
88+
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
89+
\OC::crashLog($ex);
90+
\OC::crashLog($ex2);
8191
}
82-
//show the user a detailed error page
83-
\OC::$server->getLogger()->logException($ex, ['app' => 'public']);
84-
OC_Template::printExceptionErrorPage($ex);
85-
} catch (Error $ex) {
86-
//show the user a detailed error page
87-
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
88-
\OC::$server->getLogger()->logException($ex, ['app' => 'public']);
89-
OC_Template::printExceptionErrorPage($ex);
9092
}

remote.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,13 @@ function resolveService($service) {
163163
}
164164
$baseuri = OC::$WEBROOT . '/remote.php/'.$service.'/';
165165
require_once $file;
166-
} catch (Exception $ex) {
167-
handleException($ex);
168-
} catch (Error $e) {
169-
handleException($e);
166+
} catch (\Throwable $ex) {
167+
try {
168+
handleException($ex);
169+
} catch (\Throwable $ex2) {
170+
// log through the crashLog
171+
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
172+
\OC::crashLog($ex);
173+
\OC::crashLog($ex2);
174+
}
170175
}

status.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@
4848
\header('Content-Type: application/json');
4949
echo \json_encode($values);
5050
}
51-
} catch (Exception $ex) {
52-
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
53-
\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
51+
} catch (\Throwable $ex) {
52+
try {
53+
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
54+
\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
55+
} catch (\Throwable $ex2) {
56+
// log through the crashLog
57+
\header("{$_SERVER['SERVER_PROTOCOL']} 599 Broken");
58+
\OC::crashLog($ex);
59+
\OC::crashLog($ex2);
60+
}
5461
}

0 commit comments

Comments
 (0)