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
32 changes: 32 additions & 0 deletions src/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,36 @@ public function setProfilesSampleRate(?float $sampleRate): self
return $this;
}

/**
* Gets a callback that will be invoked when we sample a profile.
*
* @phpstan-return null|callable(Tracing\SamplingContext): float
*/
public function getProfilesSampler(): ?callable
{
/** @var callable(Tracing\SamplingContext): float|null $value */
$value = $this->options['profiles_sampler'];

return $value;
}

/**
* Sets a callback that will be invoked when we take the profiling sampling decision.
* Return a number between 0 and 1 to define the sample rate for the provided SamplingContext.
*
* @param ?callable $sampler The sampler
*
* @phpstan-param null|callable(Tracing\SamplingContext): float $sampler
*/
public function setProfilesSampler(?callable $sampler): self
{
$options = array_merge($this->options, ['profiles_sampler' => $sampler]);

$this->options = $this->resolver->resolve($options);

return $this;
}

/**
* Gets whether tracing is enabled or not. The feature is enabled when at
* least one of the `traces_sample_rate` and `traces_sampler` options is
Expand Down Expand Up @@ -1395,6 +1425,7 @@ private function configureOptions(OptionsResolver $resolver): void
'traces_sample_rate' => null,
'traces_sampler' => null,
'profiles_sample_rate' => null,
'profiles_sampler' => null,
'attach_stacktrace' => false,
/**
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
Expand Down Expand Up @@ -1474,6 +1505,7 @@ private function configureOptions(OptionsResolver $resolver): void
$resolver->setAllowedTypes('traces_sample_rate', ['null', 'int', 'float']);
$resolver->setAllowedTypes('traces_sampler', ['null', 'callable']);
$resolver->setAllowedTypes('profiles_sample_rate', ['null', 'int', 'float']);
$resolver->setAllowedTypes('profiles_sampler', ['null', 'callable']);
$resolver->setAllowedTypes('attach_stacktrace', 'bool');
$resolver->setAllowedTypes('attach_metric_code_locations', 'bool');
$resolver->setAllowedTypes('context_lines', ['null', 'int']);
Expand Down
15 changes: 13 additions & 2 deletions src/State/Hub.php
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,20 @@ public function startTransaction(TransactionContext $context, array $customSampl

$transaction->initSpanRecorder();

$profilesSampleRate = $options->getProfilesSampleRate();
$profilesSampleSource = 'config:profiles_sample_rate';
$profilesSampler = $options->getProfilesSampler();

if ($profilesSampler !== null) {
$profilesSampleRate = $profilesSampler($samplingContext);
$profilesSampleSource = 'config:profiles_sampler';
} else {
$profilesSampleRate = $options->getProfilesSampleRate();
}

if ($profilesSampleRate === null) {
$logger->info(\sprintf('Transaction [%s] is not profiling because `profiles_sample_rate` option is not set.', (string) $transaction->getTraceId()));
$logger->info(\sprintf('Transaction [%s] is not profiling because neither `profiles_sample_rate` nor `profiles_sampler` option is set.', (string) $transaction->getTraceId()));
} elseif (!$this->isValidSampleRate($profilesSampleRate)) {
$logger->warning(\sprintf('Transaction [%s] is not profiling because profile sample rate (decided by %s) is invalid.', (string) $transaction->getTraceId(), $profilesSampleSource));
} elseif ($this->sample($profilesSampleRate)) {
$logger->info(\sprintf('Transaction [%s] started profiling because it was sampled.', (string) $transaction->getTraceId()));

Expand Down
1 change: 1 addition & 0 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* org_id?: int|null,
* prefixes?: array<string>,
* profiles_sample_rate?: int|float|null,
* profiles_sampler?: callable|null,
* release?: string|null,
* sample_rate?: float|int,
* send_attempts?: int,
Expand Down
7 changes: 7 additions & 0 deletions tests/OptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ static function (): void {},
'setProfilesSampleRate',
];

yield [
'profiles_sampler',
static function (): void {},
'getProfilesSampler',
'setProfilesSampler',
];

yield [
'attach_stacktrace',
false,
Expand Down
121 changes: 121 additions & 0 deletions tests/State/HubTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,127 @@ public function testStartTransactionWithCustomSamplingContext(): void
$hub->startTransaction(new TransactionContext(), $customSamplingContext);
}

public function testStartTransactionStartsProfilerWithProfilesSampler(): void
{
$client = $this->createMock(ClientInterface::class);
$client->expects($this->exactly(2))
->method('getOptions')
->willReturn(new Options([
'traces_sample_rate' => 1.0,
'profiles_sampler' => static function (): float {
return 1.0;
},
]));

$hub = new Hub($client);
$transaction = $hub->startTransaction(new TransactionContext());

$this->assertTrue($transaction->getSampled());
$this->assertNotNull($transaction->getProfiler());
}

public function testStartTransactionDoesNotStartProfilerWhenProfilesSamplerReturnsZero(): void
{
$client = $this->createMock(ClientInterface::class);
$client->expects($this->once())
->method('getOptions')
->willReturn(new Options([
'traces_sample_rate' => 1.0,
'profiles_sampler' => static function (): float {
return 0.0;
},
]));

$hub = new Hub($client);
$transaction = $hub->startTransaction(new TransactionContext());

$this->assertTrue($transaction->getSampled());
$this->assertNull($transaction->getProfiler());
}

public function testStartTransactionPrefersProfilesSamplerOverProfilesSampleRate(): void
{
$client = $this->createMock(ClientInterface::class);
$client->expects($this->once())
->method('getOptions')
->willReturn(new Options([
'traces_sample_rate' => 1.0,
'profiles_sample_rate' => 1.0,
'profiles_sampler' => static function (): float {
return 0.0;
},
]));

$hub = new Hub($client);
$transaction = $hub->startTransaction(new TransactionContext());

$this->assertTrue($transaction->getSampled());
$this->assertNull($transaction->getProfiler());
}

public function testStartTransactionWithProfilesSamplerReceivesCustomSamplingContext(): void
{
$customSamplingContext = ['a' => 'b'];

$client = $this->createMock(ClientInterface::class);
$client->expects($this->once())
->method('getOptions')
->willReturn(new Options([
'traces_sample_rate' => 1.0,
'profiles_sampler' => function (SamplingContext $samplingContext) use ($customSamplingContext): float {
$this->assertSame($samplingContext->getAdditionalContext(), $customSamplingContext);

return 0.0;
},
]));

$hub = new Hub($client);
$hub->startTransaction(new TransactionContext(), $customSamplingContext);
}

public function testStartTransactionDoesNotStartProfilerWhenProfilesSamplerReturnsInvalidValue(): void
{
$client = $this->createMock(ClientInterface::class);
$client->expects($this->once())
->method('getOptions')
->willReturn(new Options([
'traces_sample_rate' => 1.0,
'profiles_sampler' => static function (): string {
return 'foo';
},
]));

$hub = new Hub($client);
$transaction = $hub->startTransaction(new TransactionContext());

$this->assertTrue($transaction->getSampled());
$this->assertNull($transaction->getProfiler());
}

public function testStartTransactionDoesNotCallProfilesSamplerWhenTransactionIsNotSampled(): void
{
$profilesSamplerInvoked = false;

$client = $this->createMock(ClientInterface::class);
$client->expects($this->once())
->method('getOptions')
->willReturn(new Options([
'traces_sample_rate' => 0.0,
'profiles_sampler' => static function () use (&$profilesSamplerInvoked): float {
$profilesSamplerInvoked = true;

return 1.0;
},
]));

$hub = new Hub($client);
$transaction = $hub->startTransaction(new TransactionContext());

$this->assertFalse($transaction->getSampled());
$this->assertFalse($profilesSamplerInvoked);
$this->assertNull($transaction->getProfiler());
}

public function testStartTransactionUpdatesTheDscSampleRate(): void
{
$client = $this->createMock(ClientInterface::class);
Expand Down
Loading