Skip to content

Commit 5c1d96d

Browse files
committed
Add exception rate limiter
1 parent 435e36f commit 5c1d96d

File tree

4 files changed

+50
-2
lines changed

4 files changed

+50
-2
lines changed

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@
6565
"illuminate/http": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0",
6666
"illuminate/log": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0",
6767
"illuminate/queue": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0",
68-
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
68+
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0",
69+
"symfony/cache": "^5.0 || 6.0",
70+
"symfony/rate-limiter": "^5.0 || ^6.0"
6971
},
7072
"require-dev": {
7173
"brainmaestro/composer-git-hooks": "^2.7",

config/exception-notify.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@
5656
*/
5757
'dont_report' => [],
5858

59+
/*
60+
|--------------------------------------------------------------------------
61+
| Exception notification rate limiter.
62+
|--------------------------------------------------------------------------
63+
|
64+
| The exception notification rate limiter is used to prevent sending
65+
| exception notification to the same channel too frequently.
66+
|
67+
*/
68+
'rate_limiter' => [
69+
// Limit the number of exceptions.
70+
'limit' => (int) env('EXCEPTION_NOTIFY_LIMIT', 1),
71+
72+
// Time interval.
73+
'interval' => env('EXCEPTION_NOTIFY_INTERVAL', '1 minutes'),
74+
75+
// Cache adapter.
76+
'cache_adapter' => \Symfony\Component\Cache\Adapter\FilesystemAdapter::class,
77+
],
78+
5979
/*
6080
|--------------------------------------------------------------------------
6181
| Report title.

src/ExceptionNotifyManager.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use Illuminate\Support\Manager;
3535
use Illuminate\Support\Str;
3636
use Illuminate\Support\Traits\Macroable;
37+
use Symfony\Component\RateLimiter\RateLimiterFactory;
3738
use Throwable;
3839

3940
class ExceptionNotifyManager extends Manager
@@ -112,7 +113,13 @@ public function shouldntReport(Throwable $e): bool
112113
}
113114
}
114115

115-
return false;
116+
/* @var RateLimiterFactory $rateLimiterFactory */
117+
$rateLimiterFactory = $this->container->make(RateLimiterFactory::class);
118+
119+
return ! $rateLimiterFactory
120+
->create(md5($e->getFile().$e->getLine().$e->getCode().$e->getMessage()))
121+
->consume()
122+
->isAccepted();
116123
}
117124

118125
public function shouldReport(Throwable $e): bool

src/ExceptionNotifyServiceProvider.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use Illuminate\Support\ServiceProvider;
2323
use Illuminate\Support\Str;
2424
use Laravel\Lumen\Application as LumenApplication;
25+
use Symfony\Component\RateLimiter\RateLimiterFactory;
26+
use Symfony\Component\RateLimiter\Storage\CacheStorage;
2527

2628
class ExceptionNotifyServiceProvider extends ServiceProvider
2729
{
@@ -49,6 +51,7 @@ public function register()
4951
$this->setupConfig();
5052
$this->registerExceptionNotifyManager();
5153
$this->registerCollectorManager();
54+
$this->registerRateLimiterFactory();
5255
}
5356

5457
/**
@@ -96,6 +99,22 @@ protected function registerExceptionNotifyManager()
9699
$this->app->alias(ExceptionNotifyManager::class, 'exception.notifier');
97100
}
98101

102+
protected function registerRateLimiterFactory()
103+
{
104+
$this->app->singleton(RateLimiterFactory::class, function (Container $app) {
105+
return new RateLimiterFactory([
106+
'id' => 'exception_notify',
107+
'policy' => 'token_bucket',
108+
'limit' => $app['config']['exception-notify.rate_limiter.limit'],
109+
'rate' => [
110+
'interval' => $app['config']['exception-notify.rate_limiter.interval'],
111+
],
112+
], new CacheStorage($app->make($app['config']['exception-notify.rate_limiter.cache_adapter'])));
113+
});
114+
115+
$this->app->alias(RateLimiterFactory::class, 'exception.rate-limiter-factory');
116+
}
117+
99118
protected function registerReportingEvent()
100119
{
101120
foreach ($this->app['config']['exception-notify.reporting'] as $listener) {

0 commit comments

Comments
 (0)