%PDF- %PDF-
| Direktori : /www/loslex/demo/vendor/sentry/sentry-laravel/src/Sentry/Laravel/Features/ |
| Current File : //www/loslex/demo/vendor/sentry/sentry-laravel/src/Sentry/Laravel/Features/QueueIntegration.php |
<?php
namespace Sentry\Laravel\Features;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Queue\Events\JobExceptionOccurred;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Events\WorkerStopping;
use Illuminate\Queue\Queue;
use Sentry\Breadcrumb;
use Sentry\Laravel\Integration;
use Sentry\SentrySdk;
use Sentry\State\Scope;
use Sentry\Tracing\PropagationContext;
use Sentry\Tracing\Span;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\SpanStatus;
use Sentry\Tracing\TransactionContext;
use Sentry\Tracing\TransactionSource;
use function Sentry\continueTrace;
use function Sentry\getBaggage;
use function Sentry\getTraceparent;
class QueueIntegration extends Feature
{
private const QUEUE_PAYLOAD_BAGGAGE_DATA = 'sentry_baggage_data';
private const QUEUE_PAYLOAD_TRACE_PARENT_DATA = 'sentry_trace_parent_data';
/**
* Hold the number of times the scope was pushed.
*
* @var int
*/
private $pushedScopeCount = 0;
/**
* Hold the stack of parent spans that need to be put back on the scope.
*
* @var array<int, Span|null>
*/
private $parentSpanStack = [];
/**
* Hold the stack of current spans that need to be finished still.
*
* @var array<int, Span|null>
*/
private $currentSpanStack = [];
public function isApplicable(): bool
{
if (!$this->container()->bound('queue')) {
return false;
}
return $this->isBreadcrumbFeatureEnabled('queue_info')
|| $this->isTracingFeatureEnabled('queue_jobs')
|| $this->isTracingFeatureEnabled('queue_job_transactions');
}
public function onBoot(Dispatcher $events): void
{
$events->listen(JobProcessed::class, [$this, 'handleJobProcessedQueueEvent']);
$events->listen(JobProcessing::class, [$this, 'handleJobProcessingQueueEvent']);
$events->listen(WorkerStopping::class, [$this, 'handleWorkerStoppingQueueEvent']);
$events->listen(JobExceptionOccurred::class, [$this, 'handleJobExceptionOccurredQueueEvent']);
if ($this->isTracingFeatureEnabled('queue_jobs') || $this->isTracingFeatureEnabled('queue_job_transactions')) {
Queue::createPayloadUsing(static function (?string $connection, ?string $queue, ?array $payload): ?array {
if ($payload !== null) {
$payload[self::QUEUE_PAYLOAD_BAGGAGE_DATA] = getBaggage();
$payload[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] = getTraceparent();
}
return $payload;
});
}
}
public function handleJobProcessedQueueEvent(JobProcessed $event): void
{
$this->finishJobWithStatus(SpanStatus::ok());
$this->maybePopScope();
}
public function handleJobProcessingQueueEvent(JobProcessing $event): void
{
$this->maybePopScope();
$this->pushScope();
if ($this->isBreadcrumbFeatureEnabled('queue_info')) {
$job = [
'job' => $event->job->getName(),
'queue' => $event->job->getQueue(),
'attempts' => $event->job->attempts(),
'connection' => $event->connectionName,
];
// Resolve name exists only from Laravel 5.3+
if (method_exists($event->job, 'resolveName')) {
$job['resolved'] = $event->job->resolveName();
}
Integration::addBreadcrumb(new Breadcrumb(
Breadcrumb::LEVEL_INFO,
Breadcrumb::TYPE_DEFAULT,
'queue.job',
'Processing queue job',
$job
));
}
$parentSpan = SentrySdk::getCurrentHub()->getSpan();
// If there is no tracing span active and we don't trace jobs as transactions there is no need to handle the event
if ($parentSpan === null && !$this->isTracingFeatureEnabled('queue_job_transactions')) {
return;
}
// If there is a parent span we can record that job as a child unless configured to not do so
if ($parentSpan !== null && !$this->isTracingFeatureEnabled('queue_jobs')) {
return;
}
if ($parentSpan === null) {
$baggage = $event->job->payload()[self::QUEUE_PAYLOAD_BAGGAGE_DATA] ?? null;
$traceParent = $event->job->payload()[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] ?? null;
$context = continueTrace($traceParent ?? '', $baggage ?? '');
// If the parent transaction was not sampled we also stop the queue job from being recorded
if ($context->getParentSampled() === false) {
return;
}
} else {
$context = new SpanContext;
}
$resolvedJobName = $event->job->resolveName();
$job = [
'job' => $event->job->getName(),
'queue' => $event->job->getQueue(),
'resolved' => $resolvedJobName,
'attempts' => $event->job->attempts(),
'connection' => $event->connectionName,
];
if ($context instanceof TransactionContext) {
$context->setName($resolvedJobName);
$context->setSource(TransactionSource::task());
}
$context->setOp('queue.process');
$context->setData($job);
$context->setStartTimestamp(microtime(true));
// When the parent span is null we start a new transaction otherwise we start a child of the current span
if ($parentSpan === null) {
$span = SentrySdk::getCurrentHub()->startTransaction($context);
} else {
$span = $parentSpan->startChild($context);
}
$this->pushSpan($span);
}
public function handleWorkerStoppingQueueEvent(WorkerStopping $event): void
{
Integration::flushEvents();
}
public function handleJobExceptionOccurredQueueEvent(JobExceptionOccurred $event): void
{
$this->finishJobWithStatus(SpanStatus::internalError());
Integration::flushEvents();
}
private function pushSpan(Span $span): void
{
$hub = SentrySdk::getCurrentHub();
$this->parentSpanStack[] = $hub->getSpan();
$hub->setSpan($span);
$this->currentSpanStack[] = $span;
}
private function pushScope(): void
{
SentrySdk::getCurrentHub()->pushScope();
++$this->pushedScopeCount;
// When a job starts, we want to make sure the scope is cleared of breadcrumbs
// as well as setting a new propagation context.
SentrySdk::getCurrentHub()->configureScope(static function (Scope $scope) {
$scope->clearBreadcrumbs();
$scope->setPropagationContext(PropagationContext::fromDefaults());
});
}
private function maybePopSpan(): ?Span
{
if (count($this->currentSpanStack) === 0) {
return null;
}
$parent = array_pop($this->parentSpanStack);
SentrySdk::getCurrentHub()->setSpan($parent);
return array_pop($this->currentSpanStack);
}
private function maybePopScope(): void
{
Integration::flushEvents();
if ($this->pushedScopeCount === 0) {
return;
}
SentrySdk::getCurrentHub()->popScope();
--$this->pushedScopeCount;
}
private function finishJobWithStatus(SpanStatus $status): void
{
$span = $this->maybePopSpan();
if ($span !== null) {
$span->finish();
$span->setStatus($status);
}
}
}