Skip to content

Commit 2ff0c97

Browse files
authored
Merge pull request #31124 from nextcloud/enh/diagnostics-logging
Diagnostics event logging to log
2 parents 18bafef + 83717a7 commit 2ff0c97

File tree

16 files changed

+173
-31
lines changed

16 files changed

+173
-31
lines changed

apps/dav/lib/Server.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
namespace OCA\DAV;
3636

3737
use OCA\DAV\Connector\Sabre\RequestIdHeaderPlugin;
38+
use OCP\Diagnostics\IEventLogger;
3839
use Psr\Log\LoggerInterface;
3940
use OCA\DAV\AppInfo\PluginManager;
4041
use OCA\DAV\CalDAV\BirthdayService;
@@ -337,7 +338,11 @@ function () {
337338
}
338339

339340
public function exec() {
341+
/** @var IEventLogger $eventLogger */
342+
$eventLogger = \OC::$server->get(IEventLogger::class);
343+
$eventLogger->start('dav_server_exec', '');
340344
$this->server->exec();
345+
$eventLogger->end('dav_server_exec');
341346
}
342347

343348
private function requestIsForSubtree(array $subTrees): bool {

config/config.sample.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2079,4 +2079,18 @@
20792079
* Also, it might log sensitive data into a plain text file.
20802080
*/
20812081
'ldap_log_file' => '',
2082+
2083+
/**
2084+
* Enable diagnostics event logging
2085+
*
2086+
* If enabled the timings of common execution steps will be logged to the
2087+
* Nextcloud log at debug level. log.condition is useful to enable this on
2088+
* production systems to only log under some conditions
2089+
*/
2090+
'diagnostics.logging' => true,
2091+
2092+
/**
2093+
* Limit diagnostics event logging to events longer than the configured threshold in ms
2094+
*/
2095+
'diagnostics.logging.threshold' => 0,
20822096
];

lib/base.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
* along with this program. If not, see <http://www.gnu.org/licenses/>
6262
*
6363
*/
64+
6465
use OCP\EventDispatcher\IEventDispatcher;
6566
use OCP\Group\Events\UserRemovedEvent;
6667
use OCP\ILogger;
@@ -595,8 +596,13 @@ public static function init() {
595596
// setup the basic server
596597
self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
597598
self::$server->boot();
599+
598600
$eventLogger = \OC::$server->getEventLogger();
599601
$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
602+
$eventLogger->start('request', 'Full request after autoloading');
603+
register_shutdown_function(function () use ($eventLogger) {
604+
$eventLogger->end('request');
605+
});
600606
$eventLogger->start('boot', 'Initialize');
601607

602608
// Override php.ini and log everything if we're troubleshooting
@@ -794,6 +800,7 @@ public static function init() {
794800
}
795801
}
796802
$eventLogger->end('boot');
803+
$eventLogger->log('init', 'OC::init', $loaderStart, microtime(true));
797804
}
798805

799806
/**

lib/private/AppFramework/Bootstrap/Coordinator.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
namespace OC\AppFramework\Bootstrap;
3232

33+
use OCP\Diagnostics\IEventLogger;
3334
use function class_exists;
3435
use function class_implements;
3536
use function in_array;
@@ -58,6 +59,9 @@ class Coordinator {
5859
/** @var IEventDispatcher */
5960
private $eventDispatcher;
6061

62+
/** @var IEventLogger */
63+
private $eventLogger;
64+
6165
/** @var LoggerInterface */
6266
private $logger;
6367

@@ -72,12 +76,14 @@ public function __construct(
7276
Registry $registry,
7377
IManager $dashboardManager,
7478
IEventDispatcher $eventListener,
79+
IEventLogger $eventLogger,
7580
LoggerInterface $logger
7681
) {
7782
$this->serverContainer = $container;
7883
$this->registry = $registry;
7984
$this->dashboardManager = $dashboardManager;
8085
$this->eventDispatcher = $eventListener;
86+
$this->eventLogger = $eventLogger;
8187
$this->logger = $logger;
8288
}
8389

@@ -126,7 +132,9 @@ private function registerApps(array $appIds): void {
126132
continue;
127133
}
128134

135+
$this->eventLogger->start('bootstrap:register_app_' . $appId, '');
129136
$application->register($this->registrationContext->for($appId));
137+
$this->eventLogger->end('bootstrap:register_app_' . $appId);
130138
}
131139
} catch (Throwable $e) {
132140
$this->logger->emergency('Error during app service registration: ' . $e->getMessage(), [
@@ -172,6 +180,7 @@ public function bootApp(string $appId): void {
172180
* the instance was already created for register, but any other
173181
* (legacy) code will now do their magic via the constructor.
174182
*/
183+
$this->eventLogger->start('bootstrap:boot_app_' . $appId, '');
175184
try {
176185
/** @var App $application */
177186
$application = $this->serverContainer->query($applicationClassName);
@@ -189,6 +198,7 @@ public function bootApp(string $appId): void {
189198
'exception' => $e,
190199
]);
191200
}
201+
$this->eventLogger->end('bootstrap:boot_app_' . $appId);
192202
}
193203

194204
public function isBootable(string $appId) {

lib/private/AppFramework/DependencyInjection/DIContainer.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
use OC\AppFramework\ScopedPsrLogger;
4747
use OC\AppFramework\Utility\SimpleContainer;
4848
use OC\Core\Middleware\TwoFactorMiddleware;
49+
use OC\Diagnostics\EventLogger;
4950
use OC\Log\PsrLoggerAdapter;
5051
use OC\ServerContainer;
5152
use OC\Settings\AuthorizedGroupMapper;
@@ -184,7 +185,8 @@ public function __construct($appName, $urlParams = [], ServerContainer $server =
184185
$c->get(IRequest::class),
185186
$c->get(IConfig::class),
186187
$c->get(IDBConnection::class),
187-
$c->get(LoggerInterface::class)
188+
$c->get(LoggerInterface::class),
189+
$c->get(EventLogger::class)
188190
);
189191
});
190192

lib/private/AppFramework/Http/Dispatcher.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
use OCP\AppFramework\Controller;
4040
use OCP\AppFramework\Http\DataResponse;
4141
use OCP\AppFramework\Http\Response;
42+
use OCP\Diagnostics\IEventLogger;
4243
use OCP\IConfig;
4344
use OCP\IRequest;
4445
use Psr\Log\LoggerInterface;
@@ -69,6 +70,9 @@ class Dispatcher {
6970
/** @var LoggerInterface */
7071
private $logger;
7172

73+
/** @var IEventLogger */
74+
private $eventLogger;
75+
7276
/**
7377
* @param Http $protocol the http protocol with contains all status headers
7478
* @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which
@@ -79,21 +83,24 @@ class Dispatcher {
7983
* @param IConfig $config
8084
* @param ConnectionAdapter $connection
8185
* @param LoggerInterface $logger
86+
* @param IEventLogger $eventLogger
8287
*/
8388
public function __construct(Http $protocol,
8489
MiddlewareDispatcher $middlewareDispatcher,
8590
ControllerMethodReflector $reflector,
8691
IRequest $request,
8792
IConfig $config,
8893
ConnectionAdapter $connection,
89-
LoggerInterface $logger) {
94+
LoggerInterface $logger,
95+
IEventLogger $eventLogger) {
9096
$this->protocol = $protocol;
9197
$this->middlewareDispatcher = $middlewareDispatcher;
9298
$this->reflector = $reflector;
9399
$this->request = $request;
94100
$this->config = $config;
95101
$this->connection = $connection;
96102
$this->logger = $logger;
103+
$this->eventLogger = $eventLogger;
97104
}
98105

99106

@@ -214,7 +221,9 @@ private function executeController(Controller $controller, string $methodName):
214221
$arguments[] = $value;
215222
}
216223

224+
$this->eventLogger->start('controller:' . get_class($controller) . '::' . $methodName, 'App framework controller execution');
217225
$response = \call_user_func_array([$controller, $methodName], $arguments);
226+
$this->eventLogger->end('controller:' . get_class($controller) . '::' . $methodName);
218227

219228
// format response
220229
if ($response instanceof DataResponse || !($response instanceof Response)) {

lib/private/DB/Connection.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,17 @@ class Connection extends \Doctrine\DBAL\Connection {
8181
*/
8282
public function connect() {
8383
try {
84-
return parent::connect();
84+
if ($this->_conn) {
85+
return parent::connect();
86+
}
87+
88+
// Only trigger the event logger for the initial connect call
89+
$eventLogger = \OC::$server->getEventLogger();
90+
$eventLogger->start('connect:db', 'db connection opened');
91+
$status = parent::connect();
92+
$eventLogger->end('connect:db');
93+
94+
return $status;
8595
} catch (Exception $e) {
8696
// throw a new exception to prevent leaking info from the stacktrace
8797
throw new Exception('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());

lib/private/Diagnostics/Event.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,8 @@ public function getDuration() {
100100
}
101101
return $this->end - $this->start;
102102
}
103+
104+
public function __toString() {
105+
return $this->getId() . ' ' . $this->getDescription() . ' ' . $this->getDuration();
106+
}
103107
}

lib/private/Diagnostics/EventLogger.php

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,58 @@
2424
*/
2525
namespace OC\Diagnostics;
2626

27+
use OC\Log;
28+
use OC\SystemConfig;
29+
use OCP\Diagnostics\IEvent;
2730
use OCP\Diagnostics\IEventLogger;
31+
use Psr\Log\LoggerInterface;
2832

2933
class EventLogger implements IEventLogger {
30-
/**
31-
* @var \OC\Diagnostics\Event[]
32-
*/
34+
35+
/** @var Event[] */
3336
private $events = [];
34-
37+
38+
/** @var SystemConfig */
39+
private $config;
40+
41+
/** @var LoggerInterface */
42+
private $logger;
43+
44+
/** @var Log */
45+
private $internalLogger;
46+
3547
/**
3648
* @var bool - Module needs to be activated by some app
3749
*/
3850
private $activated = false;
3951

52+
public function __construct(SystemConfig $config, LoggerInterface $logger, Log $internalLogger) {
53+
$this->config = $config;
54+
$this->logger = $logger;
55+
$this->internalLogger = $internalLogger;
56+
57+
if ($this->isLoggingActivated()) {
58+
$this->activate();
59+
}
60+
}
61+
62+
public function isLoggingActivated(): bool {
63+
if ($this->config->getValue('debug', false)) {
64+
return true;
65+
}
66+
67+
$isDebugLevel = $this->internalLogger->getLogLevel([]) === Log::DEBUG;
68+
$systemValue = (bool)$this->config->getValue('diagnostics.logging', false);
69+
return $systemValue && $isDebugLevel;
70+
}
71+
4072
/**
4173
* @inheritdoc
4274
*/
43-
public function start($id, $description) {
75+
public function start($id, $description = '') {
4476
if ($this->activated) {
4577
$this->events[$id] = new Event($id, $description, microtime(true));
78+
$this->writeLog($this->events[$id]);
4679
}
4780
}
4881

@@ -53,6 +86,7 @@ public function end($id) {
5386
if ($this->activated && isset($this->events[$id])) {
5487
$timing = $this->events[$id];
5588
$timing->end(microtime(true));
89+
$this->writeLog($timing);
5690
}
5791
}
5892

@@ -63,6 +97,7 @@ public function log($id, $description, $start, $end) {
6397
if ($this->activated) {
6498
$this->events[$id] = new Event($id, $description, $start);
6599
$this->events[$id]->end($end);
100+
$this->writeLog($this->events[$id]);
66101
}
67102
}
68103

@@ -72,11 +107,29 @@ public function log($id, $description, $start, $end) {
72107
public function getEvents() {
73108
return $this->events;
74109
}
75-
110+
76111
/**
77112
* @inheritdoc
78113
*/
79114
public function activate() {
80115
$this->activated = true;
81116
}
117+
118+
private function writeLog(IEvent $event) {
119+
if ($this->activated) {
120+
if ($event->getEnd() === null) {
121+
return;
122+
}
123+
$duration = $event->getDuration();
124+
$timeInMs = round($duration * 1000, 4);
125+
126+
$loggingMinimum = (int)$this->config->getValue('diagnostics.logging.threshold', 0);
127+
if ($loggingMinimum > 0 && $timeInMs < $loggingMinimum) {
128+
return;
129+
}
130+
131+
$message = microtime() . ' - ' . $event->getId() . ': ' . $timeInMs . ' (' . $event->getDescription() . ')';
132+
$this->logger->debug($message, ['app' => 'diagnostics']);
133+
}
134+
}
82135
}

lib/private/Log.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public function log(int $level, string $message, array $context = []) {
232232
}
233233
}
234234

235-
private function getLogLevel($context) {
235+
public function getLogLevel($context) {
236236
$logCondition = $this->config->getValue('log.condition', []);
237237

238238
/**

0 commit comments

Comments
 (0)