From cb73a690e766fdbb58d6b969e6d3e0dce5281b45 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 22 May 2024 13:38:53 +0100 Subject: [PATCH 1/2] Attach Laravel's Context to events as metadata --- CHANGELOG.md | 7 +++ config/bugsnag.php | 12 ++++ src/BugsnagServiceProvider.php | 22 +++++++ tests/Middleware/UnhandledStateTest.php | 6 +- tests/ServiceProviderTest.php | 77 +++++++++++++++++++++++++ tests/Stubs/DebugBacktraceStub.php | 5 ++ 6 files changed, 128 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28b84b8c..ab6a296b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Enhancements + +* Attach Laravel 11's `Context` to events as metadata under the key 'Laravel Context'. Hidden context will only be added if the new configuration option `attach_hidden_context` is enabled, either in `config/bugsnag.php` or via the environment variable `BUGSNAG_ATTACH_HIDDEN_CONTEXT` + [#537](https://github.com/bugsnag/bugsnag-laravel/pull/537) + ## v2.27.0 (2024-03-13) ### Enhancements diff --git a/config/bugsnag.php b/config/bugsnag.php index b1071946..c58c0483 100644 --- a/config/bugsnag.php +++ b/config/bugsnag.php @@ -358,7 +358,19 @@ | The maximum number of breadcrumbs to send with a report. | | This should be an integer between 0-100 (inclusive). + | */ 'max_breadcrumbs' => null, + + /* + |-------------------------------------------------------------------------- + | Attach hidden context + |-------------------------------------------------------------------------- + | + | Whether to attach hidden Context data to events as metadata. + | + */ + + 'attach_hidden_context' => env('BUGSNAG_ATTACH_HIDDEN_CONTEXT', false), ]; diff --git a/src/BugsnagServiceProvider.php b/src/BugsnagServiceProvider.php index 944c6b6e..4164f316 100644 --- a/src/BugsnagServiceProvider.php +++ b/src/BugsnagServiceProvider.php @@ -25,6 +25,7 @@ use Illuminate\Queue\QueueManager; use Illuminate\Routing\Events\RouteMatched; use Illuminate\Support\ServiceProvider; +use Illuminate\Support\Facades\Context; use Laravel\Lumen\Application as LumenApplication; use Monolog\Handler\PsrHandler; use Monolog\Logger; @@ -439,6 +440,27 @@ protected function setupCallbacks(Client $client, Container $app, array $config) } })); } + + // Laravel 11 has a 'Context' class for storing metadata + // https://laravel.com/docs/11.x/context + if (class_exists(Context::class)) { + $client->registerCallback(function (Report $report) { + $context = Context::all(); + + $report->setMetaData(['Laravel Context' => $context]); + }); + + // only attach hidden context if enabled, otherwise sensitive data + // could leak to bugsnag + if (isset($config['attach_hidden_context']) && $config['attach_hidden_context']) { + $client->registerCallback(function (Report $report) { + $hiddenContext = Context::allHidden(); + + $report->setMetaData(['Laravel Hidden Context' => $hiddenContext]); + }); + + } + } } /** diff --git a/tests/Middleware/UnhandledStateTest.php b/tests/Middleware/UnhandledStateTest.php index 701409d8..e938daba 100644 --- a/tests/Middleware/UnhandledStateTest.php +++ b/tests/Middleware/UnhandledStateTest.php @@ -8,7 +8,11 @@ function debug_backtrace() { - return DebugBacktraceStub::get(); + if (DebugBacktraceStub::hasValue()) { + return DebugBacktraceStub::get(); + } + + return \debug_backtrace(); } namespace Bugsnag\BugsnagLaravel\Tests\Middleware; diff --git a/tests/ServiceProviderTest.php b/tests/ServiceProviderTest.php index e81500f7..5df219d5 100644 --- a/tests/ServiceProviderTest.php +++ b/tests/ServiceProviderTest.php @@ -10,11 +10,13 @@ use Bugsnag\BugsnagLaravel\Tests\Stubs\InjecteeWithLogInterface; use Bugsnag\Client; use Bugsnag\FeatureFlag; +use Bugsnag\Report; use Bugsnag\PsrLogger\BugsnagLogger; use Bugsnag\PsrLogger\MultiLogger as BaseMultiLogger; use Illuminate\Contracts\Logging\Log; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; +use Illuminate\Support\Facades\Context; class ServiceProviderTest extends AbstractTestCase { @@ -531,4 +533,79 @@ public function testMaxBreadcrumbsCanBeSetFromConfig() $this->assertInstanceOf(Client::class, $client); $this->assertSame(73, $client->getMaxBreadcrumbs()); } + + public function testItSetsContextAsMetadata() + { + if (!class_exists(Context::class)) { + $this->markTestSkipped("Requires Laravel 11's 'Context' class"); + } + + Context::add('a', 1234); + Context::add('b', true); + Context::addHidden('x', ':)'); + + /** @var Client $client */ + $client = $this->app->make(Client::class); + + Context::add('c', 'hello'); + Context::add('d', [1, 2, 3]); + + $expected = ['a' => 1234, 'b' => true, 'c' => 'hello', 'd' => [1, 2, 3]]; + $actual = []; + $hasHiddenContext = true; + + $client->notifyException( + new \Exception('hello'), + function (Report $report) use (&$actual, &$hasHiddenContext) { + $metadata = $report->getMetadata(); + + $actual = $metadata['Laravel Context']; + $hasHiddenContext = isset($metadata['Laravel Hidden Context']); + } + ); + + $this->assertSame($expected, $actual); + $this->assertFalse($hasHiddenContext); + } + + public function testItSetsHiddenContextAsMetadataIfConfiguredToDoSo() + { + if (!class_exists(Context::class)) { + $this->markTestSkipped("Requires Laravel 11's 'Context' class"); + } + + Context::addHidden('a', 1234); + Context::add('b', true); + + /** @var \Illuminate\Config\Repository $laravelConfig */ + $laravelConfig = $this->app->config; + $bugsnagConfig = $laravelConfig->get('bugsnag'); + $bugsnagConfig['attach_hidden_context'] = true; + + $laravelConfig->set('bugsnag', $bugsnagConfig); + + /** @var Client $client */ + $client = $this->app->make(Client::class); + + Context::add('c', 'hello'); + Context::addHidden('d', [1, 2, 3]); + + $expectedContext = ['b' => true, 'c' => 'hello']; + $expectedHiddenContext = ['a' => 1234, 'd' => [1, 2, 3]]; + $actualContext = []; + $actualHiddenContext = []; + + $client->notifyException( + new \Exception('hello'), + function (Report $report) use (&$actualContext, &$actualHiddenContext) { + $metadata = $report->getMetadata(); + + $actualContext = $metadata['Laravel Context']; + $actualHiddenContext = $metadata['Laravel Hidden Context']; + } + ); + + $this->assertSame($expectedContext, $actualContext); + $this->assertSame($expectedHiddenContext, $actualHiddenContext); + } } diff --git a/tests/Stubs/DebugBacktraceStub.php b/tests/Stubs/DebugBacktraceStub.php index df5bb9e7..8aea63e0 100644 --- a/tests/Stubs/DebugBacktraceStub.php +++ b/tests/Stubs/DebugBacktraceStub.php @@ -25,6 +25,11 @@ public static function set(array $backtrace) self::$backtrace = $backtrace; } + public static function hasValue() + { + return self::$backtrace !== []; + } + public static function clear() { self::$backtrace = []; From 3db936cf49a83d7a58aea48d719b14e7ee28a3de Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 31 May 2024 11:23:56 +0100 Subject: [PATCH 2/2] Bump version to v2.28.0 --- CHANGELOG.md | 4 ++-- src/BugsnagServiceProvider.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6a296b..d7eda313 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ Changelog ========= -## TBD +## v2.28.0 (2024-06-03) ### Enhancements -* Attach Laravel 11's `Context` to events as metadata under the key 'Laravel Context'. Hidden context will only be added if the new configuration option `attach_hidden_context` is enabled, either in `config/bugsnag.php` or via the environment variable `BUGSNAG_ATTACH_HIDDEN_CONTEXT` +* Attach Laravel 11's `Context` to events as metadata under the key 'Laravel Context'. Hidden context will added under the key 'Laravel Hidden Context' only if the new configuration option `attach_hidden_context` is enabled, either in `config/bugsnag.php` or via the environment variable `BUGSNAG_ATTACH_HIDDEN_CONTEXT` [#537](https://github.com/bugsnag/bugsnag-laravel/pull/537) ## v2.27.0 (2024-03-13) diff --git a/src/BugsnagServiceProvider.php b/src/BugsnagServiceProvider.php index 4164f316..7ba0fcee 100644 --- a/src/BugsnagServiceProvider.php +++ b/src/BugsnagServiceProvider.php @@ -38,7 +38,7 @@ class BugsnagServiceProvider extends ServiceProvider * * @var string */ - const VERSION = '2.27.0'; + const VERSION = '2.28.0'; /** * Boot the service provider.