ExceptionLoggerPlugin.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\DAV\Connector\Sabre;
  8. use OCA\DAV\Connector\Sabre\Exception\FileLocked;
  9. use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden;
  10. use OCA\DAV\Exception\ServerMaintenanceMode;
  11. use OCP\Files\StorageNotAvailableException;
  12. use Psr\Log\LoggerInterface;
  13. use Sabre\DAV\Exception\BadRequest;
  14. use Sabre\DAV\Exception\Conflict;
  15. use Sabre\DAV\Exception\Forbidden;
  16. use Sabre\DAV\Exception\InvalidSyncToken;
  17. use Sabre\DAV\Exception\MethodNotAllowed;
  18. use Sabre\DAV\Exception\NotAuthenticated;
  19. use Sabre\DAV\Exception\NotFound;
  20. use Sabre\DAV\Exception\NotImplemented;
  21. use Sabre\DAV\Exception\PreconditionFailed;
  22. use Sabre\DAV\Exception\RequestedRangeNotSatisfiable;
  23. class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin {
  24. protected $nonFatalExceptions = [
  25. NotAuthenticated::class => true,
  26. // If tokenauth can throw this exception (which is basically as
  27. // NotAuthenticated. So not fatal.
  28. PasswordLoginForbidden::class => true,
  29. // basically a NotAuthenticated
  30. InvalidSyncToken::class => true,
  31. // the sync client uses this to find out whether files exist,
  32. // so it is not always an error, log it as debug
  33. NotFound::class => true,
  34. // the sync client messed up their request
  35. // (e.g. propfind for tags with string instead of int)
  36. // so it is not always an error, log it as debug
  37. BadRequest::class => true,
  38. // this one mostly happens when the same file is uploaded at
  39. // exactly the same time from two clients, only one client
  40. // wins, the second one gets "Precondition failed"
  41. PreconditionFailed::class => true,
  42. // forbidden can be expected when trying to upload to
  43. // read-only folders for example
  44. Forbidden::class => true,
  45. // our forbidden is expected when access control is blocking
  46. // an item in a folder
  47. \OCA\DAV\Connector\Sabre\Exception\Forbidden::class => true,
  48. // Happens when an external storage or federated share is temporarily
  49. // not available
  50. StorageNotAvailableException::class => true,
  51. // happens if some a client uses the wrong method for a given URL
  52. // the error message itself is visible on the client side anyways
  53. NotImplemented::class => true,
  54. // happens when the parent directory is not present (for example when a
  55. // move is done to a non-existent directory)
  56. Conflict::class => true,
  57. // happens when a certain method is not allowed to be called
  58. // for example creating a folder that already exists
  59. MethodNotAllowed::class => true,
  60. // A locked file is perfectly valid and can happen in various cases
  61. FileLocked::class => true,
  62. // An invalid range is requested
  63. RequestedRangeNotSatisfiable::class => true,
  64. ServerMaintenanceMode::class => true,
  65. ];
  66. private string $appName;
  67. private LoggerInterface $logger;
  68. /**
  69. * @param string $loggerAppName app name to use when logging
  70. */
  71. public function __construct(string $loggerAppName, LoggerInterface $logger) {
  72. $this->appName = $loggerAppName;
  73. $this->logger = $logger;
  74. }
  75. /**
  76. * This initializes the plugin.
  77. *
  78. * This function is called by \Sabre\DAV\Server, after
  79. * addPlugin is called.
  80. *
  81. * This method should set up the required event subscriptions.
  82. *
  83. * @param \Sabre\DAV\Server $server
  84. * @return void
  85. */
  86. public function initialize(\Sabre\DAV\Server $server) {
  87. $server->on('exception', [$this, 'logException'], 10);
  88. }
  89. /**
  90. * Log exception
  91. */
  92. public function logException(\Throwable $ex) {
  93. $exceptionClass = get_class($ex);
  94. if (isset($this->nonFatalExceptions[$exceptionClass])) {
  95. $this->logger->debug($ex->getMessage(), [
  96. 'app' => $this->appName,
  97. 'exception' => $ex,
  98. ]);
  99. return;
  100. }
  101. $this->logger->critical($ex->getMessage(), [
  102. 'app' => $this->appName,
  103. 'exception' => $ex,
  104. ]);
  105. }
  106. }