Repository navigation
feat(profiling): allow profiling before the SDK inits #2280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,9 @@ | |
|
|
||
| namespace Sentry\Profiling; | ||
|
|
||
| use Sentry\ClientInterface; | ||
| use Sentry\Event; | ||
| use Sentry\Options; | ||
| use Sentry\SentrySdk; | ||
| use Sentry\Util\SentryUid; | ||
| use Sentry\Util\TelemetryStorage; | ||
|
|
@@ -72,7 +74,7 @@ final class ContinuousProfiler | |
| private $nextFlushTimestamp = 0.0; | ||
|
|
||
| /** | ||
| * @var bool|null | ||
| * @var bool|null Whether the profiling session is sampled, undecided while a profiler started before a client was bound | ||
| */ | ||
| private $sampled; | ||
|
|
||
|
|
@@ -112,6 +114,11 @@ public function __construct(int $maxSamplesPerChunk = self::MAX_SAMPLES_PER_CHUN | |
| $this->pendingChunks = $pendingChunks; | ||
| } | ||
|
|
||
| /** | ||
| * The profiler is started even if the SDK is not initialized yet. Once init is called, it will keep or discard | ||
| * the samples, based on the configured options. Only for manual profiling, `trace` will start with the | ||
| * transaction anyway. | ||
| */ | ||
| public function start(): void | ||
| { | ||
| $this->restartInForkedProcess(); | ||
|
|
@@ -121,29 +128,16 @@ public function start(): void | |
| } | ||
|
|
||
| $client = SentrySdk::getCurrentHub()->getClient(); | ||
| if ($client === null) { | ||
| return; | ||
| } | ||
|
|
||
| $options = $client->getOptions(); | ||
| $logger = $options->getLoggerOrNullLogger(); | ||
|
|
||
| if ($options->getProfilesSampleRate() !== null || $options->getProfilesSampler() !== null) { | ||
| $logger->warning('Continuous profiling is disabled because transaction based profiling is configured with the "profiles_sample_rate" or "profiles_sampler" option.'); | ||
|
|
||
| return; | ||
| } | ||
| $options = $client !== null ? $client->getOptions() : null; | ||
|
|
||
| if ($this->sampled === null) { | ||
| $this->sampled = $this->isSessionSampled($options->getProfileSessionSampleRate()); | ||
| } | ||
|
|
||
| if (!$this->sampled) { | ||
| if ($options !== null && !$this->isSessionEnabled($options)) { | ||
| return; | ||
| } | ||
|
|
||
| if (!\extension_loaded('excimer')) { | ||
| $logger->warning('The continuous profiler was started but is not available because the "excimer" extension is not loaded.'); | ||
| if ($options !== null) { | ||
| $options->getLoggerOrNullLogger()->warning('The continuous profiler was started but is not available because the "excimer" extension is not loaded.'); | ||
| } | ||
|
|
||
| return; | ||
| } | ||
|
|
@@ -225,6 +219,27 @@ public function stopForRuntimeContext(string $runtimeContextId): void | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Keeps a profiler started before a client was bound running if it is used manually and the | ||
| * profiling session is sampled, otherwise its samples are discarded. | ||
| */ | ||
| public function onClientBound(ClientInterface $client): void | ||
| { | ||
| if ($this->profiler === null || $this->sampled !== null) { | ||
| return; | ||
| } | ||
|
|
||
| $options = $client->getOptions(); | ||
|
|
||
| if ($options->getProfileLifecycle() === 'manual' && $this->isSessionEnabled($options)) { | ||
| return; | ||
| } | ||
|
|
||
| $this->profiler->stop(); | ||
|
|
||
| $this->clearProfiler(); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Client bind skips forked profiler restartLow Severity
Reviewed by Cursor Bugbot for commit dbecf4e. Configure here. |
||
|
|
||
| public function stop(): void | ||
| { | ||
| $this->restartInForkedProcess(); | ||
|
|
@@ -333,7 +348,8 @@ private function completeChunk(array $entries): void | |
|
|
||
| $sampleCount = \count($entries); | ||
|
|
||
| if ($sampleCount === 0) { | ||
| // Samples collected before a client was bound are only sent once it decided to keep the session | ||
| if ($sampleCount === 0 || $this->sampled === null) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -393,6 +409,24 @@ private function restartInForkedProcess(): void | |
| $this->start(); | ||
| } | ||
|
|
||
| /** | ||
| * Decides whether the profiling session is sampled, unless continuous profiling is disabled by the options. | ||
| */ | ||
| private function isSessionEnabled(Options $options): bool | ||
| { | ||
| if ($options->getProfilesSampleRate() !== null || $options->getProfilesSampler() !== null) { | ||
| $options->getLoggerOrNullLogger()->warning('Continuous profiling is disabled because transaction based profiling is configured with the "profiles_sample_rate" or "profiles_sampler" option.'); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| if ($this->sampled === null) { | ||
| $this->sampled = $this->isSessionSampled($options->getProfileSessionSampleRate()); | ||
| } | ||
|
|
||
| return $this->sampled; | ||
| } | ||
|
|
||
| private function isSessionSampled(?float $sampleRate): bool | ||
| { | ||
| if ($sampleRate === null || $sampleRate <= 0.0) { | ||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.