diff --git a/.github/workflows/changelog-preview.yml b/.github/workflows/changelog-preview.yml new file mode 100644 index 000000000..30c6083c6 --- /dev/null +++ b/.github/workflows/changelog-preview.yml @@ -0,0 +1,18 @@ +name: Changelog Preview +on: + pull_request: + types: + - opened + - synchronize + - reopened + - edited + - labeled + - unlabeled +permissions: + contents: write + pull-requests: write + +jobs: + changelog-preview: + uses: getsentry/craft/.github/workflows/changelog-preview.yml@v2 + secrets: inherit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1102d63d..bb51e01f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,15 +27,15 @@ jobs: - ubuntu-latest - windows-latest php: - - { version: '7.2', phpunit: '^8.5.40' } - - { version: '7.3', phpunit: '^9.6.21' } - - { version: '7.4', phpunit: '^9.6.21' } - - { version: '8.0', phpunit: '^9.6.21' } - - { version: '8.1', phpunit: '^9.6.21' } - - { version: '8.2', phpunit: '^9.6.21' } - - { version: '8.3', phpunit: '^9.6.21' } - - { version: '8.4', phpunit: '^9.6.21' } - - { version: '8.5', phpunit: '^9.6.25' } + - { version: '7.2', phpunit: '^8.5.52' } + - { version: '7.3', phpunit: '^9.6.34' } + - { version: '7.4', phpunit: '^9.6.34' } + - { version: '8.0', phpunit: '^9.6.34' } + - { version: '8.1', phpunit: '^9.6.34' } + - { version: '8.2', phpunit: '^9.6.34' } + - { version: '8.3', phpunit: '^9.6.34' } + - { version: '8.4', phpunit: '^9.6.34' } + - { version: '8.5', phpunit: '^9.6.34' } dependencies: - lowest - highest diff --git a/.github/workflows/publish-release.yaml b/.github/workflows/publish-release.yaml index 705c0958a..bff6fefe1 100644 --- a/.github/workflows/publish-release.yaml +++ b/.github/workflows/publish-release.yaml @@ -24,7 +24,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} @@ -35,7 +35,7 @@ jobs: fetch-depth: 0 - name: Prepare release - uses: getsentry/action-prepare-release@v1 + uses: getsentry/craft@c6e2f04939b6ee67030588afbb5af76b127d8203 env: GITHUB_TOKEN: ${{ steps.token.outputs.token }} with: diff --git a/.gitignore b/.gitignore index cd5863099..705a13d67 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ package.xml .phpunit.result.cache docs/_build tests/clover.xml + +# Local Claude Code settings that should not be committed +.claude/settings.local.json diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 8a5c51fdb..aed65b5ed 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -38,6 +38,7 @@ ], 'no_whitespace_before_comma_in_array' => false, // Should be dropped when we drop support for PHP 7.x 'stringable_for_to_string' => false, + 'modern_serialization_methods' => false, // Could be re-enabled when we drop support for PHP 7.3 and lower ]) ->setRiskyAllowed(true) ->setFinder( diff --git a/composer.json b/composer.json index b9bdfc1f1..cbc3ba203 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "monolog/monolog": "^1.6|^2.0|^3.0", "phpbench/phpbench": "^1.0", "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^8.5|^9.6", + "phpunit/phpunit": "^8.5.52|^9.6.34", "vimeo/psalm": "^4.17" }, "suggest": { diff --git a/src/HttpClient/HttpClient.php b/src/HttpClient/HttpClient.php index 2a373d930..fc0823373 100644 --- a/src/HttpClient/HttpClient.php +++ b/src/HttpClient/HttpClient.php @@ -57,7 +57,7 @@ public function sendRequest(Request $request, Options $options): Response } $responseHeaders = []; - $responseHeaderCallback = function ($curlHandle, $headerLine) use (&$responseHeaders): int { + $responseHeaderCallback = static function ($curlHandle, $headerLine) use (&$responseHeaders): int { return Http::parseResponseHeaders($headerLine, $responseHeaders); }; diff --git a/src/Logs/LogsAggregator.php b/src/Logs/LogsAggregator.php index a1922d346..31df0a735 100644 --- a/src/Logs/LogsAggregator.php +++ b/src/Logs/LogsAggregator.php @@ -78,7 +78,7 @@ public function add( $log->setAttribute('sentry.sdk.version', $client->getSdkVersion()); } - $hub->configureScope(function (Scope $scope) use ($log) { + $hub->configureScope(static function (Scope $scope) use ($log) { $user = $scope->getUser(); if ($user !== null) { if ($user->getId() !== null) { @@ -181,7 +181,7 @@ private function getTraceId(HubInterface $hub): string $traceId = ''; - $hub->configureScope(function (Scope $scope) use (&$traceId) { + $hub->configureScope(static function (Scope $scope) use (&$traceId) { $traceId = (string) $scope->getPropagationContext()->getTraceId(); }); diff --git a/src/Metrics/MetricsAggregator.php b/src/Metrics/MetricsAggregator.php index e18ca7ddf..a25df0b35 100644 --- a/src/Metrics/MetricsAggregator.php +++ b/src/Metrics/MetricsAggregator.php @@ -79,7 +79,7 @@ public function add( ]; if ($options->shouldSendDefaultPii()) { - $hub->configureScope(function (Scope $scope) use (&$defaultAttributes) { + $hub->configureScope(static function (Scope $scope) use (&$defaultAttributes) { $user = $scope->getUser(); if ($user !== null) { if ($user->getId() !== null) { @@ -111,7 +111,7 @@ public function add( $spanId = $span->getSpanId(); $traceId = $span->getTraceId(); } else { - $hub->configureScope(function (Scope $scope) use (&$traceId, &$spanId) { + $hub->configureScope(static function (Scope $scope) use (&$traceId, &$spanId) { $propagationContext = $scope->getPropagationContext(); $traceId = $propagationContext->getTraceId(); $spanId = $propagationContext->getSpanId(); diff --git a/src/Monolog/LogsHandler.php b/src/Monolog/LogsHandler.php index 9ee342a4f..9eb49e440 100644 --- a/src/Monolog/LogsHandler.php +++ b/src/Monolog/LogsHandler.php @@ -18,7 +18,9 @@ class LogsHandler implements HandlerInterface /** * The minimum logging level at which this handler will be triggered. * - * @var LogLevel + * @psalm-suppress UndefinedDocblockClass + * + * @var LogLevel|\Monolog\Level|int */ private $logLevel; @@ -32,21 +34,32 @@ class LogsHandler implements HandlerInterface /** * Creates a new Monolog handler that converts Monolog logs to Sentry logs. * - * @param LogLevel|null $logLevel the minimum logging level at which this handler will be triggered and collects the logs - * @param bool $bubble whether the messages that are handled can bubble up the stack or not + * @psalm-suppress UndefinedDocblockClass + * + * @param LogLevel|\Monolog\Level|int|null $logLevel the minimum logging level at which this handler will be triggered and collects the logs + * @param bool $bubble whether the messages that are handled can bubble up the stack or not */ - public function __construct(?LogLevel $logLevel = null, bool $bubble = true) + public function __construct($logLevel = null, bool $bubble = true) { $this->logLevel = $logLevel ?? LogLevel::debug(); $this->bubble = $bubble; } /** + * @psalm-suppress UndefinedDocblockClass + * @psalm-suppress UndefinedClass + * * @param array|LogRecord $record */ public function isHandling($record): bool { - return self::getSentryLogLevelFromMonologLevel($record['level'])->getPriority() >= $this->logLevel->getPriority(); + if ($this->logLevel instanceof LogLevel) { + return self::getSentryLogLevelFromMonologLevel($record['level'])->getPriority() >= $this->logLevel->getPriority(); + } elseif ($this->logLevel instanceof \Monolog\Level) { + return $record['level'] >= $this->logLevel->value; + } + + return $record['level'] >= $this->logLevel; } /** diff --git a/src/SentrySdk.php b/src/SentrySdk.php index 29d43931d..dc08cfaf4 100644 --- a/src/SentrySdk.php +++ b/src/SentrySdk.php @@ -4,6 +4,8 @@ namespace Sentry; +use Sentry\Logs\Logs; +use Sentry\Metrics\TraceMetrics; use Sentry\State\Hub; use Sentry\State\HubInterface; @@ -64,4 +66,20 @@ public static function setCurrentHub(HubInterface $hub): HubInterface return $hub; } + + /** + * Flushes all buffered telemetry data. + * + * This is a convenience facade that forwards the flush operation to all + * internally managed components. + * + * Calling this method is equivalent to invoking `flush()` on each component + * individually. It does not change flushing behavior, improve performance, + * or reduce the number of network requests. + */ + public static function flush(): void + { + Logs::getInstance()->flush(); + TraceMetrics::getInstance()->flush(); + } } diff --git a/src/Tracing/Span.php b/src/Tracing/Span.php index b2aa1adea..1d909ad9d 100644 --- a/src/Tracing/Span.php +++ b/src/Tracing/Span.php @@ -299,7 +299,7 @@ public function setStatus(?SpanStatus $status) */ public function setHttpStatus(int $statusCode) { - SentrySdk::getCurrentHub()->configureScope(function (Scope $scope) use ($statusCode) { + SentrySdk::getCurrentHub()->configureScope(static function (Scope $scope) use ($statusCode) { $scope->setContext('response', [ 'status_code' => $statusCode, ]); diff --git a/src/functions.php b/src/functions.php index e0a39c685..dbdee4381 100644 --- a/src/functions.php +++ b/src/functions.php @@ -14,6 +14,7 @@ use Sentry\Tracing\SpanContext; use Sentry\Tracing\Transaction; use Sentry\Tracing\TransactionContext; +use Sentry\Transport\TransportInterface; /** * Creates a new Client and Hub which will be set as current. @@ -61,7 +62,7 @@ * trace_propagation_targets?: array|null, * traces_sample_rate?: float|int|null, * traces_sampler?: callable|null, - * transport?: callable, + * transport?: TransportInterface|null, * } $options The client options */ function init(array $options = []): void @@ -247,7 +248,7 @@ function startTransaction(TransactionContext $context, array $customSamplingCont */ function trace(callable $trace, SpanContext $context) { - return SentrySdk::getCurrentHub()->withScope(function (Scope $scope) use ($context, $trace) { + return SentrySdk::getCurrentHub()->withScope(static function (Scope $scope) use ($context, $trace) { $parentSpan = $scope->getSpan(); // If there is a span set on the scope and it's sampled there is an active transaction. @@ -289,7 +290,7 @@ function getTraceparent(): string } $traceParent = ''; - $hub->configureScope(function (Scope $scope) use (&$traceParent) { + $hub->configureScope(static function (Scope $scope) use (&$traceParent) { $traceParent = $scope->getPropagationContext()->toTraceparent(); }); @@ -314,7 +315,7 @@ function getBaggage(): string } $baggage = ''; - $hub->configureScope(function (Scope $scope) use (&$baggage) { + $hub->configureScope(static function (Scope $scope) use (&$baggage) { $baggage = $scope->getPropagationContext()->toBaggage(); }); @@ -330,7 +331,7 @@ function getBaggage(): string function continueTrace(string $sentryTrace, string $baggage): TransactionContext { $hub = SentrySdk::getCurrentHub(); - $hub->configureScope(function (Scope $scope) use ($sentryTrace, $baggage) { + $hub->configureScope(static function (Scope $scope) use ($sentryTrace, $baggage) { $propagationContext = PropagationContext::fromHeaders($sentryTrace, $baggage); $scope->setPropagationContext($propagationContext); }); @@ -357,7 +358,22 @@ function trace_metrics(): TraceMetrics */ function addFeatureFlag(string $name, bool $result): void { - SentrySdk::getCurrentHub()->configureScope(function (Scope $scope) use ($name, $result) { + SentrySdk::getCurrentHub()->configureScope(static function (Scope $scope) use ($name, $result) { $scope->addFeatureFlag($name, $result); }); } + +/** + * Flushes all buffered telemetry data. + * + * This is a convenience facade that forwards the flush operation to all + * internally managed components. + * + * Calling this method is equivalent to invoking `flush()` on each component + * individually. It does not change flushing behavior, improve performance, + * or reduce the number of network requests. + */ +function flush(): void +{ + SentrySdk::flush(); +} diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 8e3bd4643..6f23fba98 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -980,7 +980,7 @@ public function testAttachStacktrace(): void $transport = $this->createMock(TransportInterface::class); $transport->expects($this->once()) ->method('send') - ->with($this->callback(function (Event $event): bool { + ->with($this->callback(static function (Event $event): bool { $result = $event->getStacktrace(); return $result !== null; diff --git a/tests/FrameBuilderTest.php b/tests/FrameBuilderTest.php index 667b85a18..ecb01f0e1 100644 --- a/tests/FrameBuilderTest.php +++ b/tests/FrameBuilderTest.php @@ -290,7 +290,7 @@ public function testGetFunctionArgumentsWithVariadicParameters(): void $options = new Options([]); $frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options)); - $testFunction = function (string $first, int $second, ...$rest) { + $testFunction = static function (string $first, int $second, ...$rest) { }; $backtraceFrame = [ @@ -325,7 +325,7 @@ public function testGetFunctionArgumentsWithOnlyVariadicParameters(): void $options = new Options([]); $frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options)); - $testFunction = function (...$args) { + $testFunction = static function (...$args) { }; $backtraceFrame = [ @@ -352,7 +352,7 @@ public function testGetFunctionArgumentsWithEmptyVariadicParameters(): void $options = new Options([]); $frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options)); - $testFunction = function (string $first, ...$rest) { + $testFunction = static function (string $first, ...$rest) { }; $backtraceFrame = [ @@ -381,7 +381,7 @@ public function testGetFunctionArgumentsWithNullValues(): void $options = new Options([]); $frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options)); - $testFunction = function (string $first, $second, ...$rest) { + $testFunction = static function (string $first, $second, ...$rest) { }; $backtraceFrame = [ @@ -411,7 +411,7 @@ public function testGetFunctionArgumentsWithGapsInBacktraceArrayIndices(): void $options = new Options([]); $frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options)); - $testFunction = function (string $first, int $second, ...$rest) { + $testFunction = static function (string $first, int $second, ...$rest) { }; $backtraceFrameArgs = []; diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 488a40c7a..1b5689b8d 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -225,15 +225,15 @@ public function testWithMonitor(): void $hub->expects($this->exactly(2)) ->method('captureCheckIn') ->with( - $this->callback(function (string $slug): bool { + $this->callback(static function (string $slug): bool { return $slug === 'test-crontab'; }), - $this->callback(function (CheckInStatus $checkInStatus): bool { + $this->callback(static function (CheckInStatus $checkInStatus): bool { // just check for type CheckInStatus return true; }), $this->anything(), - $this->callback(function (MonitorConfig $monitorConfig): bool { + $this->callback(static function (MonitorConfig $monitorConfig): bool { return $monitorConfig->getSchedule()->getValue() === '*/5 * * * *' && $monitorConfig->getSchedule()->getType() === MonitorSchedule::TYPE_CRONTAB && $monitorConfig->getCheckinMargin() === 5 @@ -244,7 +244,7 @@ public function testWithMonitor(): void SentrySdk::setCurrentHub($hub); - withMonitor('test-crontab', function () { + withMonitor('test-crontab', static function () { // Do something... }, new MonitorConfig( new MonitorSchedule(MonitorSchedule::TYPE_CRONTAB, '*/5 * * * *'), @@ -262,15 +262,15 @@ public function testWithMonitorCallableThrows(): void $hub->expects($this->exactly(2)) ->method('captureCheckIn') ->with( - $this->callback(function (string $slug): bool { + $this->callback(static function (string $slug): bool { return $slug === 'test-crontab'; }), - $this->callback(function (CheckInStatus $checkInStatus): bool { + $this->callback(static function (CheckInStatus $checkInStatus): bool { // just check for type CheckInStatus return true; }), $this->anything(), - $this->callback(function (MonitorConfig $monitorConfig): bool { + $this->callback(static function (MonitorConfig $monitorConfig): bool { return $monitorConfig->getSchedule()->getValue() === '*/5 * * * *' && $monitorConfig->getSchedule()->getType() === MonitorSchedule::TYPE_CRONTAB && $monitorConfig->getCheckinMargin() === 5 @@ -281,7 +281,7 @@ public function testWithMonitorCallableThrows(): void SentrySdk::setCurrentHub($hub); - withMonitor('test-crontab', function () { + withMonitor('test-crontab', static function () { throw new \Exception(); }, new MonitorConfig( new MonitorSchedule(MonitorSchedule::TYPE_CRONTAB, '*/5 * * * *'), @@ -353,7 +353,7 @@ public function testTraceReturnsClosureResult(): void { $returnValue = 'foo'; - $result = trace(function () use ($returnValue) { + $result = trace(static function () use ($returnValue) { return $returnValue; }, new SpanContext()); @@ -380,7 +380,7 @@ public function testTraceCorrectlyReplacesAndRestoresCurrentSpan(): void $this->assertSame($transaction, $hub->getSpan()); try { - trace(function () { + trace(static function () { throw new \RuntimeException('Throwing should still restore the previous span'); }, new SpanContext()); } catch (\RuntimeException $e) { diff --git a/tests/Logs/LogsAggregatorTest.php b/tests/Logs/LogsAggregatorTest.php index 1478853e8..92bcb365d 100644 --- a/tests/Logs/LogsAggregatorTest.php +++ b/tests/Logs/LogsAggregatorTest.php @@ -170,7 +170,7 @@ public function testAttributesAreAddedToLogMessage(): void $hub = new Hub($client); SentrySdk::setCurrentHub($hub); - $hub->configureScope(function (Scope $scope) { + $hub->configureScope(static function (Scope $scope) { $userDataBag = new UserDataBag(); $userDataBag->setId('unique_id'); $userDataBag->setEmail('foo@example.com'); diff --git a/tests/Logs/LogsTest.php b/tests/Logs/LogsTest.php index 3aab97419..4dd361b56 100644 --- a/tests/Logs/LogsTest.php +++ b/tests/Logs/LogsTest.php @@ -171,7 +171,7 @@ private function assertEvent(callable $assert, array $options = []): ClientInter $transport = $this->createMock(TransportInterface::class); $transport->expects($this->once()) ->method('send') - ->with($this->callback(function (Event $event) use ($assert): bool { + ->with($this->callback(static function (Event $event) use ($assert): bool { $assert($event); return true; diff --git a/tests/Monolog/LogsHandlerTest.php b/tests/Monolog/LogsHandlerTest.php index a18739b6b..df27ae4c8 100644 --- a/tests/Monolog/LogsHandlerTest.php +++ b/tests/Monolog/LogsHandlerTest.php @@ -4,19 +4,17 @@ namespace Sentry\Tests\Monolog; +use Monolog\Level; use Monolog\Logger; use PHPUnit\Framework\TestCase; use Sentry\ClientBuilder; -use Sentry\Event; use Sentry\Logs\Log; use Sentry\Logs\LogLevel; use Sentry\Logs\Logs; use Sentry\Monolog\LogsHandler; use Sentry\SentrySdk; use Sentry\State\Hub; -use Sentry\Transport\Result; -use Sentry\Transport\ResultStatus; -use Sentry\Transport\TransportInterface; +use Sentry\Tests\StubTransport; final class LogsHandlerTest extends TestCase { @@ -64,42 +62,47 @@ static function (string $key) { } /** - * @dataProvider logLevelDataProvider + * @dataProvider monologLegacyLevelDataProvider */ - public function testLogLevels($record, int $countLogs): void + public function testFiltersAndMapsUsingLegacyMonologThreshold(int $threshold, int $recordLevel, int $expectedCount, ?LogLevel $expectedMappedLevel): void { - $handler = new LogsHandler(LogLevel::warn()); - $handler->handle($record); + $handler = new LogsHandler($threshold); + $handler->handle(RecordFactory::create('foo bar', $recordLevel, 'channel.foo', [], [])); $logs = Logs::getInstance()->aggregator()->all(); - $this->assertCount($countLogs, $logs); + $this->assertCount($expectedCount, $logs); + + if ($expectedMappedLevel !== null) { + $this->assertEquals($expectedMappedLevel, $logs[0]->getLevel()); + } + } + + /** + * @dataProvider monologLevelDataProvider + */ + public function testFiltersAndMapsUsingMonologEnumThreshold($threshold, $recordLevel, int $expectedCount, ?LogLevel $expectedMappedLevel): void + { + if (!class_exists(Level::class)) { + $this->markTestSkipped('Test only works for Monolog >= 3'); + } + + $this->assertInstanceOf(Level::class, $threshold); + $this->assertInstanceOf(Level::class, $recordLevel); + + $handler = new LogsHandler($threshold); + $handler->handle(RecordFactory::create('foo bar', $recordLevel->value, 'channel.foo', [], [])); + + $logs = Logs::getInstance()->aggregator()->all(); + $this->assertCount($expectedCount, $logs); + + if ($expectedMappedLevel !== null) { + $this->assertEquals($expectedMappedLevel, $logs[0]->getLevel()); + } } public function testLogsHandlerDestructor() { - $transport = new class implements TransportInterface { - private $events = []; - - public function send(Event $event): Result - { - $this->events[] = $event; - - return new Result(ResultStatus::success()); - } - - public function close(?int $timeout = null): Result - { - return new Result(ResultStatus::success()); - } - - /** - * @return Event[] - */ - public function getEvents(): array - { - return $this->events; - } - }; + $transport = new StubTransport(); $client = ClientBuilder::create([ 'enable_logs' => true, ])->setTransport($transport) @@ -110,8 +113,8 @@ public function getEvents(): array $this->handleLogAndDrop(); - $this->assertCount(1, $transport->getEvents()); - $this->assertSame('I was dropped :(', $transport->getEvents()[0]->getLogs()[0]->getBody()); + $this->assertCount(1, StubTransport::$events); + $this->assertSame('I was dropped :(', StubTransport::$events[0]->getLogs()[0]->getBody()); } private function handleLogAndDrop(): void @@ -283,83 +286,127 @@ public static function handleDataProvider(): iterable ]; } - public static function logLevelDataProvider(): iterable + public static function monologLegacyLevelDataProvider(): iterable { - yield [ - RecordFactory::create( - 'foo bar', - Logger::DEBUG, - 'channel.foo', - [], - [] - ), + yield 'NOTICE threshold drops INFO (both map to sentry info)' => [ + Logger::NOTICE, + Logger::INFO, 0, + null, ]; - yield [ - RecordFactory::create( - 'foo bar', - Logger::NOTICE, - 'channel.foo', - [], - [] - ), + yield 'NOTICE threshold keeps NOTICE (mapped to sentry info)' => [ + Logger::NOTICE, + Logger::NOTICE, + 1, + LogLevel::info(), + ]; + + yield 'NOTICE threshold keeps WARNING (mapped to sentry warn)' => [ + Logger::NOTICE, + Logger::WARNING, + 1, + LogLevel::warn(), + ]; + + yield 'ALERT threshold drops CRITICAL (both map to sentry fatal)' => [ + Logger::ALERT, + Logger::CRITICAL, 0, + null, ]; - yield [ - RecordFactory::create( - 'foo bar', - Logger::INFO, - 'channel.foo', - [], - [] - ), + yield 'ALERT threshold keeps ALERT (mapped to sentry fatal)' => [ + Logger::ALERT, + Logger::ALERT, + 1, + LogLevel::fatal(), + ]; + + yield 'ALERT threshold keeps EMERGENCY (mapped to sentry fatal)' => [ + Logger::ALERT, + Logger::EMERGENCY, + 1, + LogLevel::fatal(), + ]; + + yield 'EMERGENCY threshold drops ALERT (both map to sentry fatal)' => [ + Logger::EMERGENCY, + Logger::ALERT, 0, + null, ]; - yield [ - RecordFactory::create( - 'foo bar', - Logger::WARNING, - 'channel.foo', - [], - [] - ), + yield 'EMERGENCY threshold keeps EMERGENCY (mapped to sentry fatal)' => [ + Logger::EMERGENCY, + Logger::EMERGENCY, 1, + LogLevel::fatal(), ]; + } - yield [ - RecordFactory::create( - 'foo bar', - Logger::CRITICAL, - 'channel.foo', - [], - [] - ), + public static function monologLevelDataProvider(): iterable + { + if (!class_exists(Level::class)) { + yield 'Monolog < 3 (skipped)' => [null, null, 0, null]; + + return; + } + + yield 'NOTICE threshold drops INFO (both map to sentry info)' => [ + Level::Notice, + Level::Info, + 0, + null, + ]; + + yield 'NOTICE threshold keeps NOTICE (mapped to sentry info)' => [ + Level::Notice, + Level::Notice, 1, + LogLevel::info(), ]; - yield [ - RecordFactory::create( - 'foo bar', - Logger::ALERT, - 'channel.foo', - [], - [] - ), + yield 'NOTICE threshold keeps WARNING (mapped to sentry warn)' => [ + Level::Notice, + Level::Warning, 1, + LogLevel::warn(), ]; - yield [ - RecordFactory::create( - 'foo bar', - Logger::EMERGENCY, - 'channel.foo', - [], - [] - ), + yield 'ALERT threshold drops CRITICAL (both map to sentry fatal)' => [ + Level::Alert, + Level::Critical, + 0, + null, + ]; + + yield 'ALERT threshold keeps ALERT (mapped to sentry fatal)' => [ + Level::Alert, + Level::Alert, + 1, + LogLevel::fatal(), + ]; + + yield 'ALERT threshold keeps EMERGENCY (mapped to sentry fatal)' => [ + Level::Alert, + Level::Emergency, + 1, + LogLevel::fatal(), + ]; + + yield 'EMERGENCY threshold drops ALERT (both map to sentry fatal)' => [ + Level::Emergency, + Level::Alert, + 0, + null, + ]; + + yield 'EMERGENCY threshold keeps EMERGENCY (mapped to sentry fatal)' => [ + Level::Emergency, + Level::Emergency, 1, + LogLevel::fatal(), ]; } } diff --git a/tests/Monolog/RecordFactory.php b/tests/Monolog/RecordFactory.php index 39e2b67d6..be2213059 100644 --- a/tests/Monolog/RecordFactory.php +++ b/tests/Monolog/RecordFactory.php @@ -19,7 +19,7 @@ final class RecordFactory * * @return array|LogRecord */ - public static function create(string $message, int $level, string $channel, array $context, array $extra) + public static function create(string $message, int $level, string $channel, array $context = [], array $extra = []) { if (Logger::API >= 3) { return new LogRecord( diff --git a/tests/OptionResolverTest.php b/tests/OptionResolverTest.php index df8d9c470..176b2dd17 100644 --- a/tests/OptionResolverTest.php +++ b/tests/OptionResolverTest.php @@ -114,7 +114,7 @@ public function testNormalizerReturnsInvalidType() $resolver = new OptionsResolver(); $resolver->setDefaults(['foo' => 'bar']); $resolver->setAllowedTypes('foo', ['string']); - $resolver->setNormalizer('foo', function ($value) { + $resolver->setNormalizer('foo', static function ($value) { return 8; }); $result = $resolver->resolve(['foo' => 'test']); @@ -126,7 +126,7 @@ public function testNormalizerReturnsInvalidValue() $resolver = new OptionsResolver(); $resolver->setDefaults(['foo' => 'b']); $resolver->setAllowedValues('foo', ['a', 'b', 'c']); - $resolver->setNormalizer('foo', function ($value) { + $resolver->setNormalizer('foo', static function ($value) { return 'z'; }); $result = $resolver->resolve(['foo' => 'a']); @@ -138,7 +138,7 @@ public function testNormalizerResultFailsValidation() $resolver = new OptionsResolver(); $resolver->setDefaults(['foo' => 'b']); $resolver->setAllowedValues('foo', ['a', 'b', 'c']); - $resolver->setNormalizer('foo', function ($value) { + $resolver->setNormalizer('foo', static function ($value) { return false; }); $result = $resolver->resolve(['foo' => 'a']); @@ -237,7 +237,7 @@ public function allowedValueTestProvider(): \Generator yield 'Callback validates successfully' => [ ['count' => 50], ['count' => 10], - ['count' => function ($value) { + ['count' => static function ($value) { return $value >= 0 && $value <= 100; }], ['count' => 10], @@ -246,7 +246,7 @@ public function allowedValueTestProvider(): \Generator yield 'Callback validation fails' => [ ['count' => 50], ['count' => 200], - ['count' => function ($value) { + ['count' => static function ($value) { return $value >= 0 && $value <= 100; }], ['count' => 50], @@ -281,7 +281,7 @@ public function normalizerTestProvider(): \Generator yield 'Normalizes successful' => [ ['a' => 'b'], ['a' => ' c '], - ['a' => function ($value) { + ['a' => static function ($value) { return trim($value); }], ['a' => 'c'], diff --git a/tests/Serializer/AbstractSerializerTest.php b/tests/Serializer/AbstractSerializerTest.php index 752e7e44b..a40479ad7 100644 --- a/tests/Serializer/AbstractSerializerTest.php +++ b/tests/Serializer/AbstractSerializerTest.php @@ -438,7 +438,7 @@ public function serializableCallableProvider(): array return [ [ - 'callable' => function (array $param1) { + 'callable' => static function (array $param1) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -446,7 +446,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [array param1]', ], [ - 'callable' => function ($param1a) { + 'callable' => static function ($param1a) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -454,7 +454,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [mixed|null param1a]', ], [ - 'callable' => function (callable $param1c) { + 'callable' => static function (callable $param1c) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -462,7 +462,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [callable param1c]', ], [ - 'callable' => function (\stdClass $param1d) { + 'callable' => static function (\stdClass $param1d) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -470,7 +470,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [stdClass param1d]', ], [ - 'callable' => function (?\stdClass $param1e = null) { + 'callable' => static function (?\stdClass $param1e = null) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -478,7 +478,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [stdClass|null [param1e]]', ], [ - 'callable' => function (array &$param1f) { + 'callable' => static function (array &$param1f) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -486,7 +486,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [array ¶m1f]', ], [ - 'callable' => function (?array &$param1g = null) { + 'callable' => static function (?array &$param1g = null) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -514,7 +514,7 @@ public function serializableCallableProvider(): array 'expected' => 'Callable void ' . SerializerTestObject::class . '::testy []', ], [ - 'callable' => function (int $param1_70a) { + 'callable' => static function (int $param1_70a) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -522,7 +522,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [int param1_70a]', ], [ - 'callable' => function (&$param): int { + 'callable' => static function (&$param): int { return (int) $param; }, 'expected' => $prettyClosureNames @@ -530,7 +530,7 @@ public function serializableCallableProvider(): array : 'Lambda int ' . __NAMESPACE__ . '\\{closure} [mixed|null ¶m]', ], [ - 'callable' => function (int $param): ?int { + 'callable' => static function (int $param): ?int { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -538,7 +538,7 @@ public function serializableCallableProvider(): array : 'Lambda int ' . __NAMESPACE__ . '\\{closure} [int param]', ], [ - 'callable' => function (?int $param1_70b) { + 'callable' => static function (?int $param1_70b) { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames @@ -546,7 +546,7 @@ public function serializableCallableProvider(): array : 'Lambda ' . __NAMESPACE__ . '\\{closure} [int|null param1_70b]', ], [ - 'callable' => function (?int $param1_70c): void { + 'callable' => static function (?int $param1_70c): void { throw new \Exception('Don\'t even think about invoke me'); }, 'expected' => $prettyClosureNames diff --git a/tests/State/HubTest.php b/tests/State/HubTest.php index 0b8c2286e..54c5a9dfa 100644 --- a/tests/State/HubTest.php +++ b/tests/State/HubTest.php @@ -241,7 +241,7 @@ public function testCaptureMessage(array $functionCallArgs, array $expectedFunct $eventId = EventId::generate(); $hub = new Hub(new NoOpClient()); - $hub->configureScope(function (Scope $scope) use ($propagationContext): void { + $hub->configureScope(static function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); }); @@ -300,7 +300,7 @@ public function testCaptureException(array $functionCallArgs, array $expectedFun $eventId = EventId::generate(); $hub = new Hub(new NoOpClient()); - $hub->configureScope(function (Scope $scope) use ($propagationContext): void { + $hub->configureScope(static function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); }); @@ -355,7 +355,7 @@ public function testCaptureLastError(array $functionCallArgs, array $expectedFun $eventId = EventId::generate(); $hub = new Hub(new NoOpClient()); - $hub->configureScope(function (Scope $scope) use ($propagationContext): void { + $hub->configureScope(static function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); }); @@ -814,7 +814,7 @@ public function testStartTransactionUpdatesTheDscSampleRate(): void $client->expects($this->once()) ->method('getOptions') ->willReturn(new Options([ - 'traces_sampler' => function (SamplingContext $samplingContext): float { + 'traces_sampler' => static function (SamplingContext $samplingContext): float { return 1.0; }, ])); diff --git a/tests/State/ScopeTest.php b/tests/State/ScopeTest.php index 20b90e928..b5f96f7dc 100644 --- a/tests/State/ScopeTest.php +++ b/tests/State/ScopeTest.php @@ -406,7 +406,7 @@ public function testAddEventProcessor(): void return null; }); - $scope->addEventProcessor(function () use (&$callback3Called) { + $scope->addEventProcessor(static function () use (&$callback3Called) { $callback3Called = true; return null; @@ -426,7 +426,7 @@ public function testEventProcessorReceivesTheEventAndEventHint(): void $processorCalled = false; $processorReceivedHint = null; - $scope->addEventProcessor(function (Event $eventArg, EventHint $hint) use (&$processorCalled, &$processorReceivedHint): ?Event { + $scope->addEventProcessor(static function (Event $eventArg, EventHint $hint) use (&$processorCalled, &$processorReceivedHint): ?Event { $processorCalled = true; $processorReceivedHint = $hint; diff --git a/tests/Transport/HttpTransportTest.php b/tests/Transport/HttpTransportTest.php index 1d34137ae..206261eb0 100644 --- a/tests/Transport/HttpTransportTest.php +++ b/tests/Transport/HttpTransportTest.php @@ -62,7 +62,7 @@ public function testSend(Response $response, ResultStatus $expectedResultStatus, $this->logger->expects($this->exactly(\count($messages))) ->method($level) ->with($this->logicalOr( - ...array_map(function (string $message) { + ...array_map(static function (string $message) { return new StringMatchesFormatDescription($message); }, $messages) ));