Skip to content

Commit 698a788

Browse files
committed
Merge branch 'master' into continuous-profiling
2 parents 49a6a63 + b49674b commit 698a788

15 files changed

Lines changed: 652 additions & 64 deletions

File tree

CHANGELOG.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
# CHANGELOG
22

3+
## 4.13.0
4+
5+
The Sentry SDK team is happy to announce the immediate availability of Sentry PHP SDK v4.13.0.
6+
7+
### Features
8+
9+
- Add regex support for `ignore_exceptions` and `ignore_transactions` [(#1850)](https://github.com/getsentry/sentry-php/pull/1850)
10+
11+
You can now use regular expressions to ignore exceptions and transactions:
12+
13+
```php
14+
Sentry\init([
15+
'ignore_exceptions' => [
16+
'/.*ArgumentException$/',
17+
],
18+
'ignore_transactions' => [
19+
'/^GET \/api\/users\/\d+$/',
20+
],
21+
]);
22+
```
23+
24+
- Add support for variadic parameters and null values [(#1849)](https://github.com/getsentry/sentry-php/pull/1849)
25+
26+
### Bug Fixes
27+
28+
- Fix `Options::setEnableLogs` [(#1852)](https://github.com/getsentry/sentry-php/pull/1852)
29+
- Fix `vsprintf` not handling errors [(#1855)](https://github.com/getsentry/sentry-php/pull/1855)
30+
331
## 4.12.0
432

533
The Sentry SDK team is happy to announce the immediate availability of Sentry PHP SDK v4.12.0.
@@ -86,7 +114,7 @@ The Sentry SDK team is happy to announce the immediate availability of Sentry PH
86114

87115
### Features
88116

89-
- Allow retrieving a single piece of data from the span by its key [(#1767)](https://github.com/getsentry/sentry-php/pull/1767)
117+
- Allow retrieving a single piece of data from the span by it's key [(#1767)](https://github.com/getsentry/sentry-php/pull/1767)
90118

91119
```php
92120
\Sentry\SentrySdk::getCurrentHub()->getSpan()?->setData([

src/Client.php

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ class Client implements ClientInterface
3232
/**
3333
* The version of the SDK.
3434
*/
35-
public const SDK_VERSION = '4.12.0';
35+
public const SDK_VERSION = '4.13.0';
36+
37+
/**
38+
* Regex pattern to detect if a string is a regex pattern (starts and ends with / optionally followed by flags).
39+
* Supported flags: i (case-insensitive), m (multiline), s (dotall), u (unicode).
40+
*/
41+
private const REGEX_PATTERN_DETECTION = '/^\/.*\/[imsu]*$/';
3642

3743
/**
3844
* @var Options The client options
@@ -149,7 +155,7 @@ public function captureMessage(string $message, ?Severity $level = null, ?Scope
149155
public function captureException(\Throwable $exception, ?Scope $scope = null, ?EventHint $hint = null): ?EventId
150156
{
151157
$className = \get_class($exception);
152-
if ($this->isIgnoredException($className)) {
158+
if ($this->shouldIgnoreException($className)) {
153159
$this->logger->info(
154160
'The exception will be discarded because it matches an entry in "ignore_exceptions".',
155161
['className' => $className]
@@ -359,11 +365,64 @@ private function prepareEvent(Event $event, ?EventHint $hint = null, ?Scope $sco
359365
return $event;
360366
}
361367

362-
private function isIgnoredException(string $className): bool
368+
/**
369+
* Checks if an exception should be ignored based on configured patterns.
370+
* Supports both class hierarchy matching and regex patterns.
371+
* Patterns starting and ending with '/' are treated as regex patterns.
372+
*/
373+
private function shouldIgnoreException(string $className): bool
374+
{
375+
foreach ($this->options->getIgnoreExceptions() as $pattern) {
376+
// Check for regex pattern (starts with / and ends with / optionally followed by flags)
377+
if (preg_match(self::REGEX_PATTERN_DETECTION, $pattern)) {
378+
try {
379+
if (preg_match($pattern, $className)) {
380+
return true;
381+
}
382+
} catch (\Throwable $e) {
383+
// Invalid regex pattern, log and skip
384+
$this->logger->warning(
385+
\sprintf('Invalid regex pattern in ignore_exceptions: "%s". Error: %s', $pattern, $e->getMessage())
386+
);
387+
continue;
388+
}
389+
} else {
390+
// Class hierarchy check
391+
if (is_a($className, $pattern, true)) {
392+
return true;
393+
}
394+
}
395+
}
396+
397+
return false;
398+
}
399+
400+
/**
401+
* Checks if a transaction should be ignored based on configured patterns.
402+
* Supports both exact string matching and regex patterns.
403+
* Patterns starting and ending with '/' are treated as regex patterns.
404+
*/
405+
private function shouldIgnoreTransaction(string $transactionName): bool
363406
{
364-
foreach ($this->options->getIgnoreExceptions() as $ignoredException) {
365-
if (is_a($className, $ignoredException, true)) {
366-
return true;
407+
foreach ($this->options->getIgnoreTransactions() as $pattern) {
408+
// Check for regex pattern (starts with / and ends with / optionally followed by flags)
409+
if (preg_match(self::REGEX_PATTERN_DETECTION, $pattern)) {
410+
try {
411+
if (preg_match($pattern, $transactionName)) {
412+
return true;
413+
}
414+
} catch (\Throwable $e) {
415+
// Invalid regex pattern, log and skip
416+
$this->logger->warning(
417+
\sprintf('Invalid regex pattern in ignore_transactions: "%s". Error: %s', $pattern, $e->getMessage())
418+
);
419+
continue;
420+
}
421+
} else {
422+
// Exact string match
423+
if ($transactionName === $pattern) {
424+
return true;
425+
}
367426
}
368427
}
369428

@@ -380,7 +439,7 @@ private function applyIgnoreOptions(Event $event, string $eventDescription): ?Ev
380439
}
381440

382441
foreach ($exceptions as $exception) {
383-
if ($this->isIgnoredException($exception->getType())) {
442+
if ($this->shouldIgnoreException($exception->getType())) {
384443
$this->logger->info(
385444
\sprintf('The %s will be discarded because it matches an entry in "ignore_exceptions".', $eventDescription),
386445
['event' => $event]
@@ -398,7 +457,7 @@ private function applyIgnoreOptions(Event $event, string $eventDescription): ?Ev
398457
return $event;
399458
}
400459

401-
if (\in_array($transactionName, $this->options->getIgnoreTransactions(), true)) {
460+
if ($this->shouldIgnoreTransaction($transactionName)) {
402461
$this->logger->info(
403462
\sprintf('The %s will be discarded because it matches a entry in "ignore_transactions".', $eventDescription),
404463
['event' => $event]

src/FrameBuilder.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,18 @@ private function getFunctionArgumentValues(\ReflectionFunctionAbstract $reflecti
219219
foreach ($reflectionFunction->getParameters() as $reflectionParameter) {
220220
$parameterPosition = $reflectionParameter->getPosition();
221221

222-
if (!isset($backtraceFrameArgs[$parameterPosition])) {
222+
if ($reflectionParameter->isVariadic()) {
223+
// For variadic parameters, collect all remaining arguments into an array
224+
$variadicArgs = [];
225+
for ($i = $parameterPosition; $i < \count($backtraceFrameArgs); ++$i) {
226+
$variadicArgs[] = $backtraceFrameArgs[$i];
227+
}
228+
$argumentValues[$reflectionParameter->getName()] = $variadicArgs;
229+
// Variadic parameter is always the last one, so we can break
230+
break;
231+
}
232+
233+
if (!\array_key_exists($parameterPosition, $backtraceFrameArgs)) {
223234
continue;
224235
}
225236

src/HttpClient/Request.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
namespace Sentry\HttpClient;
66

7-
/**
8-
* @internal
9-
*/
107
final class Request
118
{
129
/**

src/HttpClient/Response.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
namespace Sentry\HttpClient;
66

7-
/**
8-
* @internal
9-
*/
107
final class Response
118
{
129
/**

src/Logs/LogsAggregator.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Sentry\State\HubInterface;
1313
use Sentry\State\Scope;
1414
use Sentry\Util\Arr;
15+
use Sentry\Util\Str;
1516

1617
/**
1718
* @internal
@@ -57,7 +58,21 @@ public function add(
5758
return;
5859
}
5960

60-
$log = (new Log($timestamp, $this->getTraceId($hub), $level, vsprintf($message, $values)))
61+
$formattedMessage = Str::vsprintfOrNull($message, $values);
62+
63+
if ($formattedMessage === null) {
64+
// If formatting fails we don't format the message and log the error
65+
if ($sdkLogger !== null) {
66+
$sdkLogger->warning('Failed to format log message with values.', [
67+
'message' => $message,
68+
'values' => $values,
69+
]);
70+
}
71+
72+
$formattedMessage = $message;
73+
}
74+
75+
$log = (new Log($timestamp, $this->getTraceId($hub), $level, $formattedMessage))
6176
->setAttribute('sentry.release', $options->getRelease())
6277
->setAttribute('sentry.environment', $options->getEnvironment() ?? Event::DEFAULT_ENVIRONMENT)
6378
->setAttribute('sentry.server.address', $options->getServerName())
@@ -124,6 +139,14 @@ public function flush(): ?EventId
124139
return $hub->captureEvent($event);
125140
}
126141

142+
/**
143+
* @return Log[]
144+
*/
145+
public function all(): array
146+
{
147+
return $this->logs;
148+
}
149+
127150
private function getTraceId(HubInterface $hub): string
128151
{
129152
$span = $hub->getSpan();

src/Serializer/EnvelopItems/EventItem.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Sentry\Serializer\Traits\BreadcrumbSeralizerTrait;
1010
use Sentry\Serializer\Traits\StacktraceFrameSeralizerTrait;
1111
use Sentry\Util\JSON;
12+
use Sentry\Util\Str;
1213

1314
/**
1415
* @internal
@@ -124,7 +125,7 @@ public static function toEnvelopeItem(Event $event): string
124125
$payload['message'] = [
125126
'message' => $event->getMessage(),
126127
'params' => $event->getMessageParams(),
127-
'formatted' => $event->getMessageFormatted() ?? vsprintf($event->getMessage(), $event->getMessageParams()),
128+
'formatted' => $event->getMessageFormatted() ?? Str::vsprintfOrNull($event->getMessage(), $event->getMessageParams()) ?? $event->getMessage(),
128129
];
129130
}
130131
}

src/Util/Str.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\Util;
6+
7+
/**
8+
* This class provides some utility methods to work with strings.
9+
*
10+
* @internal
11+
*/
12+
class Str
13+
{
14+
/**
15+
* Safe way of running `vsprintf` without throwing exceptions or errors.
16+
*
17+
* If the string could not be formatted, it returns `null`.
18+
*
19+
* @see https://www.php.net/manual/en/function.vsprintf.php
20+
*
21+
* @param array<bool|float|int|string|null> $values
22+
*/
23+
public static function vsprintfOrNull(string $message, array $values): ?string
24+
{
25+
if (empty($values)) {
26+
return $message;
27+
}
28+
29+
foreach ($values as $value) {
30+
// If the value is not a scalar or null, we cannot safely format it
31+
if (!\is_scalar($value) && $value !== null) {
32+
return null;
33+
}
34+
}
35+
36+
try {
37+
$result = @vsprintf($message, $values);
38+
39+
// @phpstan-ignore-next-line on PHP 7 `vsprintf` does not throw an exception but can return `false`
40+
return $result === false ? null : $result;
41+
} catch (\Error $e) { // This is technically a `ValueError` in PHP 8.0+ but this works in PHP 7 as well
42+
return null;
43+
}
44+
}
45+
}

src/functions.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
* send_attempts?: int,
5959
* send_default_pii?: bool,
6060
* server_name?: string,
61-
* server_name?: string,
6261
* spotlight?: bool,
6362
* spotlight_url?: string,
6463
* strict_trace_propagation?: bool,

0 commit comments

Comments
 (0)