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
23 changes: 21 additions & 2 deletions src/SentrySdk.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,30 @@ public static function setCurrentHub(HubInterface $hub): HubInterface
return $hub;
}

public static function startContext(): void
/**
* Starts an isolated context for the current logical execution.
*
* A provided hub is used as-is, allowing runtimes with their own HubInterface
* implementation to manage hub isolation. When no hub is provided, the SDK
* creates an isolated hub from the baseline.
*
* If a context is already active, this method is a no-op and the provided hub
* is ignored. Use setCurrentHub() to replace the active context's hub.
*
* @param HubInterface|null $hub The hub to use for the new context
*/
public static function startContext(?HubInterface $hub = null): void
{
self::getRuntimeContextManager()->startContext();
self::getRuntimeContextManager()->startContext($hub);
}

/**
* Ends and flushes the active context for the current logical execution.
*
* When no context is active this is a no-op.
*
* @param int|null $timeout The maximum number of seconds to wait while flushing the client transport
*/
public static function endContext(?int $timeout = null): void
{
self::getRuntimeContextManager()->endContext($timeout);
Expand Down
10 changes: 8 additions & 2 deletions src/State/RuntimeContextManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ public function getCurrentContext(): RuntimeContext
/**
* Starts an isolated context for the current logical execution.
*
* A provided hub is used as-is. It is ignored when a context is already active.
*
* @param HubInterface|null $hub The hub to use for the new context
*
* @return bool Whether a new context was started
*/
public function startContext(): bool
public function startContext(?HubInterface $hub = null): bool
{
if ($this->getActiveContext() !== null) {
// Nested start calls for the same logical execution should be a no-op.
Expand All @@ -98,7 +102,7 @@ public function startContext(): bool

ErrorHandler::resetFatalErrorHandlerState();

$this->setActiveContext(new RuntimeContext($this->generateRuntimeContextId(), $this->createHubFromBaseHub()));
$this->setActiveContext(new RuntimeContext($this->generateRuntimeContextId(), $hub ?? $this->createHubFromBaseHub()));

return true;
}
Expand All @@ -107,6 +111,8 @@ public function startContext(): bool
* Ends and flushes the active context for the current logical execution.
*
* When no context is active this is a no-op.
*
* @param int|null $timeout The maximum number of seconds to wait while flushing the client transport
*/
public function endContext(?int $timeout = null): void
{
Expand Down
24 changes: 22 additions & 2 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Sentry\Logs\Logs;
use Sentry\Metrics\Metrics;
use Sentry\Metrics\TraceMetrics;
use Sentry\State\HubInterface;
use Sentry\State\Scope;
use Sentry\Tracing\PropagationContext;
use Sentry\Tracing\SpanContext;
Expand Down Expand Up @@ -222,11 +223,30 @@ function withScope(callable $callback)
return SentrySdk::getCurrentHub()->withScope($callback);
}

function startContext(): void
/**
* Starts an isolated context for the current logical execution.
*
* A provided hub is used as-is, allowing runtimes with their own HubInterface
* implementation to manage hub isolation. When no hub is provided, the SDK
* creates an isolated hub from the baseline.
*
* If a context is already active, this function is a no-op and the provided hub
* is ignored. Use SentrySdk::setCurrentHub() to replace the active context's hub.
*
* @param HubInterface|null $hub The hub to use for the new context
*/
function startContext(?HubInterface $hub = null): void
{
SentrySdk::startContext();
SentrySdk::startContext($hub);
}

/**
* Ends and flushes the active context for the current logical execution.
*
* When no context is active this is a no-op.
*
* @param int|null $timeout The maximum number of seconds to wait while flushing the client transport
*/
function endContext(?int $timeout = null): void
{
SentrySdk::endContext($timeout);
Expand Down
16 changes: 16 additions & 0 deletions tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,22 @@ public function testStartAndEndContext(): void
$this->assertSame($globalHub, SentrySdk::getCurrentHub());
}

public function testStartContextForwardsProvidedHub(): void
{
SentrySdk::init();

$globalHub = SentrySdk::getCurrentHub();
$hub = new Hub();

startContext($hub);

$this->assertSame($hub, SentrySdk::getCurrentHub());

endContext();

$this->assertSame($globalHub, SentrySdk::getCurrentHub());
}

public function testWithContext(): void
{
SentrySdk::init();
Expand Down
42 changes: 42 additions & 0 deletions tests/SentrySdkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,30 @@ public function testStartContextDoesNotInheritBaselineSpan(): void
$this->assertSame($baselineSpan, SentrySdk::getCurrentHub()->getSpan());
}

public function testStartContextUsesProvidedHubAsIs(): void
{
SentrySdk::init();

$globalHub = SentrySdk::getCurrentHub();
$span = new Span(new SpanContext());
$hub = new Hub();
$hub->setSpan($span);
$traceparent = '';
$hub->configureScope(static function (Scope $scope) use (&$traceparent): void {
$traceparent = $scope->getPropagationContext()->toTraceparent();
});

SentrySdk::startContext($hub);

$this->assertSame($hub, SentrySdk::getCurrentHub());
$this->assertSame($span, SentrySdk::getCurrentHub()->getSpan());
$this->assertSame($traceparent, $this->getCurrentScopeTraceparent());

SentrySdk::endContext();

$this->assertSame($globalHub, SentrySdk::getCurrentHub());
}

public function testStartContextCreatesFreshPropagationContext(): void
{
SentrySdk::init();
Expand Down Expand Up @@ -149,6 +173,24 @@ public function testNestedStartContextIsNoOp(): void
$this->assertSame($globalHub, SentrySdk::getCurrentHub());
}

public function testNestedStartContextIgnoresProvidedHub(): void
{
SentrySdk::init();

$globalHub = SentrySdk::getCurrentHub();

SentrySdk::startContext();
$contextHub = SentrySdk::getCurrentHub();

SentrySdk::startContext(new Hub());

$this->assertSame($contextHub, SentrySdk::getCurrentHub());

SentrySdk::endContext();

$this->assertSame($globalHub, SentrySdk::getCurrentHub());
}

public function testRuntimeContextStorageIsolatesConcurrentExecutions(): void
{
$storage = new StubRuntimeContextStorage();
Expand Down
Loading