'warning', 'days_remaining' => $daysRemaining, ]); } public static function recordGalleryExpired(): void { self::increment('gallery', 'expired', ['segment' => 'expired']); } public static function recordTenantPackageWarning(int $daysRemaining): void { $label = sprintf('warning_day_%d', $daysRemaining); self::increment('tenant_package', $label, [ 'segment' => 'warning', 'days_remaining' => $daysRemaining, ]); } public static function recordTenantPackageExpired(): void { self::increment('tenant_package', 'expired', ['segment' => 'expired']); } public static function recordCreditWarning(int $threshold, int $balance): void { $label = sprintf('threshold_%d', $threshold); self::increment('tenant_credit', $label, [ 'segment' => 'warning', 'threshold' => $threshold, 'balance' => $balance, ]); } public static function recordCreditRecovery(int $balance): void { self::increment('tenant_credit', 'recovered', [ 'segment' => 'recovered', 'balance' => $balance, ]); } public static function snapshot(): array { $index = Cache::get(self::INDEX_KEY, []); $result = []; foreach ($index as $cacheKey => $meta) { $metric = Arr::get($meta, 'metric', 'unknown'); $label = Arr::get($meta, 'label', 'unknown'); $value = (int) Cache::get($cacheKey, 0); if (! isset($result[$metric])) { $result[$metric] = []; } $result[$metric][$label] = $value; } ksort($result); foreach ($result as &$labels) { ksort($labels); } return $result; } public static function reset(): void { $index = Cache::pull(self::INDEX_KEY, []); foreach (array_keys($index) as $cacheKey) { Cache::forget($cacheKey); } } private static function increment(string $metric, string $label, array $context = []): void { $cacheKey = self::buildCacheKey($metric, $label); if (! Cache::has($cacheKey)) { Cache::put($cacheKey, 0, now()->addMinutes(self::TTL_MINUTES)); } Cache::increment($cacheKey); Cache::put($cacheKey, Cache::get($cacheKey), now()->addMinutes(self::TTL_MINUTES)); self::rememberIndex($cacheKey, $metric, $label); Log::info('package_limit_metric', array_merge([ 'metric' => $metric, 'label' => $label, 'value' => Cache::get($cacheKey, 0), ], $context)); } private static function buildCacheKey(string $metric, string $label): string { return sprintf('%s:%s:%s', self::CACHE_PREFIX, $metric, $label); } private static function rememberIndex(string $cacheKey, string $metric, string $label): void { $index = Cache::get(self::INDEX_KEY, []); $index[$cacheKey] = [ 'metric' => $metric, 'label' => $label, ]; Cache::put(self::INDEX_KEY, $index, now()->addMinutes(self::TTL_MINUTES)); } }