Registry.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Support\CrashReport;
  8. use Exception;
  9. use OCP\AppFramework\QueryException;
  10. use OCP\IServerContainer;
  11. use OCP\Support\CrashReport\ICollectBreadcrumbs;
  12. use OCP\Support\CrashReport\IMessageReporter;
  13. use OCP\Support\CrashReport\IRegistry;
  14. use OCP\Support\CrashReport\IReporter;
  15. use Psr\Log\LoggerInterface;
  16. use Throwable;
  17. use function array_shift;
  18. class Registry implements IRegistry {
  19. /** @var string[] */
  20. private $lazyReporters = [];
  21. /** @var IReporter[] */
  22. private $reporters = [];
  23. /** @var IServerContainer */
  24. private $serverContainer;
  25. public function __construct(IServerContainer $serverContainer) {
  26. $this->serverContainer = $serverContainer;
  27. }
  28. /**
  29. * Register a reporter instance
  30. *
  31. * @param IReporter $reporter
  32. */
  33. public function register(IReporter $reporter): void {
  34. $this->reporters[] = $reporter;
  35. }
  36. public function registerLazy(string $class): void {
  37. $this->lazyReporters[] = $class;
  38. }
  39. /**
  40. * Delegate breadcrumb collection to all registered reporters
  41. *
  42. * @param string $message
  43. * @param string $category
  44. * @param array $context
  45. *
  46. * @since 15.0.0
  47. */
  48. public function delegateBreadcrumb(string $message, string $category, array $context = []): void {
  49. $this->loadLazyProviders();
  50. foreach ($this->reporters as $reporter) {
  51. if ($reporter instanceof ICollectBreadcrumbs) {
  52. $reporter->collect($message, $category, $context);
  53. }
  54. }
  55. }
  56. /**
  57. * Delegate crash reporting to all registered reporters
  58. *
  59. * @param Exception|Throwable $exception
  60. * @param array $context
  61. */
  62. public function delegateReport($exception, array $context = []): void {
  63. $this->loadLazyProviders();
  64. foreach ($this->reporters as $reporter) {
  65. $reporter->report($exception, $context);
  66. }
  67. }
  68. /**
  69. * Delegate a message to all reporters that implement IMessageReporter
  70. *
  71. * @param string $message
  72. * @param array $context
  73. *
  74. * @return void
  75. */
  76. public function delegateMessage(string $message, array $context = []): void {
  77. $this->loadLazyProviders();
  78. foreach ($this->reporters as $reporter) {
  79. if ($reporter instanceof IMessageReporter) {
  80. $reporter->reportMessage($message, $context);
  81. }
  82. }
  83. }
  84. private function loadLazyProviders(): void {
  85. while (($class = array_shift($this->lazyReporters)) !== null) {
  86. try {
  87. /** @var IReporter $reporter */
  88. $reporter = $this->serverContainer->query($class);
  89. } catch (QueryException $e) {
  90. /*
  91. * There is a circular dependency between the logger and the registry, so
  92. * we can not inject it. Thus the static call.
  93. */
  94. \OC::$server->get(LoggerInterface::class)->critical('Could not load lazy crash reporter: ' . $e->getMessage(), [
  95. 'exception' => $e,
  96. ]);
  97. }
  98. /**
  99. * Try to register the loaded reporter. Theoretically it could be of a wrong
  100. * type, so we might get a TypeError here that we should catch.
  101. */
  102. try {
  103. $this->register($reporter);
  104. } catch (Throwable $e) {
  105. /*
  106. * There is a circular dependency between the logger and the registry, so
  107. * we can not inject it. Thus the static call.
  108. */
  109. \OC::$server->get(LoggerInterface::class)->critical('Could not register lazy crash reporter: ' . $e->getMessage(), [
  110. 'exception' => $e,
  111. ]);
  112. }
  113. }
  114. }
  115. public function hasReporters(): bool {
  116. return !empty($this->lazyReporters) || !empty($this->reporters);
  117. }
  118. }