Merge pull request #15965 from owncloud/conditional-logging
Conditional logging
This commit is contained in:
commit
a52afb040a
|
@ -486,6 +486,27 @@ $CONFIG = array(
|
|||
*/
|
||||
'loglevel' => 2,
|
||||
|
||||
/**
|
||||
* Log condition for log level increase based on conditions. Once one of these
|
||||
* conditions is met, the required log level is set to debug. This allows to
|
||||
* debug specific requests, users or apps
|
||||
*
|
||||
* Supported conditions:
|
||||
* - ``shared_secret``: if a request parameter with the name `log_secret` is set to
|
||||
* this value the condition is met
|
||||
* - ``users``: if the current request is done by one of the specified users,
|
||||
* this condition is met
|
||||
* - ``apps``: if the log message is invoked by one of the specified apps,
|
||||
* this condition is met
|
||||
*
|
||||
* Defaults to an empty array.
|
||||
*/
|
||||
'log.condition' => [
|
||||
'shared_secret' => '57b58edb6637fe3059b3595cf9c41b9',
|
||||
'users' => ['sample-user'],
|
||||
'apps' => ['files'],
|
||||
],
|
||||
|
||||
/**
|
||||
* This uses PHP.date formatting; see http://php.net/manual/en/function.date.php
|
||||
*/
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
namespace OC;
|
||||
|
||||
use \OCP\ILogger;
|
||||
use OCP\Security\StringUtils;
|
||||
|
||||
/**
|
||||
* logging utilities
|
||||
|
@ -41,15 +42,29 @@ use \OCP\ILogger;
|
|||
|
||||
class Log implements ILogger {
|
||||
|
||||
/** @var string */
|
||||
private $logger;
|
||||
/** @var SystemConfig */
|
||||
private $config;
|
||||
|
||||
/** @var boolean|null cache the result of the log condition check for the request */
|
||||
private $logConditionSatisfied = null;
|
||||
|
||||
/**
|
||||
* @param string $logger The logger that should be used
|
||||
* @param SystemConfig $config the system config object
|
||||
*/
|
||||
public function __construct($logger=null) {
|
||||
public function __construct($logger=null, SystemConfig $config=null) {
|
||||
// FIXME: Add this for backwards compatibility, should be fixed at some point probably
|
||||
if($config === null) {
|
||||
$config = \OC::$server->getSystemConfig();
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
// FIXME: Add this for backwards compatibility, should be fixed at some point probably
|
||||
if($logger === null) {
|
||||
$this->logger = 'OC_Log_'.ucfirst(\OC_Config::getValue('log_type', 'owncloud'));
|
||||
$this->logger = 'OC_Log_'.ucfirst($this->config->getValue('log_type', 'owncloud'));
|
||||
call_user_func(array($this->logger, 'init'));
|
||||
} else {
|
||||
$this->logger = $logger;
|
||||
|
@ -158,8 +173,22 @@ class Log implements ILogger {
|
|||
* @param array $context
|
||||
*/
|
||||
public function log($level, $message, array $context = array()) {
|
||||
$minLevel = min($this->config->getValue('loglevel', \OC_Log::WARN), \OC_Log::ERROR);
|
||||
$logCondition = $this->config->getValue('log.condition', []);
|
||||
|
||||
if (isset($context['app'])) {
|
||||
$app = $context['app'];
|
||||
|
||||
/**
|
||||
* check log condition based on the context of each log message
|
||||
* once this is met -> change the required log level to debug
|
||||
*/
|
||||
if(!empty($logCondition)
|
||||
&& isset($logCondition['apps'])
|
||||
&& in_array($app, $logCondition['apps'], true)) {
|
||||
$minLevel = \OC_Log::DEBUG;
|
||||
}
|
||||
|
||||
} else {
|
||||
$app = 'no app in context';
|
||||
}
|
||||
|
@ -172,7 +201,45 @@ class Log implements ILogger {
|
|||
// interpolate replacement values into the message and return
|
||||
$message = strtr($message, $replace);
|
||||
|
||||
$logger = $this->logger;
|
||||
call_user_func(array($logger, 'write'), $app, $message, $level);
|
||||
/**
|
||||
* check for a special log condition - this enables an increased log on
|
||||
* a per request/user base
|
||||
*/
|
||||
if($this->logConditionSatisfied === null) {
|
||||
// default to false to just process this once per request
|
||||
$this->logConditionSatisfied = false;
|
||||
if(!empty($logCondition)) {
|
||||
|
||||
// check for secret token in the request
|
||||
if(isset($logCondition['shared_secret'])) {
|
||||
$request = \OC::$server->getRequest();
|
||||
|
||||
// if token is found in the request change set the log condition to satisfied
|
||||
if($request && StringUtils::equals($request->getParam('log_secret'), $logCondition['shared_secret'])) {
|
||||
$this->logConditionSatisfied = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check for user
|
||||
if(isset($logCondition['users'])) {
|
||||
$user = \OC::$server->getUserSession()->getUser();
|
||||
|
||||
// if the user matches set the log condition to satisfied
|
||||
if($user !== null && in_array($user->getUID(), $logCondition['users'], true)) {
|
||||
$this->logConditionSatisfied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if log condition is satisfied change the required log level to DEBUG
|
||||
if($this->logConditionSatisfied) {
|
||||
$minLevel = \OC_Log::DEBUG;
|
||||
}
|
||||
|
||||
if ($level >= $minLevel) {
|
||||
$logger = $this->logger;
|
||||
call_user_func(array($logger, 'write'), $app, $message, $level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,7 @@ class OC_Log_Errorlog {
|
|||
* @param int $level
|
||||
*/
|
||||
public static function write($app, $message, $level) {
|
||||
$minLevel = min(OC_Config::getValue("loglevel", OC_Log::WARN), OC_Log::ERROR);
|
||||
if ($level >= $minLevel) {
|
||||
error_log('[owncloud]['.$app.'] '.$message);
|
||||
}
|
||||
error_log('[owncloud]['.$app.']['.$level.'] '.$message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,40 +69,40 @@ class OC_Log_Owncloud {
|
|||
* @param int $level
|
||||
*/
|
||||
public static function write($app, $message, $level) {
|
||||
$minLevel=min(OC_Config::getValue( "loglevel", OC_Log::WARN ), OC_Log::ERROR);
|
||||
if($level>=$minLevel) {
|
||||
// default to ISO8601
|
||||
$format = OC_Config::getValue('logdateformat', 'c');
|
||||
$logtimezone=OC_Config::getValue( "logtimezone", 'UTC' );
|
||||
try {
|
||||
$timezone = new DateTimeZone($logtimezone);
|
||||
} catch (Exception $e) {
|
||||
$timezone = new DateTimeZone('UTC');
|
||||
}
|
||||
$time = new DateTime(null, $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);
|
||||
if($minLevel == OC_Log::DEBUG) {
|
||||
$url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '--';
|
||||
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '--';
|
||||
$entry = compact('reqId', 'remoteAddr', 'app', 'message', 'level', 'time', 'method', 'url');
|
||||
}
|
||||
else {
|
||||
$entry = compact('reqId', 'remoteAddr', 'app', 'message', 'level', 'time');
|
||||
}
|
||||
$entry = json_encode($entry);
|
||||
$handle = @fopen(self::$logFile, 'a');
|
||||
@chmod(self::$logFile, 0640);
|
||||
if ($handle) {
|
||||
fwrite($handle, $entry."\n");
|
||||
fclose($handle);
|
||||
} else {
|
||||
// Fall back to error_log
|
||||
error_log($entry);
|
||||
}
|
||||
$config = \OC::$server->getSystemConfig();
|
||||
|
||||
// default to ISO8601
|
||||
$format = $config->getValue('logdateformat', 'c');
|
||||
$logtimezone = $config->getValue( "logtimezone", 'UTC' );
|
||||
try {
|
||||
$timezone = new DateTimeZone($logtimezone);
|
||||
} catch (Exception $e) {
|
||||
$timezone = new DateTimeZone('UTC');
|
||||
}
|
||||
$time = new DateTime(null, $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);
|
||||
$minLevel=min($config->getValue( "loglevel", OC_Log::WARN ), OC_Log::ERROR);
|
||||
if($minLevel == OC_Log::DEBUG) {
|
||||
$url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '--';
|
||||
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '--';
|
||||
$entry = compact('reqId', 'remoteAddr', 'app', 'message', 'level', 'time', 'method', 'url');
|
||||
}
|
||||
else {
|
||||
$entry = compact('reqId', 'remoteAddr', 'app', 'message', 'level', 'time');
|
||||
}
|
||||
$entry = json_encode($entry);
|
||||
$handle = @fopen(self::$logFile, 'a');
|
||||
@chmod(self::$logFile, 0640);
|
||||
if ($handle) {
|
||||
fwrite($handle, $entry."\n");
|
||||
fclose($handle);
|
||||
} else {
|
||||
// Fall back to error_log
|
||||
error_log($entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,10 +47,7 @@ class OC_Log_Syslog {
|
|||
* @param int $level
|
||||
*/
|
||||
public static function write($app, $message, $level) {
|
||||
$minLevel = min(OC_Config::getValue("loglevel", OC_Log::WARN), OC_Log::ERROR);
|
||||
if ($level >= $minLevel) {
|
||||
$syslog_level = self::$levels[$level];
|
||||
syslog($syslog_level, '{'.$app.'} '.$message);
|
||||
}
|
||||
$syslog_level = self::$levels[$level];
|
||||
syslog($syslog_level, '{'.$app.'} '.$message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,38 @@ class Logger extends TestCase {
|
|||
parent::setUp();
|
||||
|
||||
self::$logs = array();
|
||||
$this->logger = new Log('Test\Logger');
|
||||
$this->config = $this->getMockBuilder(
|
||||
'\OC\SystemConfig')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->logger = new Log('Test\Logger', $this->config);
|
||||
}
|
||||
|
||||
public function testInterpolation() {
|
||||
$logger = $this->logger;
|
||||
$logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
|
||||
$logger->warning('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
|
||||
|
||||
$expected = array('1 {Message {nothing} Bob Bar a}');
|
||||
$expected = array('2 {Message {nothing} Bob Bar a}');
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function testAppCondition() {
|
||||
$this->config->expects($this->any())
|
||||
->method('getValue')
|
||||
->will(($this->returnValueMap([
|
||||
['loglevel', \OC_Log::WARN, \OC_Log::WARN],
|
||||
['log.condition', [], ['apps' => ['files']]]
|
||||
])));
|
||||
$logger = $this->logger;
|
||||
|
||||
$logger->info('Don\'t display info messages');
|
||||
$logger->info('Show info messages of files app', ['app' => 'files']);
|
||||
$logger->warning('Show warning messages of other apps');
|
||||
|
||||
$expected = [
|
||||
'1 Show info messages of files app',
|
||||
'2 Show warning messages of other apps',
|
||||
];
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue