Skip to content

Commit

Permalink
Merge pull request #21457 from nextcloud/enhancement/crash-reporters-…
Browse files Browse the repository at this point in the history
…registration-boostrap

Allow crash reporters registration during app bootstrap
  • Loading branch information
rullzer authored Jun 19, 2020
2 parents dd0b965 + 2b7b714 commit 52dd1e3
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 6 deletions.
7 changes: 7 additions & 0 deletions lib/private/AppFramework/Bootstrap/Coordinator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

namespace OC\AppFramework\Bootstrap;

use OC\Support\CrashReport\Registry;
use OC_App;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootstrap;
Expand All @@ -42,16 +43,21 @@ class Coordinator {
/** @var IServerContainer */
private $serverContainer;

/** @var Registry */
private $registry;

/** @var IEventDispatcher */
private $eventDispatcher;

/** @var ILogger */
private $logger;

public function __construct(IServerContainer $container,
Registry $registry,
IEventDispatcher $eventListener,
ILogger $logger) {
$this->serverContainer = $container;
$this->registry = $registry;
$this->eventDispatcher = $eventListener;
$this->logger = $logger;
}
Expand Down Expand Up @@ -102,6 +108,7 @@ public function runRegistration(): void {
* to the actual services
*/
$context->delegateCapabilityRegistrations($apps);
$context->delegateCrashReporterRegistrations($apps, $this->registry);
$context->delegateEventListenerRegistrations($this->eventDispatcher);
$context->delegateContainerRegistrations($apps);
$context->delegateMiddlewareRegistrations($apps);
Expand Down
35 changes: 35 additions & 0 deletions lib/private/AppFramework/Bootstrap/RegistrationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
namespace OC\AppFramework\Bootstrap;

use Closure;
use OC\Support\CrashReport\Registry;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\EventDispatcher\IEventDispatcher;
Expand All @@ -37,6 +38,9 @@ class RegistrationContext {
/** @var array[] */
private $capabilities = [];

/** @var array[] */
private $crashReporters = [];

/** @var array[] */
private $services = [];

Expand Down Expand Up @@ -79,6 +83,13 @@ public function registerCapability(string $capability): void {
);
}

public function registerCrashReporter(string $reporterClass): void {
$this->context->registerCrashReporter(
$this->appId,
$reporterClass
);
}

public function registerService(string $name, callable $factory, bool $shared = true): void {
$this->context->registerService(
$this->appId,
Expand Down Expand Up @@ -129,6 +140,13 @@ public function registerCapability(string $appId, string $capability): void {
];
}

public function registerCrashReporter(string $appId, string $reporterClass): void {
$this->crashReporters[] = [
'appId' => $appId,
'class' => $reporterClass,
];
}

public function registerService(string $appId, string $name, callable $factory, bool $shared = true): void {
$this->services[] = [
"appId" => $appId,
Expand Down Expand Up @@ -189,6 +207,23 @@ public function delegateCapabilityRegistrations(array $apps): void {
}
}

/**
* @param App[] $apps
*/
public function delegateCrashReporterRegistrations(array $apps, Registry $registry): void {
foreach ($this->crashReporters as $registration) {
try {
$registry->registerLazy($registration['class']);
} catch (Throwable $e) {
$appId = $registration['appId'];
$this->logger->logException($e, [
'message' => "Error during crash reporter registration of $appId: " . $e->getMessage(),
'level' => ILogger::ERROR,
]);
}
}
}

public function delegateEventListenerRegistrations(IEventDispatcher $eventDispatcher): void {
foreach ($this->eventListeners as $registration) {
try {
Expand Down
66 changes: 65 additions & 1 deletion lib/private/Support/CrashReport/Registry.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php

declare(strict_types=1);

/**
*
*
Expand All @@ -25,6 +28,9 @@
namespace OC\Support\CrashReport;

use Exception;
use OCP\AppFramework\QueryException;
use OCP\ILogger;
use OCP\IServerContainer;
use OCP\Support\CrashReport\ICollectBreadcrumbs;
use OCP\Support\CrashReport\IMessageReporter;
use OCP\Support\CrashReport\IRegistry;
Expand All @@ -33,9 +39,22 @@

class Registry implements IRegistry {

/** @var string[] */
private $lazyReporters = [];

/** @var IReporter[] */
private $reporters = [];

/** @var IServerContainer */
private $serverContainer;

/** @var ILogger */
private $logger;

public function __construct(IServerContainer $serverContainer) {
$this->serverContainer = $serverContainer;
}

/**
* Register a reporter instance
*
Expand All @@ -45,6 +64,10 @@ public function register(IReporter $reporter): void {
$this->reporters[] = $reporter;
}

public function registerLazy(string $class): void {
$this->lazyReporters[] = $class;
}

/**
* Delegate breadcrumb collection to all registered reporters
*
Expand All @@ -55,6 +78,8 @@ public function register(IReporter $reporter): void {
* @since 15.0.0
*/
public function delegateBreadcrumb(string $message, string $category, array $context = []): void {
$this->loadLazyProviders();

foreach ($this->reporters as $reporter) {
if ($reporter instanceof ICollectBreadcrumbs) {
$reporter->collect($message, $category, $context);
Expand All @@ -69,7 +94,8 @@ public function delegateBreadcrumb(string $message, string $category, array $con
* @param array $context
*/
public function delegateReport($exception, array $context = []): void {
/** @var IReporter $reporter */
$this->loadLazyProviders();

foreach ($this->reporters as $reporter) {
$reporter->report($exception, $context);
}
Expand All @@ -84,10 +110,48 @@ public function delegateReport($exception, array $context = []): void {
* @return void
*/
public function delegateMessage(string $message, array $context = []): void {
$this->loadLazyProviders();

foreach ($this->reporters as $reporter) {
if ($reporter instanceof IMessageReporter) {
$reporter->reportMessage($message, $context);
}
}
}

private function loadLazyProviders(): void {
$classes = $this->lazyReporters;
foreach ($classes as $class) {
try {
/** @var IReporter $reporter */
$reporter = $this->serverContainer->query($class);
} catch (QueryException $e) {
/*
* There is a circular dependency between the logger and the registry, so
* we can not inject it. Thus the static call.
*/
\OC::$server->getLogger()->logException($e, [
'message' => 'Could not load lazy crash reporter: ' . $e->getMessage(),
'level' => ILogger::FATAL,
]);
}
/**
* Try to register the loaded reporter. Theoretically it could be of a wrong
* type, so we might get a TypeError here that we should catch.
*/
try {
$this->register($reporter);
} catch (Throwable $e) {
/*
* There is a circular dependency between the logger and the registry, so
* we can not inject it. Thus the static call.
*/
\OC::$server->getLogger()->logException($e, [
'message' => 'Could not register lazy crash reporter: ' . $e->getMessage(),
'level' => ILogger::FATAL,
]);
}
}
$this->lazyReporters = [];
}
}
10 changes: 10 additions & 0 deletions lib/public/AppFramework/Bootstrap/IRegistrationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ interface IRegistrationContext {
*/
public function registerCapability(string $capability): void;

/**
* Register an implementation of \OCP\Support\CrashReport\IReporter that
* will receive unhandled exceptions and throwables
*
* @param string $reporterClass
* @return void
* @since 20.0.0
*/
public function registerCrashReporter(string $reporterClass): void;

/**
* Register a service
*
Expand Down
7 changes: 7 additions & 0 deletions lib/public/Support/CrashReport/IRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@
namespace OCP\Support\CrashReport;

use Exception;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use Throwable;

/**
* @since 13.0.0
* @deprecated used internally only
*/
interface IRegistry {

Expand All @@ -40,6 +42,8 @@ interface IRegistry {
* @param IReporter $reporter
*
* @since 13.0.0
* @deprecated 20.0.0 use IRegistrationContext::registerCrashReporter
* @see IRegistrationContext::registerCrashReporter()
*/
public function register(IReporter $reporter): void;

Expand All @@ -50,6 +54,7 @@ public function register(IReporter $reporter): void;
* @param string $category
* @param array $context
*
* @deprecated used internally only
* @since 15.0.0
*/
public function delegateBreadcrumb(string $message, string $category, array $context = []): void;
Expand All @@ -60,6 +65,7 @@ public function delegateBreadcrumb(string $message, string $category, array $con
* @param Exception|Throwable $exception
* @param array $context
*
* @deprecated used internally only
* @since 13.0.0
*/
public function delegateReport($exception, array $context = []);
Expand All @@ -72,6 +78,7 @@ public function delegateReport($exception, array $context = []);
*
* @return void
*
* @deprecated used internally only
* @since 17.0.0
*/
public function delegateMessage(string $message, array $context = []): void;
Expand Down
3 changes: 3 additions & 0 deletions lib/public/Support/CrashReport/IReporter.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php

declare(strict_types=1);

/**
*
*
Expand Down
6 changes: 6 additions & 0 deletions tests/lib/AppFramework/Bootstrap/CoordinatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
namespace lib\AppFramework\Bootstrap;

use OC\AppFramework\Bootstrap\Coordinator;
use OC\Support\CrashReport\Registry;
use OCP\App\IAppManager;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
Expand All @@ -46,6 +47,9 @@ class CoordinatorTest extends TestCase {
/** @var IServerContainer|MockObject */
private $serverContainer;

/** @var Registry|MockObject */
private $crashReporterRegistry;

/** @var IEventDispatcher|MockObject */
private $eventDispatcher;

Expand All @@ -60,11 +64,13 @@ protected function setUp(): void {

$this->appManager = $this->createMock(IAppManager::class);
$this->serverContainer = $this->createMock(IServerContainer::class);
$this->crashReporterRegistry = $this->createMock(Registry::class);
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->logger = $this->createMock(ILogger::class);

$this->coordinator = new Coordinator(
$this->serverContainer,
$this->crashReporterRegistry,
$this->eventDispatcher,
$this->logger
);
Expand Down
Loading

0 comments on commit 52dd1e3

Please sign in to comment.