Skip to content
Closed
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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"psy/psysh": "^0.12.22",
"sentry/sentry": "^4.27",
"sentry/sentry": "dev-master",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"spomky-labs/otphp": "^11.0",
"symfony/console": "^8.1",
"symfony/dom-crawler": "^8.1",
Expand Down
263 changes: 263 additions & 0 deletions docs/plans/2026-09-04-0500-sentry-runtime-context-integration.md

Large diffs are not rendered by default.

26 changes: 18 additions & 8 deletions src/docs/sentry.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

[Sentry](https://sentry.io) provides error tracking and performance monitoring for your Hypervel application. Hypervel's Sentry integration captures exceptions, logs, requests, database queries, cache operations, queued jobs, notifications, Redis commands, scheduled tasks, and filesystem operations.

The integration is designed for Hypervel's long-running Swoole workers. Request state is isolated between coroutines, while Sentry's HTTP connections are pooled and reused across requests.
The integration is designed for Hypervel's long-running Swoole workers. Sentry state is isolated across requests, queued jobs, scheduled tasks, and WebSocket callbacks, while HTTP connections are pooled and reused across executions. An execution remains active until any application child coroutines it starts have finished.

<a name="installation"></a>
## Installation
Expand Down Expand Up @@ -142,10 +142,11 @@ Log records sent through this channel are converted into Sentry events. Exceptio
<a name="sentry-logs"></a>
### Sentry Logs

> [!WARNING]
> Sentry Logs are currently unsupported in Hypervel. The Sentry SDK shares its buffered log records across application executions, so one execution may flush records collected by another. Keep `SENTRY_ENABLE_LOGS` disabled. The regular `sentry` event channel remains fully supported.
The `sentry_logs` channel sends structured records to Sentry Logs. Records are isolated between concurrent requests, queued jobs, scheduled tasks, and WebSocket callbacks, then flushed when that execution finishes.

The `sentry_logs` channel and its configuration remain available so applications can adopt Sentry Logs when the SDK provides isolated runtime contexts. The channel uses `SENTRY_LOG_LEVEL` and falls back directly to your application's `LOG_LEVEL` value. The upstream `SENTRY_LOGS_LEVEL` compatibility alias is not supported.
The channel uses `SENTRY_LOG_LEVEL` and falls back directly to your application's `LOG_LEVEL` value. The upstream `SENTRY_LOGS_LEVEL` compatibility alias is not supported.

The `SENTRY_ENABLE_LOGS` option is kept for compatibility but is deprecated and no longer turns logs on or off. To disable Sentry Logs, configure `before_send_log` to return `null`.

<a name="performance-monitoring"></a>
## Performance Monitoring
Expand Down Expand Up @@ -209,10 +210,19 @@ This middleware can downsample a transaction that was already sampled by your gl
<a name="metrics"></a>
### Metrics

> [!WARNING]
> Sentry trace metrics are currently unsupported in Hypervel. The Sentry SDK shares its metric aggregators across application executions, so one execution may flush metrics collected by another. This does not affect transaction tracing or spans.
You may record trace metrics using Sentry's `traceMetrics` function:

```php
use function Sentry\traceMetrics;

traceMetrics()->count('orders.processed', 1, [
'region' => 'us-east',
]);
```

Metrics are isolated between concurrent requests, queued jobs, scheduled tasks, and WebSocket callbacks, then flushed when that execution finishes.

Trace metrics are disabled by default. The `SENTRY_ENABLE_METRICS` option remains available, but enabling it is unsupported until the SDK can isolate metric aggregators between application executions.
The `SENTRY_ENABLE_METRICS` option is kept for compatibility but is deprecated and no longer turns metrics on or off. To disable trace metrics, configure `before_send_metric` to return `null`.

<a name="scheduled-tasks"></a>
### Scheduled Tasks
Expand Down Expand Up @@ -308,7 +318,7 @@ Spotlight may be used without configuring a Sentry DSN.
<a name="delivery-and-shutdown"></a>
## Delivery and Shutdown

Sentry events are sent from detached coroutines using a bounded pool of reusable HTTP transports. Normal requests and queued jobs do not wait for event delivery, and commands use the same non-blocking delivery by default. Sends started outside a coroutine complete before returning so short-lived CLI processes cannot exit while an accepted send is still running. If the pool is exhausted during an exception storm, new telemetry is dropped instead of delaying application work.
Buffered logs and metrics are flushed when their execution finishes. Sentry envelopes are then sent from detached coroutines using a bounded pool of reusable HTTP transports. Requests, queued jobs, scheduled tasks, and WebSocket callbacks do not wait for event delivery, and commands use the same non-blocking delivery by default. Sends started outside a coroutine complete before returning so short-lived CLI processes cannot exit while an accepted send is still running. If the pool is exhausted during an exception storm, new telemetry is dropped instead of delaying application work.

Graceful worker shutdown performs a bounded drain after the worker-exit coordinator is released, then closes the transport pool. Delivery during a worker exit is best effort because Swoole may terminate outstanding reactor work after its shutdown deadline.

Expand Down
9 changes: 6 additions & 3 deletions src/docs/websockets.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,12 @@ Route::get('/ws/chat', ChatSocket::class)

Hypervel dispatches the following events for custom WebSocket connections:

- `Hypervel\WebSocketServer\Events\ConnectionOpened` provides the file descriptor, native request, and server name.
- `Hypervel\WebSocketServer\Events\MessageReceived` provides the file descriptor, native frame, and server name.
- `Hypervel\WebSocketServer\Events\ConnectionClosed` provides the file descriptor, reactor ID, and server name.
- `Hypervel\WebSocketServer\Events\ConnectionOpening` is dispatched before handshake validation and routing. It provides the file descriptor, bridged HTTP request, and server name.
- `Hypervel\WebSocketServer\Events\ConnectionOpened` is dispatched after a successful handshake and before the handler's `onOpen` method. It provides the file descriptor, native request, and server name.
- `Hypervel\WebSocketServer\Events\MessageReceived` is dispatched before the handler's `onMessage` method. It provides the file descriptor, native frame, and server name.
- `Hypervel\WebSocketServer\Events\MessageHandled` is dispatched after the handler's `onMessage` method. It provides the file descriptor, native frame, server name, and any exception raised while handling the message.
- `Hypervel\WebSocketServer\Events\ConnectionClosing` is dispatched before the handler's `onClose` method. It provides the file descriptor, reactor ID, and server name.
- `Hypervel\WebSocketServer\Events\ConnectionClosed` is dispatched after the handler's `onClose` method. It provides the file descriptor, reactor ID, and server name.

You may listen for these events using Hypervel's normal [event listeners](/docs/{{version}}/events#registering-events-and-listeners).

Expand Down
2 changes: 1 addition & 1 deletion src/sentry/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"nyholm/psr7": "^1.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"sentry/sentry": "^4.27",
"sentry/sentry": "dev-master",
Comment thread
binaryfire marked this conversation as resolved.
"symfony/console": "^8.1",
"symfony/http-foundation": "^8.1",
"symfony/psr-http-message-bridge": "^8.1"
Expand Down
9 changes: 2 additions & 7 deletions src/sentry/config/sentry.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,16 @@
// Only continue incoming traces when the organization IDs are compatible with this SDK instance.
'strict_trace_continuation' => (bool) env('SENTRY_STRICT_TRACE_CONTINUATION', false),

// Sentry Logs are currently unsupported because the SDK buffers them across executions.
// See https://hypervel.org/docs/sentry#sentry-logs before enabling this option.
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_logs
'enable_logs' => (bool) env('SENTRY_ENABLE_LOGS', false),

// Trace metrics are currently unsupported because the SDK aggregates them across executions.
// See https://hypervel.org/docs/sentry#metrics before enabling this option.
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_metrics
'enable_metrics' => (bool) env('SENTRY_ENABLE_METRICS', false),
'enable_metrics' => (bool) env('SENTRY_ENABLE_METRICS', true),
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// This option affects the currently unsupported Sentry Logs feature only.
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#log_flush_threshold
'log_flush_threshold' => $logFlushThreshold === null ? null : (int) $logFlushThreshold,

// The minimum log level for the currently unsupported `sentry_logs` logging channel.
// The minimum log level sent to Sentry through the `sentry_logs` logging channel.
'logs_channel_level' => env('SENTRY_LOG_LEVEL', env('LOG_LEVEL', 'debug')),

// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send_default_pii
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Hypervel\Context\CoroutineContext;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Sentry\Integration;
use Sentry\SentrySdk;
use Sentry\State\Scope;
use Sentry\Tracing\Span;
Expand Down Expand Up @@ -99,7 +98,6 @@ protected function maybePopScope(): void
return;
}

Integration::flushEvents();
SentrySdk::getCurrentHub()->popScope();

CoroutineContext::set($this->contextKey('scope_count'), $count - 1);
Expand Down
10 changes: 2 additions & 8 deletions src/sentry/src/Features/ConsoleSchedulingFeature.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Hypervel\Console\Scheduling\Event as SchedulingEvent;
use Hypervel\Log\Context\Repository as ContextRepository;
use Hypervel\Sentry\Features\Concerns\TracksPushedScopesAndSpans;
use Hypervel\Sentry\Integration;
use Hypervel\Support\Str;
use RuntimeException;
use Sentry\CheckIn;
Expand Down Expand Up @@ -163,20 +162,15 @@ public function handleScheduledTaskFinished(ScheduledTaskFinished $event): void
? SpanStatus::ok()
: SpanStatus::internalError();

// Only the terminal event that owns the tracked span flushes buffered logs and trace metrics.
if ($this->maybeFinishSpan($status) !== null) {
Integration::flushEvents();
}
$this->maybeFinishSpan($status);
}

/**
* Mark tracing for the scheduled task as failed.
*/
public function handleScheduledTaskFailed(): void
{
if ($this->maybeFinishSpan(SpanStatus::internalError()) !== null) {
Integration::flushEvents();
}
$this->maybeFinishSpan(SpanStatus::internalError());
}

private function startCheckIn(
Expand Down
2 changes: 0 additions & 2 deletions src/sentry/src/Features/QueueFeature.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,6 @@ public function handleWorkerStoppingQueueEvent(WorkerStopping $event): void
public function handleJobExceptionOccurredQueueEvent(JobExceptionOccurred $event): void
{
$this->maybeFinishSpan(SpanStatus::internalError());

Integration::flushEvents();
}

private function normalizeQueueName(?string $queue): string
Expand Down
18 changes: 13 additions & 5 deletions src/sentry/src/Http/FlushEventsMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@
namespace Hypervel\Sentry\Http;

use Closure;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Http\Request;
use Hypervel\Sentry\Integration;
use Hypervel\Sentry\State\RuntimeContextBoundary;
use Symfony\Component\HttpFoundation\Response;

/**
* Open the outer request context whose deferred end flushes buffered telemetry.
*/
class FlushEventsMiddleware
{
/**
* Create a Sentry runtime context middleware.
*/
public function __construct(
protected RuntimeContextBoundary $runtimeContextBoundary,
) {
}

/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
Coroutine::defer(static function (): void {
Integration::flushEvents();
});
$this->runtimeContextBoundary->start();

return $next($request);
}
Expand Down
43 changes: 11 additions & 32 deletions src/sentry/src/Integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
use Sentry\EventId;
use Sentry\ExceptionMechanism;
use Sentry\Integration\IntegrationInterface;
use Sentry\Logs\Logs;
use Sentry\Metrics\TraceMetrics;
use Sentry\SentrySdk;
use Sentry\State\Scope;
use Sentry\Tracing\TransactionSource;
Expand Down Expand Up @@ -114,20 +112,22 @@ public static function setTransaction(?string $transaction): void
CoroutineContext::set(self::CONTEXT_TRANSACTION_KEY, $transaction);
}

/**
* Flush buffered events without waiting for delivery.
*/
public static function flushEvents(): void
{
self::flush(null, false);
}

/**
* Flush buffered events and wait for the captured delivery generation.
*/
public static function drainEvents(?int $timeout = null): Result
{
return self::flush($timeout, true);
SentrySdk::flush();

$client = SentrySdk::getCurrentHub()->getClient();

if ($client === null) {
return new Result(ResultStatus::success());
}

$timeout = max(1, $timeout ?? (int) ceil($client->getOptions()->getHttpTimeout()));

return $client->flush($timeout);
}

/**
Expand Down Expand Up @@ -222,27 +222,6 @@ private static function escapeMetaTagContent(string $value): string
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}

/**
* Flush buffered SDK telemetry before flushing its client transport.
*/
private static function flush(?int $timeout, bool $drain): Result
{
$client = SentrySdk::getCurrentHub()->getClient();

if ($client === null) {
return new Result(ResultStatus::success());
}

if ($drain) {
$timeout = max(1, $timeout ?? (int) ceil($client->getOptions()->getHttpTimeout()));
}

Logs::getInstance()->flush();
TraceMetrics::getInstance()->flush();

return $client->flush($timeout);
}

/**
* Try to make an educated guess if the call came from the `report` helper.
*
Expand Down
Loading