Signed-off-by: Julius Härtl <jus@bitgrid.net>tags/v17.0.0beta1
@@ -974,6 +974,7 @@ return array( | |||
'OC\\Log\\Errorlog' => $baseDir . '/lib/private/Log/Errorlog.php', | |||
'OC\\Log\\ExceptionSerializer' => $baseDir . '/lib/private/Log/ExceptionSerializer.php', | |||
'OC\\Log\\File' => $baseDir . '/lib/private/Log/File.php', | |||
'OC\\Log\\LogDetails' => $baseDir . '/lib/private/Log/LogDetails.php', | |||
'OC\\Log\\LogFactory' => $baseDir . '/lib/private/Log/LogFactory.php', | |||
'OC\\Log\\Rotate' => $baseDir . '/lib/private/Log/Rotate.php', | |||
'OC\\Log\\Syslog' => $baseDir . '/lib/private/Log/Syslog.php', |
@@ -1008,6 +1008,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c | |||
'OC\\Log\\Errorlog' => __DIR__ . '/../../..' . '/lib/private/Log/Errorlog.php', | |||
'OC\\Log\\ExceptionSerializer' => __DIR__ . '/../../..' . '/lib/private/Log/ExceptionSerializer.php', | |||
'OC\\Log\\File' => __DIR__ . '/../../..' . '/lib/private/Log/File.php', | |||
'OC\\Log\\LogDetails' => __DIR__ . '/../../..' . '/lib/private/Log/LogDetails.php', | |||
'OC\\Log\\LogFactory' => __DIR__ . '/../../..' . '/lib/private/Log/LogFactory.php', | |||
'OC\\Log\\Rotate' => __DIR__ . '/../../..' . '/lib/private/Log/Rotate.php', | |||
'OC\\Log\\Syslog' => __DIR__ . '/../../..' . '/lib/private/Log/Syslog.php', |
@@ -47,7 +47,7 @@ use OCP\ILogger; | |||
* Log is saved at data/nextcloud.log (on default) | |||
*/ | |||
class File implements IWriter, IFileBased { | |||
class File extends LogDetails implements IWriter, IFileBased { | |||
/** @var string */ | |||
protected $logFile; | |||
/** @var int */ | |||
@@ -56,6 +56,7 @@ class File implements IWriter, IFileBased { | |||
private $config; | |||
public function __construct(string $path, string $fallbackPath = '', SystemConfig $config) { | |||
parent::__construct($config); | |||
$this->logFile = $path; | |||
if (!file_exists($this->logFile)) { | |||
if( | |||
@@ -79,63 +80,7 @@ class File implements IWriter, IFileBased { | |||
* @param int $level | |||
*/ | |||
public function write(string $app, $message, int $level) { | |||
// default to ISO8601 | |||
$format = $this->config->getValue('logdateformat', \DateTime::ATOM); | |||
$logTimeZone = $this->config->getValue('logtimezone', 'UTC'); | |||
try { | |||
$timezone = new \DateTimeZone($logTimeZone); | |||
} catch (\Exception $e) { | |||
$timezone = new \DateTimeZone('UTC'); | |||
} | |||
$time = \DateTime::createFromFormat("U.u", number_format(microtime(true), 4, ".", "")); | |||
if ($time === false) { | |||
$time = new \DateTime(null, $timezone); | |||
} else { | |||
// apply timezone if $time is created from UNIX timestamp | |||
$time->setTimezone($timezone); | |||
} | |||
$request = \OC::$server->getRequest(); | |||
$reqId = $request->getId(); | |||
$remoteAddr = $request->getRemoteAddress(); | |||
// remove username/passwords from URLs before writing the to the log file | |||
$time = $time->format($format); | |||
$url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--'; | |||
$method = is_string($request->getMethod()) ? $request->getMethod() : '--'; | |||
if($this->config->getValue('installed', false)) { | |||
$user = \OC_User::getUser() ? \OC_User::getUser() : '--'; | |||
} else { | |||
$user = '--'; | |||
} | |||
$userAgent = $request->getHeader('User-Agent'); | |||
if ($userAgent === '') { | |||
$userAgent = '--'; | |||
} | |||
$version = $this->config->getValue('version', ''); | |||
$entry = compact( | |||
'reqId', | |||
'level', | |||
'time', | |||
'remoteAddr', | |||
'user', | |||
'app', | |||
'method', | |||
'url', | |||
'message', | |||
'userAgent', | |||
'version' | |||
); | |||
// PHP's json_encode only accept proper UTF-8 strings, loop over all | |||
// elements to ensure that they are properly UTF-8 compliant or convert | |||
// them manually. | |||
foreach($entry as $key => $value) { | |||
if(is_string($value)) { | |||
$testEncode = json_encode($value); | |||
if($testEncode === false) { | |||
$entry[$key] = utf8_encode($value); | |||
} | |||
} | |||
} | |||
$entry = json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR); | |||
$entry = $this->logDetailsAsJSON($app, $message, $level); | |||
$handle = @fopen($this->logFile, 'a'); | |||
if ($this->logFileMode > 0 && (fileperms($this->logFile) & 0777) != $this->logFileMode) { | |||
@chmod($this->logFile, $this->logFileMode); |
@@ -0,0 +1,101 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> | |||
* | |||
* @author Julius Härtl <jus@bitgrid.net> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\Log; | |||
use OC\SystemConfig; | |||
abstract class LogDetails { | |||
/** @var SystemConfig */ | |||
private $config; | |||
public function __construct(SystemConfig $config) { | |||
$this->config = $config; | |||
} | |||
public function logDetails(string $app, $message, int $level): array { | |||
// default to ISO8601 | |||
$format = $this->config->getValue('logdateformat', \DateTime::ATOM); | |||
$logTimeZone = $this->config->getValue('logtimezone', 'UTC'); | |||
try { | |||
$timezone = new \DateTimeZone($logTimeZone); | |||
} catch (\Exception $e) { | |||
$timezone = new \DateTimeZone('UTC'); | |||
} | |||
$time = \DateTime::createFromFormat("U.u", number_format(microtime(true), 4, ".", "")); | |||
if ($time === false) { | |||
$time = new \DateTime(null, $timezone); | |||
} else { | |||
// apply timezone if $time is created from UNIX timestamp | |||
$time->setTimezone($timezone); | |||
} | |||
$request = \OC::$server->getRequest(); | |||
$reqId = $request->getId(); | |||
$remoteAddr = $request->getRemoteAddress(); | |||
// remove username/passwords from URLs before writing the to the log file | |||
$time = $time->format($format); | |||
$url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--'; | |||
$method = is_string($request->getMethod()) ? $request->getMethod() : '--'; | |||
if($this->config->getValue('installed', false)) { | |||
$user = \OC_User::getUser() ? \OC_User::getUser() : '--'; | |||
} else { | |||
$user = '--'; | |||
} | |||
$userAgent = $request->getHeader('User-Agent'); | |||
if ($userAgent === '') { | |||
$userAgent = '--'; | |||
} | |||
$version = $this->config->getValue('version', ''); | |||
$entry = compact( | |||
'reqId', | |||
'level', | |||
'time', | |||
'remoteAddr', | |||
'user', | |||
'app', | |||
'method', | |||
'url', | |||
'message', | |||
'userAgent', | |||
'version' | |||
); | |||
return $entry; | |||
} | |||
public function logDetailsAsJSON(string $app, $message, int $level): string { | |||
$entry = $this->logDetails($app, $message, $level); | |||
// PHP's json_encode only accept proper UTF-8 strings, loop over all | |||
// elements to ensure that they are properly UTF-8 compliant or convert | |||
// them manually. | |||
foreach($entry as $key => $value) { | |||
if(is_string($value)) { | |||
$testEncode = json_encode($value); | |||
if($testEncode === false) { | |||
$entry[$key] = utf8_encode($value); | |||
} | |||
} | |||
} | |||
return json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR); | |||
} | |||
} |
@@ -25,11 +25,11 @@ | |||
namespace OC\Log; | |||
use OC\SystemConfig; | |||
use OCP\ILogger; | |||
use OCP\IConfig; | |||
use OCP\Log\IWriter; | |||
class Syslog implements IWriter { | |||
class Syslog extends LogDetails implements IWriter { | |||
protected $levels = [ | |||
ILogger::DEBUG => LOG_DEBUG, | |||
ILogger::INFO => LOG_INFO, | |||
@@ -38,7 +38,8 @@ class Syslog implements IWriter { | |||
ILogger::FATAL => LOG_CRIT, | |||
]; | |||
public function __construct(IConfig $config) { | |||
public function __construct(SystemConfig $config) { | |||
parent::__construct($config); | |||
openlog($config->getSystemValue('syslog_tag', 'Nextcloud'), LOG_PID | LOG_CONS, LOG_USER); | |||
} | |||
@@ -54,6 +55,6 @@ class Syslog implements IWriter { | |||
*/ | |||
public function write(string $app, $message, int $level) { | |||
$syslog_level = $this->levels[$level]; | |||
syslog($syslog_level, '{'.$app.'} '.$message); | |||
syslog($syslog_level, $this->logDetailsAsJSON($app, $message, $level)); | |||
} | |||
} |
@@ -23,6 +23,7 @@ | |||
namespace OC\Log; | |||
use OC\HintException; | |||
use OC\SystemConfig; | |||
use OCP\ILogger; | |||
use OCP\IConfig; | |||
use OCP\Log\IWriter; | |||
@@ -42,7 +43,7 @@ use OCP\Log\IWriter; | |||
// SYSLOG_FACILITY=, SYSLOG_IDENTIFIER=, SYSLOG_PID= | |||
// Syslog compatibility fields | |||
class Systemdlog implements IWriter { | |||
class Systemdlog extends LogDetails implements IWriter { | |||
protected $levels = [ | |||
ILogger::DEBUG => 7, | |||
ILogger::INFO => 6, | |||
@@ -53,14 +54,15 @@ class Systemdlog implements IWriter { | |||
protected $syslogId; | |||
public function __construct(IConfig $config) { | |||
public function __construct(SystemConfig $config) { | |||
parent::__construct($config); | |||
if(!function_exists('sd_journal_send')) { | |||
throw new HintException( | |||
'PHP extension php-systemd is not available.', | |||
'Please install and enable PHP extension systemd if you wish to log to the Systemd journal.'); | |||
} | |||
$this->syslogId = $config->getSystemValue('syslog_tag', 'Nextcloud'); | |||
$this->syslogId = $config->getValue('syslog_tag', 'Nextcloud'); | |||
} | |||
/** | |||
@@ -73,6 +75,6 @@ class Systemdlog implements IWriter { | |||
$journal_level = $this->levels[$level]; | |||
sd_journal_send('PRIORITY='.$journal_level, | |||
'SYSLOG_IDENTIFIER='.$this->syslogId, | |||
'MESSAGE={'.$app.'} '.$message); | |||
'MESSAGE=' . $this->logDetailsAsJSON($app, $message, $level)); | |||
} | |||
} |