Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,14 @@ public function getSdkVersion(): string
*/
private function prepareEvent(Event $event, ?EventHint $hint = null, ?Scope $scope = null): ?Event
{
$sampleRate = $this->options->getSampleRate();

if (EventType::event() === $event->getType() && $sampleRate < 1 && mt_rand(1, 100) / 100.0 > $sampleRate) {
$this->logger->info(\sprintf('The %s will be discarded because it has been sampled.', $this->getEventDescription($event)), ['event' => $event]);

return null;
}

if ($hint !== null) {
if ($hint->exception !== null && empty($event->getExceptions())) {
$this->addThrowableToEvent($event, $hint->exception, $hint);
Expand Down Expand Up @@ -314,22 +322,7 @@ private function prepareEvent(Event $event, ?EventHint $hint = null, ?Scope $sco
$event->setEnvironment($this->options->getEnvironment() ?? Event::DEFAULT_ENVIRONMENT);
}

$eventDescription = \sprintf(
'%s%s [%s]',
$event->getLevel() !== null ? $event->getLevel() . ' ' : '',
(string) $event->getType(),
(string) $event->getId()
);

$isEvent = EventType::event() === $event->getType();
$sampleRate = $this->options->getSampleRate();

// only sample with the `sample_rate` on errors/messages
if ($isEvent && $sampleRate < 1 && mt_rand(1, 100) / 100.0 > $sampleRate) {
$this->logger->info(\sprintf('The %s will be discarded because it has been sampled.', $eventDescription), ['event' => $event]);

return null;
}
$eventDescription = $this->getEventDescription($event);

$event = $this->applyIgnoreOptions($event, $eventDescription);

Expand Down Expand Up @@ -368,6 +361,16 @@ private function prepareEvent(Event $event, ?EventHint $hint = null, ?Scope $sco
return $event;
}

private function getEventDescription(Event $event): string
{
return \sprintf(
'%s%s [%s]',
$event->getLevel() !== null ? $event->getLevel() . ' ' : '',
(string) $event->getType(),
(string) $event->getId()
);
}

/**
* Checks if an exception should be ignored based on configured patterns.
* Supports both class hierarchy matching and regex patterns.
Expand Down Expand Up @@ -528,6 +531,11 @@ private function addMissingStacktraceToEvent(Event $event): void
return;
}

// Only error events carry a stacktrace, for any other type it would be discarded
if ($event->getType() !== EventType::event()) {
return;
}

// We should not add a stacktrace when the event already has one or contains exceptions
if ($event->getStacktrace() !== null || !empty($event->getExceptions())) {
return;
Expand Down
57 changes: 57 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,40 @@ public static function captureEventAttachesStacktraceAccordingToAttachStacktrace
];
}

/**
* @dataProvider captureEventDoesNotAttachStacktraceToNonErrorEventsDataProvider
*/
public function testCaptureEventDoesNotAttachStacktraceToNonErrorEvents(Event $event): void
{
/** @var TransportInterface&MockObject $transport */
$transport = $this->createMock(TransportInterface::class);
$transport->expects($this->once())
->method('send')
->with($this->callback(static function (Event $event): bool {
return $event->getStacktrace() === null;
}))
->willReturnCallback(static function (Event $event): Result {
return new Result(ResultStatus::success(), $event);
});

$client = ClientBuilder::create(['attach_stacktrace' => true])
->setTransport($transport)
->getClient();

$this->assertNotNull($client->captureEvent($event));
}

public static function captureEventDoesNotAttachStacktraceToNonErrorEventsDataProvider(): \Generator
{
yield 'transaction' => [Event::createTransaction()];

yield 'check-in' => [Event::createCheckIn()];

yield 'logs' => [Event::createLogs()];

yield 'metrics' => [Event::createMetrics()];
}

public function testCaptureEventPrefersExplicitStacktrace(): void
{
$stacktrace = new Stacktrace([
Expand Down Expand Up @@ -652,6 +686,29 @@ public function testProcessEventDiscardsEventWhenSampleRateOptionIsZero(): void
$client->captureEvent(Event::createEvent());
}

public function testProcessEventDoesNotBuildStacktracesWhenEventIsNotSampled(): void
{
$logger = $this->createMock(LoggerInterface::class);
$logger->expects($this->exactly(2))
->method('info')
->with(
new StringMatchesFormatDescription('The event [%s] will be discarded because it has been sampled.'),
$this->callback(static function (array $context): bool {
return $context['event'] instanceof Event
&& $context['event']->getExceptions() === []
&& $context['event']->getStacktrace() === null;
})
);

$client = ClientBuilder::create(['sample_rate' => 0, 'attach_stacktrace' => true])
->setTransport($this->createMock(TransportInterface::class))
->setLogger($logger)
->getClient();

$client->captureException(new \Exception('foo'));
$client->captureMessage('foo');
}

public function testProcessEventCapturesEventWhenSampleRateOptionIsAboveZero(): void
{
$transport = $this->createMock(TransportInterface::class);
Expand Down
Loading