Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace Symfony\Component\HttpKernel\Log; Chris@14: Chris@14: use Psr\Log\AbstractLogger; Chris@14: use Psr\Log\InvalidArgumentException; Chris@14: use Psr\Log\LogLevel; Chris@14: Chris@14: /** Chris@14: * Minimalist PSR-3 logger designed to write in stderr or any other stream. Chris@14: * Chris@14: * @author Kévin Dunglas Chris@14: */ Chris@14: class Logger extends AbstractLogger Chris@14: { Chris@17: private static $levels = [ Chris@14: LogLevel::DEBUG => 0, Chris@14: LogLevel::INFO => 1, Chris@14: LogLevel::NOTICE => 2, Chris@14: LogLevel::WARNING => 3, Chris@14: LogLevel::ERROR => 4, Chris@14: LogLevel::CRITICAL => 5, Chris@14: LogLevel::ALERT => 6, Chris@14: LogLevel::EMERGENCY => 7, Chris@17: ]; Chris@14: Chris@14: private $minLevelIndex; Chris@14: private $formatter; Chris@14: private $handle; Chris@14: Chris@14: public function __construct($minLevel = null, $output = 'php://stderr', callable $formatter = null) Chris@14: { Chris@14: if (null === $minLevel) { Chris@18: $minLevel = 'php://stdout' === $output || 'php://stderr' === $output ? LogLevel::CRITICAL : LogLevel::WARNING; Chris@14: Chris@14: if (isset($_ENV['SHELL_VERBOSITY']) || isset($_SERVER['SHELL_VERBOSITY'])) { Chris@14: switch ((int) (isset($_ENV['SHELL_VERBOSITY']) ? $_ENV['SHELL_VERBOSITY'] : $_SERVER['SHELL_VERBOSITY'])) { Chris@14: case -1: $minLevel = LogLevel::ERROR; break; Chris@14: case 1: $minLevel = LogLevel::NOTICE; break; Chris@14: case 2: $minLevel = LogLevel::INFO; break; Chris@14: case 3: $minLevel = LogLevel::DEBUG; break; Chris@14: } Chris@14: } Chris@14: } Chris@14: Chris@14: if (!isset(self::$levels[$minLevel])) { Chris@14: throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $minLevel)); Chris@14: } Chris@14: Chris@14: $this->minLevelIndex = self::$levels[$minLevel]; Chris@17: $this->formatter = $formatter ?: [$this, 'format']; Chris@17: if (false === $this->handle = \is_resource($output) ? $output : @fopen($output, 'a')) { Chris@14: throw new InvalidArgumentException(sprintf('Unable to open "%s".', $output)); Chris@14: } Chris@14: } Chris@14: Chris@14: /** Chris@14: * {@inheritdoc} Chris@14: */ Chris@17: public function log($level, $message, array $context = []) Chris@14: { Chris@14: if (!isset(self::$levels[$level])) { Chris@14: throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); Chris@14: } Chris@14: Chris@14: if (self::$levels[$level] < $this->minLevelIndex) { Chris@14: return; Chris@14: } Chris@14: Chris@14: $formatter = $this->formatter; Chris@14: fwrite($this->handle, $formatter($level, $message, $context)); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param string $level Chris@14: * @param string $message Chris@14: * @param array $context Chris@14: * Chris@14: * @return string Chris@14: */ Chris@14: private function format($level, $message, array $context) Chris@14: { Chris@14: if (false !== strpos($message, '{')) { Chris@17: $replacements = []; Chris@14: foreach ($context as $key => $val) { Chris@14: if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) { Chris@14: $replacements["{{$key}}"] = $val; Chris@14: } elseif ($val instanceof \DateTimeInterface) { Chris@14: $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339); Chris@14: } elseif (\is_object($val)) { Chris@14: $replacements["{{$key}}"] = '[object '.\get_class($val).']'; Chris@14: } else { Chris@14: $replacements["{{$key}}"] = '['.\gettype($val).']'; Chris@14: } Chris@14: } Chris@14: Chris@14: $message = strtr($message, $replacements); Chris@14: } Chris@14: Chris@14: return sprintf('%s [%s] %s', date(\DateTime::RFC3339), $level, $message).\PHP_EOL; Chris@14: } Chris@14: }