From cfc3ab0119d23a4c8ccccefb13d2271b0c0675ae Mon Sep 17 00:00:00 2001
From: Arthur Schiwon <blizzz@arthur-schiwon.de>
Date: Wed, 25 Apr 2018 02:27:43 +0200
Subject: [PATCH] offer API to create own File log. admin_audit makes use of it

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
---
 apps/admin_audit/lib/AppInfo/Application.php  | 96 +++++++++++--------
 .../Sabre/ExceptionLoggerPluginTest.php       |  2 +-
 lib/composer/composer/autoload_classmap.php   |  3 +-
 lib/composer/composer/autoload_static.php     |  3 +-
 lib/private/Log.php                           | 19 ++--
 lib/private/Log/Errorlog.php                  |  6 +-
 lib/private/Log/File.php                      | 23 +++--
 lib/private/Log/LogFactory.php                | 23 +++--
 lib/private/Log/Syslog.php                    |  5 +-
 lib/private/Server.php                        | 16 +++-
 lib/public/IServerContainer.php               |  9 ++
 lib/public/Log/ILogFactory.php                | 48 ++++++++++
 .../IWritable.php => public/Log/IWriter.php}  | 15 ++-
 tests/lib/Log/FileTest.php                    |  4 +-
 tests/lib/LoggerTest.php                      |  9 +-
 15 files changed, 198 insertions(+), 83 deletions(-)
 create mode 100644 lib/public/Log/ILogFactory.php
 rename lib/{private/Log/IWritable.php => public/Log/IWriter.php} (81%)

diff --git a/apps/admin_audit/lib/AppInfo/Application.php b/apps/admin_audit/lib/AppInfo/Application.php
index df39e3eb111..f0387e86057 100644
--- a/apps/admin_audit/lib/AppInfo/Application.php
+++ b/apps/admin_audit/lib/AppInfo/Application.php
@@ -53,8 +53,24 @@ use OCP\Share;
 
 class Application extends App {
 
+	/** @var ILogger */
+	protected $logger;
+
 	public function __construct() {
 		parent::__construct('admin_audit');
+		$this->initLogger();
+	}
+
+	public function initLogger() {
+		$c = $this->getContainer()->getServer();
+
+		$logFile = $c->getConfig()->getAppValue('admin_audit', 'logfile', null);
+		if($logFile === null) {
+			$this->logger = $c->getLogger();
+			return;
+		}
+		$this->logger = $c->getLogFactory()->getCustomLogger($logFile);
+
 	}
 
 	public function register() {
@@ -65,26 +81,24 @@ class Application extends App {
 	 * Register hooks in order to log them
 	 */
 	protected function registerHooks() {
-		$logger = $this->getContainer()->getServer()->getLogger();
-
-		$this->userManagementHooks($logger);
-		$this->groupHooks($logger);
-		$this->authHooks($logger);
+		$this->userManagementHooks();
+		$this->groupHooks();
+		$this->authHooks();
 
-		$this->consoleHooks($logger);
-		$this->appHooks($logger);
+		$this->consoleHooks();
+		$this->appHooks();
 
-		$this->sharingHooks($logger);
+		$this->sharingHooks();
 
-		$this->fileHooks($logger);
-		$this->trashbinHooks($logger);
-		$this->versionsHooks($logger);
+		$this->fileHooks();
+		$this->trashbinHooks();
+		$this->versionsHooks();
 
-		$this->securityHooks($logger);
+		$this->securityHooks();
 	}
 
-	protected function userManagementHooks(ILogger $logger) {
-		$userActions = new UserManagement($logger);
+	protected function userManagementHooks() {
+		$userActions = new UserManagement($this->logger);
 
 		Util::connectHook('OC_User', 'post_createUser',	$userActions, 'create');
 		Util::connectHook('OC_User', 'post_deleteUser',	$userActions, 'delete');
@@ -97,8 +111,8 @@ class Application extends App {
 		$userSession->listen('\OC\User', 'postUnassignedUserId', [$userActions, 'unassign']);
 	}
 
-	protected function groupHooks(ILogger $logger)  {
-		$groupActions = new GroupManagement($logger);
+	protected function groupHooks()  {
+		$groupActions = new GroupManagement($this->logger);
 
 		/** @var IGroupManager|Manager $groupManager */
 		$groupManager = $this->getContainer()->getServer()->getGroupManager();
@@ -108,8 +122,8 @@ class Application extends App {
 		$groupManager->listen('\OC\Group', 'postCreate',  [$groupActions, 'createGroup']);
 	}
 
-	protected function sharingHooks(ILogger $logger) {
-		$shareActions = new Sharing($logger);
+	protected function sharingHooks() {
+		$shareActions = new Sharing($this->logger);
 
 		Util::connectHook(Share::class, 'post_shared', $shareActions, 'shared');
 		Util::connectHook(Share::class, 'post_unshare', $shareActions, 'unshare');
@@ -119,42 +133,42 @@ class Application extends App {
 		Util::connectHook(Share::class, 'share_link_access', $shareActions, 'shareAccessed');
 	}
 
-	protected function authHooks(ILogger $logger) {
-		$authActions = new Auth($logger);
+	protected function authHooks() {
+		$authActions = new Auth($this->logger);
 
 		Util::connectHook('OC_User', 'pre_login', $authActions, 'loginAttempt');
 		Util::connectHook('OC_User', 'post_login', $authActions, 'loginSuccessful');
 		Util::connectHook('OC_User', 'logout', $authActions, 'logout');
 	}
 
-	protected function appHooks(ILogger $logger) {
+	protected function appHooks() {
 
 		$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
-		$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, function(ManagerEvent $event) use ($logger) {
-			$appActions = new AppManagement($logger);
+		$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, function(ManagerEvent $event) {
+			$appActions = new AppManagement($this->logger);
 			$appActions->enableApp($event->getAppID());
 		});
-		$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, function(ManagerEvent $event) use ($logger) {
-			$appActions = new AppManagement($logger);
+		$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, function(ManagerEvent $event) {
+			$appActions = new AppManagement($this->logger);
 			$appActions->enableAppForGroups($event->getAppID(), $event->getGroups());
 		});
-		$eventDispatcher->addListener(ManagerEvent::EVENT_APP_DISABLE, function(ManagerEvent $event) use ($logger) {
-			$appActions = new AppManagement($logger);
+		$eventDispatcher->addListener(ManagerEvent::EVENT_APP_DISABLE, function(ManagerEvent $event) {
+			$appActions = new AppManagement($this->logger);
 			$appActions->disableApp($event->getAppID());
 		});
 
 	}
 
-	protected function consoleHooks(ILogger $logger) {
+	protected function consoleHooks() {
 		$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
-		$eventDispatcher->addListener(ConsoleEvent::EVENT_RUN, function(ConsoleEvent $event) use ($logger) {
-			$appActions = new Console($logger);
+		$eventDispatcher->addListener(ConsoleEvent::EVENT_RUN, function(ConsoleEvent $event) {
+			$appActions = new Console($this->logger);
 			$appActions->runCommand($event->getArguments());
 		});
 	}
 
-	protected function fileHooks(ILogger $logger) {
-		$fileActions = new Files($logger);
+	protected function fileHooks() {
+		$fileActions = new Files($this->logger);
 		$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
 		$eventDispatcher->addListener(
 			IPreview::EVENT,
@@ -215,26 +229,26 @@ class Application extends App {
 		);
 	}
 
-	protected function versionsHooks(ILogger $logger) {
-		$versionsActions = new Versions($logger);
+	protected function versionsHooks() {
+		$versionsActions = new Versions($this->logger);
 		Util::connectHook('\OCP\Versions', 'rollback', $versionsActions, 'rollback');
 		Util::connectHook('\OCP\Versions', 'delete',$versionsActions, 'delete');
 	}
 
-	protected function trashbinHooks(ILogger $logger) {
-		$trashActions = new Trashbin($logger);
+	protected function trashbinHooks() {
+		$trashActions = new Trashbin($this->logger);
 		Util::connectHook('\OCP\Trashbin', 'preDelete', $trashActions, 'delete');
 		Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $trashActions, 'restore');
 	}
 
-	protected function securityHooks(ILogger $logger) {
+	protected function securityHooks() {
 		$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
-		$eventDispatcher->addListener(IProvider::EVENT_SUCCESS, function(GenericEvent $event) use ($logger) {
-			$security = new Security($logger);
+		$eventDispatcher->addListener(IProvider::EVENT_SUCCESS, function(GenericEvent $event) {
+			$security = new Security($this->logger);
 			$security->twofactorSuccess($event->getSubject(), $event->getArguments());
 		});
-		$eventDispatcher->addListener(IProvider::EVENT_FAILED, function(GenericEvent $event) use ($logger) {
-			$security = new Security($logger);
+		$eventDispatcher->addListener(IProvider::EVENT_FAILED, function(GenericEvent $event) {
+			$security = new Security($this->logger);
 			$security->twofactorFailed($event->getSubject(), $event->getArguments());
 		});
 	}
diff --git a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php
index 2f5d4ac4b77..1de9333207f 100644
--- a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php
@@ -69,7 +69,7 @@ class ExceptionLoggerPluginTest extends TestCase {
 			});
 
 		$this->server = new Server();
-		$this->logger = new TestLogger(new Log\File(\OC::$SERVERROOT.'/data/nextcloud.log'), $config);
+		$this->logger = new TestLogger(new Log\File(\OC::$SERVERROOT.'/data/nextcloud.log', '', $config), $config);
 		$this->plugin = new PluginToTest('unit-test', $this->logger);
 		$this->plugin->initialize($this->server);
 	}
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index f706fea2cbe..2c079a2c295 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -239,6 +239,8 @@ return array(
     'OCP\\Lock\\ILockingProvider' => $baseDir . '/lib/public/Lock/ILockingProvider.php',
     'OCP\\Lock\\LockedException' => $baseDir . '/lib/public/Lock/LockedException.php',
     'OCP\\Lockdown\\ILockdownManager' => $baseDir . '/lib/public/Lockdown/ILockdownManager.php',
+    'OCP\\Log\\ILogFactory' => $baseDir . '/lib/public/Log/ILogFactory.php',
+    'OCP\\Log\\IWriter' => $baseDir . '/lib/public/Log/IWriter.php',
     'OCP\\Mail\\IAttachment' => $baseDir . '/lib/public/Mail/IAttachment.php',
     'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php',
     'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php',
@@ -751,7 +753,6 @@ return array(
     'OC\\Log\\ExceptionSerializer' => $baseDir . '/lib/private/Log/ExceptionSerializer.php',
     'OC\\Log\\File' => $baseDir . '/lib/private/Log/File.php',
     'OC\\Log\\IFileBased' => $baseDir . '/lib/private/Log/IFileBased.php',
-    'OC\\Log\\IWritable' => $baseDir . '/lib/private/Log/IWritable.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',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index b879dc5a214..f76c626a069 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -269,6 +269,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OCP\\Lock\\ILockingProvider' => __DIR__ . '/../../..' . '/lib/public/Lock/ILockingProvider.php',
         'OCP\\Lock\\LockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/LockedException.php',
         'OCP\\Lockdown\\ILockdownManager' => __DIR__ . '/../../..' . '/lib/public/Lockdown/ILockdownManager.php',
+        'OCP\\Log\\ILogFactory' => __DIR__ . '/../../..' . '/lib/public/Log/ILogFactory.php',
+        'OCP\\Log\\IWriter' => __DIR__ . '/../../..' . '/lib/public/Log/IWriter.php',
         'OCP\\Mail\\IAttachment' => __DIR__ . '/../../..' . '/lib/public/Mail/IAttachment.php',
         'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php',
         'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php',
@@ -781,7 +783,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OC\\Log\\ExceptionSerializer' => __DIR__ . '/../../..' . '/lib/private/Log/ExceptionSerializer.php',
         'OC\\Log\\File' => __DIR__ . '/../../..' . '/lib/private/Log/File.php',
         'OC\\Log\\IFileBased' => __DIR__ . '/../../..' . '/lib/private/Log/IFileBased.php',
-        'OC\\Log\\IWritable' => __DIR__ . '/../../..' . '/lib/private/Log/IWritable.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',
diff --git a/lib/private/Log.php b/lib/private/Log.php
index c6f676db588..313201460a2 100644
--- a/lib/private/Log.php
+++ b/lib/private/Log.php
@@ -39,7 +39,8 @@ use InterfaSys\LogNormalizer\Normalizer;
 
 use OC\Log\ExceptionSerializer;
 use OC\Log\IFileBased;
-use OC\Log\IWritable;
+use OCP\IConfig;
+use OCP\Log\IWriter;
 use OCP\ILogger;
 use OCP\Support\CrashReport\IRegistry;
 use OCP\Util;
@@ -55,10 +56,10 @@ use OCP\Util;
  */
 class Log implements ILogger {
 
-	/** @var IWritable */
+	/** @var IWriter */
 	private $logger;
 
-	/** @var SystemConfig */
+	/** @var IConfig */
 	private $config;
 
 	/** @var boolean|null cache the result of the log condition check for the request */
@@ -71,15 +72,15 @@ class Log implements ILogger {
 	private $crashReporters;
 
 	/**
-	 * @param IWritable $logger The logger that should be used
-	 * @param SystemConfig $config the system config object
+	 * @param IWriter $logger The logger that should be used
+	 * @param IConfig $config the system config object
 	 * @param Normalizer|null $normalizer
 	 * @param IRegistry|null $registry
 	 */
-	public function __construct(IWritable $logger, SystemConfig $config = null, $normalizer = null, IRegistry $registry = null) {
+	public function __construct(IWriter $logger, IConfig $config = null, $normalizer = null, IRegistry $registry = null) {
 		// FIXME: Add this for backwards compatibility, should be fixed at some point probably
 		if ($config === null) {
-			$config = \OC::$server->getSystemConfig();
+			$config = \OC::$server->getConfig();
 		}
 
 		$this->config = $config;
@@ -257,7 +258,7 @@ class Log implements ILogger {
 		}
 
 		if (isset($context['app'])) {
-			$logCondition = $this->config->getValue('log.condition', []);
+			$logCondition = $this->config->getSystemValue('log.condition', []);
 			$app = $context['app'];
 
 			/**
@@ -271,7 +272,7 @@ class Log implements ILogger {
 			}
 		}
 
-		return min($this->config->getValue('loglevel', ILogger::WARN), ILogger::FATAL);
+		return min($this->config->getSystemValue('loglevel', ILogger::WARN), ILogger::FATAL);
 	}
 
 	/**
diff --git a/lib/private/Log/Errorlog.php b/lib/private/Log/Errorlog.php
index 1302a31fe5c..9dc8b2cc49c 100644
--- a/lib/private/Log/Errorlog.php
+++ b/lib/private/Log/Errorlog.php
@@ -25,7 +25,9 @@
 
 namespace OC\Log;
 
-class Errorlog implements IWritable {
+use OCP\Log\IWriter;
+
+class Errorlog implements IWriter {
 
 	/**
 	 * write a message in the log
@@ -33,7 +35,7 @@ class Errorlog implements IWritable {
 	 * @param string $message
 	 * @param int $level
 	 */
-	public function write($app, $message, $level) {
+	public function write(string $app, $message, int $level) {
 		error_log('[owncloud]['.$app.']['.$level.'] '.$message);
 	}
 }
diff --git a/lib/private/Log/File.php b/lib/private/Log/File.php
index 639e4de8ac7..6e95de229cd 100644
--- a/lib/private/Log/File.php
+++ b/lib/private/Log/File.php
@@ -36,6 +36,8 @@
  */
 
 namespace OC\Log;
+use OCP\IConfig;
+use OCP\Log\IWriter;
 
 use OCP\ILogger;
 
@@ -45,11 +47,13 @@ use OCP\ILogger;
  * Log is saved at data/nextcloud.log (on default)
  */
 
-class File implements IWritable, IFileBased {
+class File implements IWriter, IFileBased {
 	/** @var string */
 	protected $logFile;
+	/** @var IConfig */
+	private $config;
 
-	public function __construct(string $path, string $fallbackPath = '') {
+	public function __construct(string $path, string $fallbackPath = '', IConfig $config) {
 		$this->logFile = $path;
 		if (!file_exists($this->logFile)) {
 			if(
@@ -62,6 +66,7 @@ class File implements IWritable, IFileBased {
 				$this->logFile = $fallbackPath;
 			}
 		}
+		$this->config = $config;
 	}
 
 	/**
@@ -70,12 +75,10 @@ class File implements IWritable, IFileBased {
 	 * @param string|array $message
 	 * @param int $level
 	 */
-	public function write($app, $message, $level) {
-		$config = \OC::$server->getSystemConfig();
-
+	public function write(string $app, $message, int $level) {
 		// default to ISO8601
-		$format = $config->getValue('logdateformat', \DateTime::ATOM);
-		$logTimeZone = $config->getValue('logtimezone', 'UTC');
+		$format = $this->config->getSystemValue('logdateformat', \DateTime::ATOM);
+		$logTimeZone = $this->config->getSystemValue('logtimezone', 'UTC');
 		try {
 			$timezone = new \DateTimeZone($logTimeZone);
 		} catch (\Exception $e) {
@@ -95,7 +98,7 @@ class File implements IWritable, IFileBased {
 		$time = $time->format($format);
 		$url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--';
 		$method = is_string($request->getMethod()) ? $request->getMethod() : '--';
-		if($config->getValue('installed', false)) {
+		if($this->config->getSystemValue('installed', false)) {
 			$user = \OC_User::getUser() ? \OC_User::getUser() : '--';
 		} else {
 			$user = '--';
@@ -104,7 +107,7 @@ class File implements IWritable, IFileBased {
 		if ($userAgent === '') {
 			$userAgent = '--';
 		}
-		$version = $config->getValue('version', '');
+		$version = $this->config->getSystemValue('version', '');
 		$entry = compact(
 			'reqId',
 			'level',
@@ -153,7 +156,7 @@ class File implements IWritable, IFileBased {
 	 * @return array
 	 */
 	public function getEntries($limit=50, $offset=0) {
-		$minLevel = \OC::$server->getSystemConfig()->getValue("loglevel", ILogger::WARN);
+		$minLevel = $this->config->getSystemValue("loglevel", ILogger::WARN);
 		$entries = array();
 		$handle = @fopen($this->logFile, 'rb');
 		if ($handle) {
diff --git a/lib/private/Log/LogFactory.php b/lib/private/Log/LogFactory.php
index 13049083fee..c1a4d00ceaa 100644
--- a/lib/private/Log/LogFactory.php
+++ b/lib/private/Log/LogFactory.php
@@ -23,9 +23,13 @@
 
 namespace OC\Log;
 
+use OC\Log;
+use OCP\ILogger;
 use OCP\IServerContainer;
+use OCP\Log\ILogFactory;
+use OCP\Log\IWriter;
 
-class LogFactory {
+class LogFactory implements ILogFactory {
 	/** @var IServerContainer */
 	private $c;
 
@@ -38,7 +42,7 @@ class LogFactory {
 	 * @return \OC\Log\Errorlog|File|\stdClass
 	 * @throws \OCP\AppFramework\QueryException
 	 */
-	public function get($type) {
+	public function get(string $type):IWriter {
 		switch (strtolower($type)) {
 			case 'errorlog':
 				return new Errorlog();
@@ -51,16 +55,23 @@ class LogFactory {
 			case 'owncloud':
 			case 'nextcloud':
 			default:
-			return $this->buildLogFile();
+				return $this->buildLogFile();
 		}
 	}
 
-	protected function buildLogFile() {
+	public function getCustomLogger(string $path):ILogger {
+		$log = $this->buildLogFile($path);
+		return new Log($log, $this->c->getConfig());
+	}
+
+	protected function buildLogFile(string $logFile = ''):File {
 		$config = $this->c->getConfig();
 		$defaultLogFile = $config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').'/nextcloud.log';
-		$logFile = $config->getSystemValue('logfile', $defaultLogFile);
+		if($logFile === '') {
+			$logFile = $config->getSystemValue('logfile', $defaultLogFile);
+		}
 		$fallback = $defaultLogFile !== $logFile ? $defaultLogFile : '';
 
-		return new File($logFile, $fallback);
+		return new File($logFile, $fallback, $config);
 	}
 }
diff --git a/lib/private/Log/Syslog.php b/lib/private/Log/Syslog.php
index be8ecdebb2e..7bd7467f5c4 100644
--- a/lib/private/Log/Syslog.php
+++ b/lib/private/Log/Syslog.php
@@ -27,8 +27,9 @@ namespace OC\Log;
 
 use OCP\ILogger;
 use OCP\IConfig;
+use OCP\Log\IWriter;
 
-class Syslog implements IWritable {
+class Syslog implements IWriter {
 	static protected $levels = [
 		ILogger::DEBUG => LOG_DEBUG,
 		ILogger::INFO => LOG_INFO,
@@ -48,7 +49,7 @@ class Syslog implements IWritable {
 	 * @param string $message
 	 * @param int $level
 	 */
-	public function write($app, $message, $level) {
+	public function write(string $app, $message, int $level) {
 		$syslog_level = self::$levels[$level];
 		syslog($syslog_level, '{'.$app.'} '.$message);
 	}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index a4608bf7a8f..05772555a03 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -135,6 +135,7 @@ use OCP\ITempManager;
 use OCP\Contacts\ContactsMenu\IActionFactory;
 use OCP\IUser;
 use OCP\Lock\ILockingProvider;
+use OCP\Log\ILogFactory;
 use OCP\Remote\Api\IApiFactory;
 use OCP\Remote\IInstanceFactory;
 use OCP\RichObjectStrings\IValidator;
@@ -549,13 +550,18 @@ class Server extends ServerContainer implements IServerContainer {
 			$logType = $c->query('AllConfig')->getSystemValue('log_type', 'file');
 			$factory = new LogFactory($c);
 			$logger = $factory->get($logType);
-			$config = $this->getSystemConfig();
+			$config = $this->getConfig();
 			$registry = $c->query(\OCP\Support\CrashReport\IRegistry::class);
 
 			return new Log($logger, $config, null, $registry);
 		});
 		$this->registerAlias('Logger', \OCP\ILogger::class);
 
+		$this->registerService(ILogFactory::class, function (Server $c) {
+			return new LogFactory($c);
+		});
+		$this->registerAlias('LogFactory', ILogFactory::class);
+
 		$this->registerService(\OCP\BackgroundJob\IJobList::class, function (Server $c) {
 			$config = $c->getConfig();
 			return new \OC\BackgroundJob\JobList(
@@ -1529,6 +1535,14 @@ class Server extends ServerContainer implements IServerContainer {
 		return $this->query('Logger');
 	}
 
+	/**
+	 * @return ILogFactory
+	 * @throws \OCP\AppFramework\QueryException
+	 */
+	public function getLogFactory() {
+		return $this->query('LogFactory');
+	}
+
 	/**
 	 * Returns a router for generating and matching urls
 	 *
diff --git a/lib/public/IServerContainer.php b/lib/public/IServerContainer.php
index 19c9578ee23..c38aaf9f2cb 100644
--- a/lib/public/IServerContainer.php
+++ b/lib/public/IServerContainer.php
@@ -44,6 +44,7 @@
 // use OCP namespace for all classes that are considered public.
 // This means that they should be used by apps instead of the internal ownCloud classes
 namespace OCP;
+use OCP\Log\ILogFactory;
 use OCP\Security\IContentSecurityPolicyManager;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
@@ -314,6 +315,14 @@ interface IServerContainer extends IContainer {
 	 */
 	public function getLogger();
 
+	/**
+	 * returns a log factory instance
+	 *
+	 * @return ILogFactory
+	 * @since 14.0.0
+	 */
+	public function getLogFactory();
+
 	/**
 	 * Returns a router for generating and matching urls
 	 *
diff --git a/lib/public/Log/ILogFactory.php b/lib/public/Log/ILogFactory.php
new file mode 100644
index 00000000000..d8e1ab4ee76
--- /dev/null
+++ b/lib/public/Log/ILogFactory.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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 OCP\Log;
+
+use OCP\ILogger;
+
+/**
+ * Interface ILogFactory
+ *
+ * @package OCP\Log
+ * @since 14.0.0
+ */
+interface ILogFactory {
+	/**
+	 * @param string $type - one of: file, errorlog, syslog
+	 * @return IWriter
+	 * @since 14.0.0
+	 */
+	public function get(string $type): IWriter;
+
+	/**
+	 * @param string $path
+	 * @return ILogger
+	 * @since 14.0.0
+	 */
+	public function getCustomLogger(string $path): ILogger;
+}
diff --git a/lib/private/Log/IWritable.php b/lib/public/Log/IWriter.php
similarity index 81%
rename from lib/private/Log/IWritable.php
rename to lib/public/Log/IWriter.php
index ff9bb4e71a8..c9b906bf4a3 100644
--- a/lib/private/Log/IWritable.php
+++ b/lib/public/Log/IWriter.php
@@ -21,8 +21,17 @@
  *
  */
 
-namespace OC\Log;
+namespace OCP\Log;
 
-interface IWritable {
-	public function write($app, $message, $level);
+/**
+ * Interface IWriter
+ *
+ * @package OCP\Log
+ * @since 14.0.0
+ */
+interface IWriter {
+	/**
+	 * @since 14.0.0
+	 */
+	public function write(string $app, $message, int $level);
 }
diff --git a/tests/lib/Log/FileTest.php b/tests/lib/Log/FileTest.php
index 33f0ee8dd19..0c50c6c08ba 100644
--- a/tests/lib/Log/FileTest.php
+++ b/tests/lib/Log/FileTest.php
@@ -41,7 +41,7 @@ class FileTest extends TestCase
 		$this->restore_logdateformat = $config->getSystemValue('logdateformat');
 		
 		$config->setSystemValue("logfile", $config->getSystemValue('datadirectory') . "/logtest");
-		$this->logFile = new File($config->getSystemValue('datadirectory') . '/logtest');
+		$this->logFile = new File($config->getSystemValue('datadirectory') . '/logtest', '', $config);
 	}
 	protected function tearDown() {
 		$config = \OC::$server->getConfig();
@@ -55,7 +55,7 @@ class FileTest extends TestCase
 		} else {
 			$config->deleteSystemValue("logdateformat");
 		}
-		$this->logFile = new File($this->restore_logfile);
+		$this->logFile = new File($this->restore_logfile, '', $config);
 		parent::tearDown();
 	}
 	
diff --git a/tests/lib/LoggerTest.php b/tests/lib/LoggerTest.php
index 3d0847d6c8a..9f226a395b6 100644
--- a/tests/lib/LoggerTest.php
+++ b/tests/lib/LoggerTest.php
@@ -10,8 +10,9 @@ namespace Test;
 
 use OC\Log;
 use OCP\ILogger;
+use OCP\Log\IWriter;
 
-class LoggerTest extends TestCase implements Log\IWritable {
+class LoggerTest extends TestCase implements IWriter {
 
 	/** @var \OC\SystemConfig|\PHPUnit_Framework_MockObject_MockObject */
 	private $config;
@@ -29,7 +30,7 @@ class LoggerTest extends TestCase implements Log\IWritable {
 		parent::setUp();
 
 		$this->logs = [];
-		$this->config = $this->createMock(\OC\SystemConfig::class);
+		$this->config = $this->createMock(\OCP\IConfig::class);
 		$this->registry = $this->createMock(\OCP\Support\CrashReport\IRegistry::class);
 		$this->logger = new Log($this, $this->config, null, $this->registry);
 	}
@@ -44,7 +45,7 @@ class LoggerTest extends TestCase implements Log\IWritable {
 
 	public function testAppCondition() {
 		$this->config->expects($this->any())
-			->method('getValue')
+			->method('getSystemValue')
 			->will(($this->returnValueMap([
 				['loglevel', ILogger::WARN, ILogger::WARN],
 				['log.condition', [], ['apps' => ['files']]]
@@ -66,7 +67,7 @@ class LoggerTest extends TestCase implements Log\IWritable {
 		return $this->logs;
 	}
 
-	public function write($app, $message, $level) {
+	public function write(string $app, $message, int $level) {
 		$this->logs[]= "$level $message";
 	}
 
-- 
GitLab