<?php
declare(strict_types=1);
namespace App\EventsSubscriber\System;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
final class EnvStartupLogSubscriber implements EventSubscriberInterface
{
private const SENSITIVE_KEY_PATTERN = '/SECRET|PASSWORD|TOKEN|KEY|DSN|DATABASE_URL|PRIVATE|CREDENTIAL|AUTH|WEBHOOK|JWT/i';
private bool $wasLogged = false;
public function __construct(
private readonly LoggerInterface $logger,
) {
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => [
'onKernelRequest',
2048,
],
];
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest() || $this->wasLogged) {
return;
}
$this->wasLogged = true;
$this->logger->info('Application startup environment variables', [
'env_vars' => $this->collectEnvironmentVariables(),
]);
}
/**
* @return array<string, string>
*/
private function collectEnvironmentVariables(): array
{
$values = getenv();
if (!is_array($values)) {
return [];
}
ksort($values);
$result = [];
foreach ($values as $key => $value) {
if (!is_string($key)) {
continue;
}
$normalizedValue = is_scalar($value) ? (string) $value : '[non-scalar]';
$result[$key] = $this->maskIfSensitive($key, $normalizedValue);
}
return $result;
}
private function maskIfSensitive(string $key, string $value): string
{
if (preg_match(self::SENSITIVE_KEY_PATTERN, $key) === 1) {
return '[masked]';
}
if (strlen($value) > 200) {
return sprintf('%s...[truncated]', substr($value, 0, 200));
}
return $value;
}
}