%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/ConsoleIntegration.php |
<?php
namespace Sentry\Laravel\Features;
use DateTimeZone;
use Illuminate\Console\Application as ConsoleApplication;
use Illuminate\Console\Scheduling\Event as SchedulingEvent;
use Illuminate\Contracts\Cache\Factory as Cache;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Str;
use RuntimeException;
use Sentry\CheckIn;
use Sentry\CheckInStatus;
use Sentry\Event as SentryEvent;
use Sentry\MonitorConfig;
use Sentry\MonitorSchedule;
use Sentry\SentrySdk;
class ConsoleIntegration extends Feature
{
/**
* @var array<string, CheckIn> The list of checkins that are currently in progress.
*/
private $checkInStore = [];
/**
* @var Cache The cache repository.
*/
private $cache;
public function isApplicable(): bool
{
return $this->container()->make(Application::class)->runningInConsole();
}
public function onBoot(Cache $cache): void
{
$this->cache = $cache;
$startCheckIn = function (?string $slug, SchedulingEvent $scheduled, ?int $checkInMargin, ?int $maxRuntime, bool $updateMonitorConfig) {
$this->startCheckIn($slug, $scheduled, $checkInMargin, $maxRuntime, $updateMonitorConfig);
};
$finishCheckIn = function (?string $slug, SchedulingEvent $scheduled, CheckInStatus $status) {
$this->finishCheckIn($slug, $scheduled, $status);
};
SchedulingEvent::macro('sentryMonitor', function (
?string $monitorSlug = null,
?int $checkInMargin = null,
?int $maxRuntime = null,
bool $updateMonitorConfig = true
) use ($startCheckIn, $finishCheckIn) {
/** @var SchedulingEvent $this */
if ($monitorSlug === null && $this->command === null) {
throw new RuntimeException('The command string is null, please set a slug manually for this scheduled command using the `sentryMonitor(\'your-monitor-slug\')` macro.');
}
return $this
->before(function () use ($startCheckIn, $monitorSlug, $checkInMargin, $maxRuntime, $updateMonitorConfig) {
/** @var SchedulingEvent $this */
$startCheckIn($monitorSlug, $this, $checkInMargin, $maxRuntime, $updateMonitorConfig);
})
->onSuccess(function () use ($finishCheckIn, $monitorSlug) {
/** @var SchedulingEvent $this */
$finishCheckIn($monitorSlug, $this, CheckInStatus::ok());
})
->onFailure(function () use ($finishCheckIn, $monitorSlug) {
/** @var SchedulingEvent $this */
$finishCheckIn($monitorSlug, $this, CheckInStatus::error());
});
});
}
public function onBootInactive(): void
{
// This is an exact copy of the macro above, but without doing anything so that even when no DSN is configured the user can still use the macro
SchedulingEvent::macro('sentryMonitor', function (
?string $monitorSlug = null,
?int $checkInMargin = null,
?int $maxRuntime = null,
bool $updateMonitorConfig = true
) {
return $this;
});
}
private function startCheckIn(?string $slug, SchedulingEvent $scheduled, ?int $checkInMargin, ?int $maxRuntime, bool $updateMonitorConfig): void
{
$checkInSlug = $slug ?? $this->makeSlugForScheduled($scheduled);
$checkIn = $this->createCheckIn($checkInSlug, CheckInStatus::inProgress());
if ($updateMonitorConfig || $slug === null) {
$timezone = $scheduled->timezone;
if ($timezone instanceof DateTimeZone) {
$timezone = $timezone->getName();
}
$checkIn->setMonitorConfig(new MonitorConfig(
MonitorSchedule::crontab($scheduled->getExpression()),
$checkInMargin,
$maxRuntime,
$timezone
));
}
$cacheKey = $this->buildCacheKey($scheduled->mutexName(), $checkInSlug);
$this->checkInStore[$cacheKey] = $checkIn;
if ($scheduled->runInBackground) {
$this->cache->store()->put($cacheKey, $checkIn->getId(), $scheduled->expiresAt * 60);
}
$this->sendCheckIn($checkIn);
}
private function finishCheckIn(?string $slug, SchedulingEvent $scheduled, CheckInStatus $status): void
{
$mutex = $scheduled->mutexName();
$checkInSlug = $slug ?? $this->makeSlugForScheduled($scheduled);
$cacheKey = $this->buildCacheKey($mutex, $checkInSlug);
$checkIn = $this->checkInStore[$cacheKey] ?? null;
if ($checkIn === null && $scheduled->runInBackground) {
$checkInId = $this->cache->store()->get($cacheKey);
if ($checkInId !== null) {
$checkIn = $this->createCheckIn($checkInSlug, $status, $checkInId);
}
}
// This should never happen (because we should always start before we finish), but better safe than sorry
if ($checkIn === null) {
return;
}
// We don't need to keep the checkIn ID stored since we finished executing the command
unset($this->checkInStore[$mutex]);
if ($scheduled->runInBackground) {
$this->cache->store()->forget($cacheKey);
}
$checkIn->setStatus($status);
$this->sendCheckIn($checkIn);
}
private function sendCheckIn(CheckIn $checkIn): void
{
$event = SentryEvent::createCheckIn();
$event->setCheckIn($checkIn);
SentrySdk::getCurrentHub()->captureEvent($event);
}
private function createCheckIn(string $slug, CheckInStatus $status, string $id = null): CheckIn
{
$options = SentrySdk::getCurrentHub()->getClient()->getOptions();
return new CheckIn(
$slug,
$status,
$id,
$options->getRelease(),
$options->getEnvironment()
);
}
private function buildCacheKey(string $mutex, string $slug): string
{
// We use the mutex name as part of the cache key to avoid collisions between the same commands with the same schedule but with different slugs
return 'sentry:checkIn:' . sha1("{$mutex}:{$slug}");
}
private function makeSlugForScheduled(SchedulingEvent $scheduled): string
{
$generatedSlug = Str::slug(
str_replace(
// `:` is commonly used in the command name, so we replace it with `-` to avoid it being stripped out by the slug function
':',
'-',
trim(
// The command string always starts with the PHP binary, so we remove it since it's not relevant to the slug
Str::after($scheduled->command, ConsoleApplication::phpBinary())
)
)
);
return "scheduled_{$generatedSlug}";
}
}