From 46aaeb45612e20b6f460396533faf7f26ed1d6f7 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 14 Jan 2020 10:43:50 +0100 Subject: [PATCH 1/2] log Flow activity Signed-off-by: Arthur Schiwon --- apps/workflowengine/appinfo/info.xml | 4 + .../composer/composer/autoload_classmap.php | 3 + .../composer/composer/autoload_static.php | 3 + .../lib/AppInfo/Application.php | 15 ++ .../lib/BackgroundJobs/Rotate.php | 53 ++++++ apps/workflowengine/lib/Helper/LogContext.php | 95 ++++++++++ apps/workflowengine/lib/Manager.php | 9 +- apps/workflowengine/lib/Service/Logger.php | 170 ++++++++++++++++++ .../lib/Service/RuleMatcher.php | 50 +++++- lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/Log.php | 25 ++- lib/public/Log/IDataLogger.php | 42 +++++ 13 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 apps/workflowengine/lib/BackgroundJobs/Rotate.php create mode 100644 apps/workflowengine/lib/Helper/LogContext.php create mode 100644 apps/workflowengine/lib/Service/Logger.php create mode 100644 lib/public/Log/IDataLogger.php diff --git a/apps/workflowengine/appinfo/info.xml b/apps/workflowengine/appinfo/info.xml index 8b672735ff..10ce7661e5 100644 --- a/apps/workflowengine/appinfo/info.xml +++ b/apps/workflowengine/appinfo/info.xml @@ -25,6 +25,10 @@ + + OCA\WorkflowEngine\BackgroundJobs\Rotate + + OCA\WorkflowEngine\Migration\PopulateNewlyIntroducedDatabaseFields diff --git a/apps/workflowengine/composer/composer/autoload_classmap.php b/apps/workflowengine/composer/composer/autoload_classmap.php index e90b6edd4e..bcbc9af457 100644 --- a/apps/workflowengine/composer/composer/autoload_classmap.php +++ b/apps/workflowengine/composer/composer/autoload_classmap.php @@ -7,6 +7,7 @@ $baseDir = $vendorDir; return array( 'OCA\\WorkflowEngine\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', + 'OCA\\WorkflowEngine\\BackgroundJobs\\Rotate' => $baseDir . '/../lib/BackgroundJobs/Rotate.php', 'OCA\\WorkflowEngine\\Check\\AbstractStringCheck' => $baseDir . '/../lib/Check/AbstractStringCheck.php', 'OCA\\WorkflowEngine\\Check\\FileMimeType' => $baseDir . '/../lib/Check/FileMimeType.php', 'OCA\\WorkflowEngine\\Check\\FileName' => $baseDir . '/../lib/Check/FileName.php', @@ -24,10 +25,12 @@ return array( 'OCA\\WorkflowEngine\\Controller\\RequestTime' => $baseDir . '/../lib/Controller/RequestTime.php', 'OCA\\WorkflowEngine\\Controller\\UserWorkflowsController' => $baseDir . '/../lib/Controller/UserWorkflowsController.php', 'OCA\\WorkflowEngine\\Entity\\File' => $baseDir . '/../lib/Entity/File.php', + 'OCA\\WorkflowEngine\\Helper\\LogContext' => $baseDir . '/../lib/Helper/LogContext.php', 'OCA\\WorkflowEngine\\Helper\\ScopeContext' => $baseDir . '/../lib/Helper/ScopeContext.php', 'OCA\\WorkflowEngine\\Manager' => $baseDir . '/../lib/Manager.php', 'OCA\\WorkflowEngine\\Migration\\PopulateNewlyIntroducedDatabaseFields' => $baseDir . '/../lib/Migration/PopulateNewlyIntroducedDatabaseFields.php', 'OCA\\WorkflowEngine\\Migration\\Version2000Date20190808074233' => $baseDir . '/../lib/Migration/Version2000Date20190808074233.php', + 'OCA\\WorkflowEngine\\Service\\Logger' => $baseDir . '/../lib/Service/Logger.php', 'OCA\\WorkflowEngine\\Service\\RuleMatcher' => $baseDir . '/../lib/Service/RuleMatcher.php', 'OCA\\WorkflowEngine\\Settings\\ASettings' => $baseDir . '/../lib/Settings/ASettings.php', 'OCA\\WorkflowEngine\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php', diff --git a/apps/workflowengine/composer/composer/autoload_static.php b/apps/workflowengine/composer/composer/autoload_static.php index 63f1bf74ab..98853a837d 100644 --- a/apps/workflowengine/composer/composer/autoload_static.php +++ b/apps/workflowengine/composer/composer/autoload_static.php @@ -22,6 +22,7 @@ class ComposerStaticInitWorkflowEngine public static $classMap = array ( 'OCA\\WorkflowEngine\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', + 'OCA\\WorkflowEngine\\BackgroundJobs\\Rotate' => __DIR__ . '/..' . '/../lib/BackgroundJobs/Rotate.php', 'OCA\\WorkflowEngine\\Check\\AbstractStringCheck' => __DIR__ . '/..' . '/../lib/Check/AbstractStringCheck.php', 'OCA\\WorkflowEngine\\Check\\FileMimeType' => __DIR__ . '/..' . '/../lib/Check/FileMimeType.php', 'OCA\\WorkflowEngine\\Check\\FileName' => __DIR__ . '/..' . '/../lib/Check/FileName.php', @@ -39,10 +40,12 @@ class ComposerStaticInitWorkflowEngine 'OCA\\WorkflowEngine\\Controller\\RequestTime' => __DIR__ . '/..' . '/../lib/Controller/RequestTime.php', 'OCA\\WorkflowEngine\\Controller\\UserWorkflowsController' => __DIR__ . '/..' . '/../lib/Controller/UserWorkflowsController.php', 'OCA\\WorkflowEngine\\Entity\\File' => __DIR__ . '/..' . '/../lib/Entity/File.php', + 'OCA\\WorkflowEngine\\Helper\\LogContext' => __DIR__ . '/..' . '/../lib/Helper/LogContext.php', 'OCA\\WorkflowEngine\\Helper\\ScopeContext' => __DIR__ . '/..' . '/../lib/Helper/ScopeContext.php', 'OCA\\WorkflowEngine\\Manager' => __DIR__ . '/..' . '/../lib/Manager.php', 'OCA\\WorkflowEngine\\Migration\\PopulateNewlyIntroducedDatabaseFields' => __DIR__ . '/..' . '/../lib/Migration/PopulateNewlyIntroducedDatabaseFields.php', 'OCA\\WorkflowEngine\\Migration\\Version2000Date20190808074233' => __DIR__ . '/..' . '/../lib/Migration/Version2000Date20190808074233.php', + 'OCA\\WorkflowEngine\\Service\\Logger' => __DIR__ . '/..' . '/../lib/Service/Logger.php', 'OCA\\WorkflowEngine\\Service\\RuleMatcher' => __DIR__ . '/..' . '/../lib/Service/RuleMatcher.php', 'OCA\\WorkflowEngine\\Settings\\ASettings' => __DIR__ . '/..' . '/../lib/Settings/ASettings.php', 'OCA\\WorkflowEngine\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php', diff --git a/apps/workflowengine/lib/AppInfo/Application.php b/apps/workflowengine/lib/AppInfo/Application.php index efa1943d75..289ac167ac 100644 --- a/apps/workflowengine/lib/AppInfo/Application.php +++ b/apps/workflowengine/lib/AppInfo/Application.php @@ -22,7 +22,9 @@ namespace OCA\WorkflowEngine\AppInfo; use OCA\WorkflowEngine\Controller\RequestTime; +use OCA\WorkflowEngine\Helper\LogContext; use OCA\WorkflowEngine\Manager; +use OCA\WorkflowEngine\Service\Logger; use OCP\AppFramework\QueryException; use OCP\EventDispatcher\Event; use OCP\Template; @@ -97,6 +99,16 @@ class Application extends \OCP\AppFramework\App { $ruleMatcher->setEntity($entity); $ruleMatcher->setOperation($operation); + $ctx = new LogContext(); + $ctx + ->setOperation($operation) + ->setEntity($entity) + ->setEventName($eventName); + + /** @var Logger $flowLogger */ + $flowLogger = $this->getContainer()->query(Logger::class); + $flowLogger->logEventInit($ctx); + if ($event instanceof Event) { $entity->prepareRuleMatcher($ruleMatcher, $eventName, $event); $operation->onEvent($eventName, $event, $ruleMatcher); @@ -117,6 +129,7 @@ class Application extends \OCP\AppFramework\App { ] ); } + $flowLogger->logEventDone($ctx); } catch (QueryException $e) { // Ignore query exceptions since they might occur when an entity/operation were setup before by an app that is disabled now @@ -126,5 +139,7 @@ class Application extends \OCP\AppFramework\App { }, $eventNames ?? []); } } + + } } diff --git a/apps/workflowengine/lib/BackgroundJobs/Rotate.php b/apps/workflowengine/lib/BackgroundJobs/Rotate.php new file mode 100644 index 0000000000..1602e5a72d --- /dev/null +++ b/apps/workflowengine/lib/BackgroundJobs/Rotate.php @@ -0,0 +1,53 @@ + + * + * @author Arthur Schiwon + * + * @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 . + * + */ + +namespace OCA\WorkflowEngine\BackgroundJobs; + +use OC\BackgroundJob\TimedJob; +use OCA\WorkflowEngine\AppInfo\Application; +use OCP\Log\RotationTrait; + +class Rotate extends TimedJob { + use RotationTrait; + + public function __construct() { + $this->setInterval(60*60*3); + } + + protected function run($argument) { + $config = \OC::$server->getConfig(); + $default = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/flow.log'; + $this->filePath = trim((string)$config->getAppValue(Application::APP_ID, 'logfile', $default)); + + if($this->filePath === '') { + // disabled, nothing to do + return; + } + + $this->maxSize = $config->getSystemValue('log_rotate_size', 100 * 1024 * 1024); + + if($this->shouldRotateBySize()) { + $this->rotate(); + } + } +} diff --git a/apps/workflowengine/lib/Helper/LogContext.php b/apps/workflowengine/lib/Helper/LogContext.php new file mode 100644 index 0000000000..786aa7cf85 --- /dev/null +++ b/apps/workflowengine/lib/Helper/LogContext.php @@ -0,0 +1,95 @@ + + * + * @author Arthur Schiwon + * + * @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 . + * + */ + +namespace OCA\WorkflowEngine\Helper; + +use OCP\WorkflowEngine\IEntity; +use OCP\WorkflowEngine\IManager; +use OCP\WorkflowEngine\IOperation; + +class LogContext { + /** @var array */ + protected $details; + + public function setDescription(string $description): LogContext { + $this->details['description'] = $description; + return $this; + } + + public function setScopes(array $scopes): LogContext { + $this->details['scopes'] = []; + foreach ($scopes as $scope) { + if($scope instanceof ScopeContext) { + switch($scope->getScope()) { + case IManager::SCOPE_ADMIN: + $this->details['scopes'][] = ['scope' => 'admin']; + break; + case IManager::SCOPE_USER: + $this->details['scopes'][] = [ + 'scope' => 'user', + 'uid' => $scope->getScopeId(), + ]; + break; + default: + continue; + } + } + } + return $this; + } + + public function setOperation(?IOperation $operation): LogContext { + if($operation instanceof IOperation) { + $this->details['operation'] = [ + 'class' => get_class($operation), + 'name' => $operation->getDisplayName(), + ]; + } + return $this; + } + + public function setEntity(?IEntity $entity): LogContext { + if($entity instanceof IEntity) { + $this->details['entity'] = [ + 'class' => get_class($entity), + 'name' => $entity->getName(), + ]; + } + return $this; + } + + public function setConfiguration(array $configuration): LogContext { + $this->details['configuration'] = $configuration; + return $this; + } + + public function setEventName(string $eventName): LogContext { + $this->details['eventName'] = $eventName; + return $this; + } + + public function getDetails(): array { + return $this->details; + } +} diff --git a/apps/workflowengine/lib/Manager.php b/apps/workflowengine/lib/Manager.php index 5ba2533ffe..2818238081 100644 --- a/apps/workflowengine/lib/Manager.php +++ b/apps/workflowengine/lib/Manager.php @@ -34,6 +34,7 @@ use OCA\WorkflowEngine\Check\RequestUserAgent; use OCA\WorkflowEngine\Check\UserGroupMembership; use OCA\WorkflowEngine\Entity\File; use OCA\WorkflowEngine\Helper\ScopeContext; +use OCA\WorkflowEngine\Service\Logger; use OCA\WorkflowEngine\Service\RuleMatcher; use OCP\AppFramework\QueryException; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -127,7 +128,13 @@ class Manager implements IManager { } public function getRuleMatcher(): IRuleMatcher { - return new RuleMatcher($this->session, $this->container, $this->l, $this); + return new RuleMatcher( + $this->session, + $this->container, + $this->l, + $this, + $this->container->query(Logger::class) + ); } public function getAllConfiguredEvents() { diff --git a/apps/workflowengine/lib/Service/Logger.php b/apps/workflowengine/lib/Service/Logger.php new file mode 100644 index 0000000000..0bf09ed8a7 --- /dev/null +++ b/apps/workflowengine/lib/Service/Logger.php @@ -0,0 +1,170 @@ + + * + * @author Arthur Schiwon + * + * @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 . + * + */ + +namespace OCA\WorkflowEngine\Service; + +use OCA\WorkflowEngine\AppInfo\Application; +use OCA\WorkflowEngine\Helper\LogContext; +use OCP\IConfig; +use OCP\ILogger; +use OCP\Log\IDataLogger; +use OCP\Log\ILogFactory; + +class Logger { + /** @var ILogger */ + protected $generalLogger; + /** @var ILogger */ + protected $flowLogger; + /** @var IConfig */ + private $config; + /** @var ILogFactory */ + private $logFactory; + + public function __construct(ILogger $generalLogger, IConfig $config, ILogFactory $logFactory) { + $this->generalLogger = $generalLogger; + $this->config = $config; + $this->logFactory = $logFactory; + + $this->initLogger(); + } + + protected function initLogger() { + $default = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/flow.log'; + $logFile = trim((string)$this->config->getAppValue(Application::APP_ID, 'logfile', $default)); + if($logFile !== '') { + $this->flowLogger = $this->logFactory->getCustomLogger($logFile); + } + } + + public function logFlowRequests(LogContext $logContext) { + $message = 'Flow activation: rules were requested for operation {op}'; + $context = ['op' => $logContext->getDetails()['operation']['name']]; + + $logContext->setDescription('Flow activation: rules were requested'); + + $this->log($message, $context, $logContext); + } + + public function logScopeExpansion(LogContext $logContext) { + $message = 'Flow rule of a different user is legit for operation {op}'; + $context = ['op' => $logContext->getDetails()['operation']['name']]; + + $logContext->setDescription('Flow rule of a different user is legit'); + + $this->log($message, $context, $logContext); + } + + public function logPassedCheck(LogContext $logContext) { + $message = 'Flow rule qualified to run {op}, config: {config}'; + $context = [ + 'op' => $logContext->getDetails()['operation']['name'], + 'config' => $logContext->getDetails()['configuration'], + ]; + + $logContext->setDescription('Flow rule qualified to run'); + + $this->log($message, $context, $logContext); + } + + public function logRunSingle(LogContext $logContext) { + $message = 'Last qualified flow configuration is going to run {op}'; + $context = [ + 'op' => $logContext->getDetails()['operation']['name'], + ]; + + $logContext->setDescription('Last qualified flow configuration is going to run'); + + $this->log($message, $context, $logContext); + } + + public function logRunAll(LogContext $logContext) { + $message = 'All qualified flow configurations are going to run {op}'; + $context = [ + 'op' => $logContext->getDetails()['operation']['name'], + ]; + + $logContext->setDescription('All qualified flow configurations are going to run'); + + $this->log($message, $context, $logContext); + } + + public function logRunNone(LogContext $logContext) { + $message = 'No flow configurations is going to run {op}'; + $context = [ + 'op' => $logContext->getDetails()['operation']['name'], + ]; + + $logContext->setDescription('No flow configurations is going to run'); + + $this->log($message, $context, $logContext); + } + + public function logEventInit(LogContext $logContext) { + $message = 'Flow activated by event {ev}'; + + $context = [ + 'ev' => $logContext->getDetails()['eventName'], + ]; + + $logContext->setDescription('Flow activated by event'); + + $this->log($message, $context, $logContext); + } + + public function logEventDone(LogContext $logContext) { + $message = 'Flow handling done for event {ev}'; + + $context = [ + 'ev' => $logContext->getDetails()['eventName'], + ]; + + $logContext->setDescription('Flow handling for event done'); + + $this->log($message, $context, $logContext); + } + + protected function log( + string $message, + array $context, + LogContext $logContext + ): void + { + if(!isset($context['app'])) { + $context['app'] = Application::APP_ID; + } + if(!isset($context['level'])) { + $context['level'] = ILogger::INFO; + } + $this->generalLogger->log($context['level'], $message, $context); + + if(!$this->flowLogger instanceof IDataLogger) { + return; + } + + $this->flowLogger->logData( + $logContext->getDetails(), + ['app' => Application::APP_ID, 'level' => $context['level']] + ); + } +} diff --git a/apps/workflowengine/lib/Service/RuleMatcher.php b/apps/workflowengine/lib/Service/RuleMatcher.php index 57e3813c30..d73fe752f7 100644 --- a/apps/workflowengine/lib/Service/RuleMatcher.php +++ b/apps/workflowengine/lib/Service/RuleMatcher.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace OCA\WorkflowEngine\Service; +use OCA\WorkflowEngine\Helper\LogContext; use OCA\WorkflowEngine\Helper\ScopeContext; use OCA\WorkflowEngine\Manager; use OCP\AppFramework\QueryException; @@ -58,17 +59,21 @@ class RuleMatcher implements IRuleMatcher { protected $operation; /** @var IEntity */ protected $entity; + /** @var Logger */ + protected $logger; public function __construct( IUserSession $session, IServerContainer $container, IL10N $l, - Manager $manager + Manager $manager, + Logger $logger ) { $this->session = $session; $this->manager = $manager; $this->container = $container; $this->l = $l; + $this->logger = $logger; } public function setFileInfo(IStorage $storage, string $path, bool $isDir = false): void { @@ -116,6 +121,13 @@ class RuleMatcher implements IRuleMatcher { $scopes[] = new ScopeContext(IManager::SCOPE_USER, $user->getUID()); } + $ctx = new LogContext(); + $ctx + ->setScopes($scopes) + ->setEntity($this->entity) + ->setOperation($this->operation); + $this->logger->logFlowRequests($ctx); + $operations = []; foreach ($scopes as $scope) { $operations = array_merge($operations, $this->manager->getOperations($class, $scope)); @@ -129,6 +141,12 @@ class RuleMatcher implements IRuleMatcher { continue; } if ($this->entity->isLegitimatedForUserId($scopeCandidate->getScopeId())) { + $ctx = new LogContext(); + $ctx + ->setScopes($scopeCandidate) + ->setEntity($this->entity) + ->setOperation($this->operation); + $this->logger->logScopeExpansion($ctx); $operations = array_merge($operations, $this->manager->getOperations($class, $scopeCandidate)); } } @@ -146,12 +164,36 @@ class RuleMatcher implements IRuleMatcher { } } + $ctx = new LogContext(); + $ctx + ->setEntity($this->entity) + ->setOperation($this->operation) + ->setConfiguration($operation); + $this->logger->logPassedCheck($ctx); + if ($returnFirstMatchingOperationOnly) { + $ctx = new LogContext(); + $ctx + ->setEntity($this->entity) + ->setOperation($this->operation) + ->setConfiguration($operation); + $this->logger->logRunSingle($ctx); return $operation; } $matches[] = $operation; } + $ctx = new LogContext(); + $ctx + ->setEntity($this->entity) + ->setOperation($this->operation); + if(!empty($matches)) { + $ctx->setConfiguration($matches); + $this->logger->logRunAll($ctx); + } else { + $this->logger->logRunNone($ctx); + } + return $matches; } @@ -183,4 +225,10 @@ class RuleMatcher implements IRuleMatcher { } return $checkInstance->executeCheck($check['operator'], $check['value']); } + + protected function logCandidate() { + $logContext = new LogContext(); + $logContext + ->setOperation(); + } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 5edd4f2e1a..d66039ae1f 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -367,6 +367,7 @@ return array( 'OCP\\Lock\\LockedException' => $baseDir . '/lib/public/Lock/LockedException.php', 'OCP\\Lock\\ManuallyLockedException' => $baseDir . '/lib/public/Lock/ManuallyLockedException.php', 'OCP\\Lockdown\\ILockdownManager' => $baseDir . '/lib/public/Lockdown/ILockdownManager.php', + 'OCP\\Log\\IDataLogger' => $baseDir . '/lib/public/Log/IDataLogger.php', 'OCP\\Log\\IFileBased' => $baseDir . '/lib/public/Log/IFileBased.php', 'OCP\\Log\\ILogFactory' => $baseDir . '/lib/public/Log/ILogFactory.php', 'OCP\\Log\\IWriter' => $baseDir . '/lib/public/Log/IWriter.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index f81dbd7e16..0a65eae24b 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -396,6 +396,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Lock\\LockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/LockedException.php', 'OCP\\Lock\\ManuallyLockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/ManuallyLockedException.php', 'OCP\\Lockdown\\ILockdownManager' => __DIR__ . '/../../..' . '/lib/public/Lockdown/ILockdownManager.php', + 'OCP\\Log\\IDataLogger' => __DIR__ . '/../../..' . '/lib/public/Log/IDataLogger.php', 'OCP\\Log\\IFileBased' => __DIR__ . '/../../..' . '/lib/public/Log/IFileBased.php', 'OCP\\Log\\ILogFactory' => __DIR__ . '/../../..' . '/lib/public/Log/ILogFactory.php', 'OCP\\Log\\IWriter' => __DIR__ . '/../../..' . '/lib/public/Log/IWriter.php', diff --git a/lib/private/Log.php b/lib/private/Log.php index 916d557003..8d51a673a4 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -36,6 +36,7 @@ declare(strict_types=1); namespace OC; +use OCP\Log\IDataLogger; use function array_merge; use InterfaSys\LogNormalizer\Normalizer; @@ -54,7 +55,7 @@ use OCP\Support\CrashReport\IRegistry; * * MonoLog is an example implementing this interface. */ -class Log implements ILogger { +class Log implements ILogger, IDataLogger { /** @var IWriter */ private $logger; @@ -339,6 +340,28 @@ class Log implements ILogger { } } + public function logData(array $data, array $context = []): void { + $app = $context['app'] ?? 'no app in context'; + $level = $context['level'] ?? ILogger::ERROR; + + $minLevel = $this->getLogLevel($context); + + array_walk($context, [$this->normalizer, 'format']); + + try { + if ($level >= $minLevel) { + if (!$this->logger instanceof IFileBased) { + $data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_SLASHES); + } + $this->writeLog($app, $data, $level); + } + + $context['level'] = $level; + } catch (\Throwable $e) { + // make sure we dont hard crash if logging fails + } + } + /** * @param string $app * @param string|array $entry diff --git a/lib/public/Log/IDataLogger.php b/lib/public/Log/IDataLogger.php new file mode 100644 index 0000000000..895ba43f5c --- /dev/null +++ b/lib/public/Log/IDataLogger.php @@ -0,0 +1,42 @@ + + * + * @author Arthur Schiwon + * + * @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 . + * + */ + +namespace OCP\Log; + +/** + * Interface IDataLogger + * + * @package OCP\Log + * @since 18.0.0 + */ +interface IDataLogger { + + /** + * allows to log custom data, similar to how logException works + * + * @since 18.0.0 + */ + public function logData(array $data, array $context = []): void; + +} From e00844488709552febdc07c19bed4c7ceb52a98d Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 28 Jan 2020 13:00:13 +0100 Subject: [PATCH 2/2] always require a message paramter for data logging also ensure it plays well with current log reader Signed-off-by: Arthur Schiwon --- apps/workflowengine/lib/Helper/LogContext.php | 2 +- apps/workflowengine/lib/Service/Logger.php | 4 +++- lib/private/Log.php | 3 ++- lib/private/Log/LogDetails.php | 10 ++++++++++ lib/public/Log/IDataLogger.php | 6 +++--- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/workflowengine/lib/Helper/LogContext.php b/apps/workflowengine/lib/Helper/LogContext.php index 786aa7cf85..548e872207 100644 --- a/apps/workflowengine/lib/Helper/LogContext.php +++ b/apps/workflowengine/lib/Helper/LogContext.php @@ -33,7 +33,7 @@ class LogContext { protected $details; public function setDescription(string $description): LogContext { - $this->details['description'] = $description; + $this->details['message'] = $description; return $this; } diff --git a/apps/workflowengine/lib/Service/Logger.php b/apps/workflowengine/lib/Service/Logger.php index 0bf09ed8a7..8b90e6fa15 100644 --- a/apps/workflowengine/lib/Service/Logger.php +++ b/apps/workflowengine/lib/Service/Logger.php @@ -162,8 +162,10 @@ class Logger { return; } + $details = $logContext->getDetails(); $this->flowLogger->logData( - $logContext->getDetails(), + $details['message'], + $details, ['app' => Application::APP_ID, 'level' => $context['level']] ); } diff --git a/lib/private/Log.php b/lib/private/Log.php index 8d51a673a4..d288e72417 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -340,7 +340,7 @@ class Log implements ILogger, IDataLogger { } } - public function logData(array $data, array $context = []): void { + public function logData(string $message, array $data, array $context = []): void { $app = $context['app'] ?? 'no app in context'; $level = $context['level'] ?? ILogger::ERROR; @@ -350,6 +350,7 @@ class Log implements ILogger, IDataLogger { try { if ($level >= $minLevel) { + $data['message'] = $message; if (!$this->logger instanceof IFileBased) { $data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_SLASHES); } diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php index 2eea17ad73..b1dc6e4311 100644 --- a/lib/private/Log/LogDetails.php +++ b/lib/private/Log/LogDetails.php @@ -80,6 +80,16 @@ abstract class LogDetails { 'userAgent', 'version' ); + + if(is_array($message) && !array_key_exists('Exception', $message)) { + // Exception messages should stay as they are, + // anything else modern is split to 'message' (string) and + // data (array) fields + $shortMessage = $message['message'] ?? '(no message provided)'; + $entry['data'] = $message; + $entry['message'] = $shortMessage; + } + return $entry; } diff --git a/lib/public/Log/IDataLogger.php b/lib/public/Log/IDataLogger.php index 895ba43f5c..b5d3aa3075 100644 --- a/lib/public/Log/IDataLogger.php +++ b/lib/public/Log/IDataLogger.php @@ -28,15 +28,15 @@ namespace OCP\Log; * Interface IDataLogger * * @package OCP\Log - * @since 18.0.0 + * @since 18.0.1 */ interface IDataLogger { /** * allows to log custom data, similar to how logException works * - * @since 18.0.0 + * @since 18.0.1 */ - public function logData(array $data, array $context = []): void; + public function logData(string $message, array $data, array $context = []): void; }