PHPackages                             philiprehberger/php-exception-reporter - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. philiprehberger/php-exception-reporter

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

philiprehberger/php-exception-reporter
======================================

Lightweight exception reporting to log channels and webhooks

v1.2.0(2mo ago)138MITPHPPHP ^8.2CI passing

Since Mar 13Pushed 1mo agoCompare

[ Source](https://github.com/philiprehberger/php-exception-reporter)[ Packagist](https://packagist.org/packages/philiprehberger/php-exception-reporter)[ Docs](https://github.com/philiprehberger/php-exception-reporter)[ GitHub Sponsors](https://github.com/philiprehberger)[ RSS](/packages/philiprehberger-php-exception-reporter/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (6)Versions (7)Used By (0)

PHP Exception Reporter
======================

[](#php-exception-reporter)

[![Tests](https://github.com/philiprehberger/php-exception-reporter/actions/workflows/tests.yml/badge.svg)](https://github.com/philiprehberger/php-exception-reporter/actions/workflows/tests.yml)[![Latest Version on Packagist](https://camo.githubusercontent.com/93a18191900d4ab41c322765041e35087c67679428fa8f3765cb7f4e578b582f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7068696c69707265686265726765722f7068702d657863657074696f6e2d7265706f727465722e737667)](https://packagist.org/packages/philiprehberger/php-exception-reporter)[![Last updated](https://camo.githubusercontent.com/d2878608ede0e9b4ba9d4c4329326d60be06632678a9cb2a67af729c5d221c73/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f7068696c69707265686265726765722f7068702d657863657074696f6e2d7265706f72746572)](https://github.com/philiprehberger/php-exception-reporter/commits/main)

Lightweight exception reporting to log channels and webhooks.

Requirements
------------

[](#requirements)

- PHP 8.2+

Installation
------------

[](#installation)

```
composer require philiprehberger/php-exception-reporter
```

Usage
-----

[](#usage)

### Basic reporting with a callback

[](#basic-reporting-with-a-callback)

```
use PhilipRehberger\ExceptionReporter\ExceptionReporter;
use PhilipRehberger\ExceptionReporter\Channels\CallbackChannel;

$reporter = new ExceptionReporter();

$reporter->addChannel(new CallbackChannel(function ($report) {
    error_log("[{$report->class}] {$report->message} in {$report->file}:{$report->line}");
}));

try {
    riskyOperation();
} catch (\Throwable $e) {
    $reporter->capture($e);
}
```

### File channel

[](#file-channel)

```
use PhilipRehberger\ExceptionReporter\Channels\FileChannel;

$reporter->addChannel(new FileChannel('/var/log/app-exceptions.log'));

// Each report is written as a JSON line
$reporter->capture(new \RuntimeException('Something failed'));
```

### Multiple channels

[](#multiple-channels)

```
$reporter
    ->addChannel(new CallbackChannel(function ($report) {
        // Send to your monitoring service
    }))
    ->addChannel(new FileChannel('/var/log/exceptions.log'));
```

### Deduplication

[](#deduplication)

Prevent the same exception (same class, file, and line) from being reported more than once:

```
$reporter->enableDeduplication();

$exception = new \RuntimeException('flaky');
$reporter->capture($exception); // Reported
$reporter->capture($exception); // Skipped (duplicate)

$reporter->resetFingerprints(); // Clear dedup state
$reporter->capture($exception); // Reported again
```

### Adding context

[](#adding-context)

```
$reporter->capture($exception, [
    'user_id' => 42,
    'request_url' => '/checkout',
]);
```

### Persistent context

[](#persistent-context)

Attach context fields that are included in every subsequent report. `withContext()` returns a new immutable instance:

```
$reporter = $reporter->withContext([
    'request_id' => 'abc-123',
    'user_id' => 42,
]);

// Both reports will include request_id and user_id
$reporter->capture(new \RuntimeException('first'));
$reporter->capture(new \LogicException('second'));

// Per-call context is merged with persistent context
$reporter->capture($exception, ['action' => 'checkout']);
```

### Filtering exceptions

[](#filtering-exceptions)

Skip certain exceptions from being reported using a filter callable:

```
$reporter->setFilter(function (\Throwable $e): bool {
    // Return false to skip reporting
    return !$e instanceof \DeprecationException;
});

$reporter->capture(new \DeprecationException('old API')); // Skipped
$reporter->capture(new \RuntimeException('real error'));   // Reported
```

### Webhook channel

[](#webhook-channel)

Send exception reports to an HTTP endpoint:

```
use PhilipRehberger\ExceptionReporter\Channels\WebhookChannel;

$reporter->addChannel(new WebhookChannel('https://example.com/webhook'));

// With custom headers
$reporter->addChannel(new WebhookChannel(
    'https://example.com/webhook',
    ['Authorization' => 'Bearer secret'],
));

// With a custom payload transformer
$reporter->addChannel(new WebhookChannel(
    'https://example.com/webhook',
    [],
    fn ($report) => ['text' => "[{$report->class}] {$report->message}"],
));
```

### Rate limiting

[](#rate-limiting)

Prevent flooding during cascading failures by limiting how many reports are dispatched:

```
$reporter->rateLimit(maxReports: 10, windowSeconds: 60);

// Only the first 10 reports per fingerprint (and 10 globally) within 60 seconds are sent
for ($i = 0; $i < 100; $i++) {
    $reporter->capture(new \RuntimeException('storm'));
}
```

### Summary reports

[](#summary-reports)

Access an aggregated summary of captured exceptions:

```
$reporter->capture(new \RuntimeException('db timeout'));
$reporter->capture(new \RuntimeException('db timeout'));
$reporter->capture(new \LogicException('bad state'));

$summary = $reporter->summary();

$summary->totalCount();    // 3
$summary->uniqueCount();   // 2
$summary->since();         // DateTimeImmutable of earliest report
$summary->until();         // DateTimeImmutable of latest report
$summary->topExceptions(); // [{message, count, lastSeen}] sorted by frequency

$reporter->clearHistory(); // Reset stored reports
```

### Tracking report count

[](#tracking-report-count)

```
$reporter->capture(new \RuntimeException('one'));
$reporter->capture(new \LogicException('two'));

echo $reporter->count(); // 2
```

### Custom channels

[](#custom-channels)

Implement the `ReportChannel` interface to build your own channel:

```
use PhilipRehberger\ExceptionReporter\Contracts\ReportChannel;
use PhilipRehberger\ExceptionReporter\ExceptionReport;

class SlackChannel implements ReportChannel
{
    public function report(ExceptionReport $report): void
    {
        // POST to Slack webhook with $report->toArray()
    }
}
```

API
---

[](#api)

### `ExceptionReporter`

[](#exceptionreporter)

MethodDescription`addChannel(ReportChannel $channel): self`Register a reporting channel`enableDeduplication(): self`Enable fingerprint-based deduplication`capture(Throwable $e, array $context = []): ExceptionReport`Capture and report an exception`resetFingerprints(): void`Clear deduplication state`withContext(array $context): self`Return a new instance with persistent context fields`setFilter(callable $filter): self`Set a filter; return `false` to skip reporting`count(): int`Number of exceptions reported by this instance`rateLimit(int $maxReports, int $windowSeconds = 60): self`Enable rate limiting to cap reports per window`summary(): ReportSummary`Return an aggregated summary of stored reports`clearHistory(): void`Clear the stored report history### `ExceptionReport`

[](#exceptionreport)

Property / MethodDescription`string $class`Exception class name`string $message`Exception message`string $file`File where the exception was thrown`int $line`Line number`string $trace`Stack trace as string`DateTimeImmutable $timestamp`When the exception was captured`array $context`Additional context data`?string $previousClass`Previous exception class, if any`?string $previousMessage`Previous exception message, if any`fingerprint(): string`MD5 hash of class + file + line`toArray(): array`Serialize to array`fromThrowable(Throwable, array): self`Create from a throwable### Channels

[](#channels)

ChannelDescription`CallbackChannel`Invokes a user-provided callable`FileChannel`Appends JSON-encoded reports to a file`WebhookChannel`Sends JSON-encoded reports to an HTTP endpoint via POST### `RateLimiter`

[](#ratelimiter)

MethodDescription`__construct(int $maxReports = 100, int $windowSeconds = 60)`Create a rate limiter with per-fingerprint and global limits`shouldReport(string $fingerprint): bool`Check if a report should be allowed within the current window### `ReportSummary`

[](#reportsummary)

MethodDescription`totalCount(): int`Total number of stored reports`uniqueCount(): int`Count of unique exception fingerprints`topExceptions(int $limit = 5): array`Top exceptions sorted by frequency with message, count, and lastSeen`since(): ?DateTimeImmutable`Earliest report timestamp`until(): ?DateTimeImmutable`Latest report timestampDevelopment
-----------

[](#development)

```
composer install
vendor/bin/phpunit
vendor/bin/pint --test
vendor/bin/phpstan analyse
```

Support
-------

[](#support)

If you find this project useful:

⭐ [Star the repo](https://github.com/philiprehberger/php-exception-reporter)

🐛 [Report issues](https://github.com/philiprehberger/php-exception-reporter/issues?q=is%3Aissue+is%3Aopen+label%3Abug)

💡 [Suggest features](https://github.com/philiprehberger/php-exception-reporter/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)

❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)

🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)

💻 [GitHub Profile](https://github.com/philiprehberger)

🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)

License
-------

[](#license)

[MIT](LICENSE)

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance88

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 94.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

6

Last Release

87d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cfd7d24cbbf32400fa13ce0bbe7a31edd2d66a6d4488eafdb3d64c5337bf0435?d=identicon)[philiprehberger](/maintainers/philiprehberger)

---

Top Contributors

[![philiprehberger](https://avatars.githubusercontent.com/u/8218077?v=4)](https://github.com/philiprehberger "philiprehberger (18 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

loggingexceptionerrorreportingwebhook

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/philiprehberger-php-exception-reporter/health.svg)

```
[![Health](https://phpackages.com/badges/philiprehberger-php-exception-reporter/health.svg)](https://phpackages.com/packages/philiprehberger-php-exception-reporter)
```

###  Alternatives

[facade/flare-client-php

Send PHP errors to Flare

518100.9M4](/packages/facade-flare-client-php)[spatie/flare-client-php

Send PHP errors to Flare

177156.9M21](/packages/spatie-flare-client-php)[justbetter/magento2-sentry

Magento 2 Logger for Sentry

1861.5M3](/packages/justbetter-magento2-sentry)[phptek/sentry

Sentry.io integration for SilverStripe. Binds Sentry.io to SilverStripe's error &amp; exception handling subsystem.

15208.0k4](/packages/phptek-sentry)[e2ex/e2ex

Converts PHP Errors to Exceptions and (optionally) logs PHP Errors, Notices and Warnings with a PSR-3 compatible logger

101.5k](/packages/e2ex-e2ex)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
