1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-22 21:40:54 +01:00

Add a simple primitive for managing PHP runtime error logs

Summary:
Ref T13624. If we want to send PHP errors to a log, using the "error_log" configuration option catches the broadest set of errors across versions of PHP.

Configuring this disables errors on `stderr`, since they're sent to the log instead. We'd like them to go to both places; provide a simple wrapper for this. Also do a bit of writability testing.

Test Plan: Wrote errors to a new log, see followup changes.

Maniphest Tasks: T13624

Differential Revision: https://secure.phabricator.com/D21578
This commit is contained in:
epriestley 2021-02-26 11:59:49 -08:00
parent e95afd1d00
commit 6d60422dbb
3 changed files with 104 additions and 1 deletions

View file

@ -744,6 +744,7 @@ phutil_register_library_map(array(
'PhutilEnglishCanadaLocale' => 'internationalization/locales/PhutilEnglishCanadaLocale.php',
'PhutilErrorHandler' => 'error/PhutilErrorHandler.php',
'PhutilErrorHandlerTestCase' => 'error/__tests__/PhutilErrorHandlerTestCase.php',
'PhutilErrorLog' => 'filesystem/PhutilErrorLog.php',
'PhutilErrorTrap' => 'error/PhutilErrorTrap.php',
'PhutilEvent' => 'events/PhutilEvent.php',
'PhutilEventConstants' => 'events/constant/PhutilEventConstants.php',
@ -1816,6 +1817,7 @@ phutil_register_library_map(array(
'PhutilEnglishCanadaLocale' => 'PhutilLocale',
'PhutilErrorHandler' => 'Phobject',
'PhutilErrorHandlerTestCase' => 'PhutilTestCase',
'PhutilErrorLog' => 'Phobject',
'PhutilErrorTrap' => 'Phobject',
'PhutilEvent' => 'Phobject',
'PhutilEventConstants' => 'Phobject',

View file

@ -52,7 +52,7 @@ final class Filesystem extends Phobject {
* Make assertions about the state of path in preparation for
* writeFile() and writeFileIfChanged().
*/
private static function assertWritableFile($path) {
public static function assertWritableFile($path) {
$path = self::resolvePath($path);
$dir = dirname($path);

View file

@ -0,0 +1,101 @@
<?php
final class PhutilErrorLog
extends Phobject {
private $logName;
private $logPath;
public function setLogName($log_name) {
$this->logName = $log_name;
return $this;
}
public function getLogName() {
return $this->logName;
}
public function setLogPath($log_path) {
$this->logPath = $log_path;
return $this;
}
public function getLogPath() {
return $this->logPath;
}
public function activateLog() {
$log_path = $this->getLogPath();
if ($log_path !== null) {
// Test that the path is writable.
$write_exception = null;
try {
Filesystem::assertWritableFile($log_path);
} catch (FilesystemException $ex) {
$write_exception = $ex;
}
// If we hit an exception, try to create the containing directory.
if ($write_exception) {
$log_dir = dirname($log_path);
if (!Filesystem::pathExists($log_dir)) {
try {
Filesystem::createDirectory($log_dir, 0755, true);
} catch (FilesystemException $ex) {
throw new PhutilProxyException(
pht(
'Unable to write log "%s" to path "%s". The containing '.
'directory ("%s") does not exist or is not readable, and '.
'could not be created.',
$this->getLogName(),
$log_path,
$log_dir),
$ex);
}
}
// If we created the parent directory, test if the path is writable
// again.
try {
Filesystem::assertWritableFile($log_path);
$write_exception = null;
} catch (FilesystemException $ex) {
$write_exception = $ex;
}
}
// If we ran into a write exception and couldn't resolve it, fail.
if ($write_exception) {
throw new PhutilProxyException(
pht(
'Unable to write log "%s" to path "%s" because the path is not '.
'writable.',
$this->getLogName(),
$log_path),
$write_exception);
}
}
ini_set('error_log', $log_path);
PhutilErrorHandler::setErrorListener(array($this, 'onError'));
}
public function onError($event, $value, array $metadata) {
// If we've set "error_log" to a real file, so messages won't be output to
// stderr by default. Copy them to stderr.
if ($this->logPath === null) {
return;
}
$message = idx($metadata, 'default_message');
if (strlen($message)) {
$message = tsprintf("%B\n", $message);
@fwrite(STDERR, $message);
}
}
}