Logger.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpKernel\Log;
  11. use Psr\Log\AbstractLogger;
  12. use Psr\Log\InvalidArgumentException;
  13. use Psr\Log\LogLevel;
  14. /**
  15. * Minimalist PSR-3 logger designed to write in stderr or any other stream.
  16. *
  17. * @author Kévin Dunglas <dunglas@gmail.com>
  18. */
  19. class Logger extends AbstractLogger
  20. {
  21. private static $levels = [
  22. LogLevel::DEBUG => 0,
  23. LogLevel::INFO => 1,
  24. LogLevel::NOTICE => 2,
  25. LogLevel::WARNING => 3,
  26. LogLevel::ERROR => 4,
  27. LogLevel::CRITICAL => 5,
  28. LogLevel::ALERT => 6,
  29. LogLevel::EMERGENCY => 7,
  30. ];
  31. private $minLevelIndex;
  32. private $formatter;
  33. private $handle;
  34. public function __construct(string $minLevel = null, $output = 'php://stderr', callable $formatter = null)
  35. {
  36. if (null === $minLevel) {
  37. $minLevel = LogLevel::WARNING;
  38. if (isset($_ENV['SHELL_VERBOSITY']) || isset($_SERVER['SHELL_VERBOSITY'])) {
  39. switch ((int) (isset($_ENV['SHELL_VERBOSITY']) ? $_ENV['SHELL_VERBOSITY'] : $_SERVER['SHELL_VERBOSITY'])) {
  40. case -1: $minLevel = LogLevel::ERROR; break;
  41. case 1: $minLevel = LogLevel::NOTICE; break;
  42. case 2: $minLevel = LogLevel::INFO; break;
  43. case 3: $minLevel = LogLevel::DEBUG; break;
  44. }
  45. }
  46. }
  47. if (!isset(self::$levels[$minLevel])) {
  48. throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $minLevel));
  49. }
  50. $this->minLevelIndex = self::$levels[$minLevel];
  51. $this->formatter = $formatter ?: [$this, 'format'];
  52. if (false === $this->handle = \is_resource($output) ? $output : @fopen($output, 'a')) {
  53. throw new InvalidArgumentException(sprintf('Unable to open "%s".', $output));
  54. }
  55. }
  56. /**
  57. * {@inheritdoc}
  58. */
  59. public function log($level, $message, array $context = [])
  60. {
  61. if (!isset(self::$levels[$level])) {
  62. throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
  63. }
  64. if (self::$levels[$level] < $this->minLevelIndex) {
  65. return;
  66. }
  67. $formatter = $this->formatter;
  68. fwrite($this->handle, $formatter($level, $message, $context));
  69. }
  70. private function format(string $level, string $message, array $context): string
  71. {
  72. if (false !== strpos($message, '{')) {
  73. $replacements = [];
  74. foreach ($context as $key => $val) {
  75. if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
  76. $replacements["{{$key}}"] = $val;
  77. } elseif ($val instanceof \DateTimeInterface) {
  78. $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
  79. } elseif (\is_object($val)) {
  80. $replacements["{{$key}}"] = '[object '.\get_class($val).']';
  81. } else {
  82. $replacements["{{$key}}"] = '['.\gettype($val).']';
  83. }
  84. }
  85. $message = strtr($message, $replacements);
  86. }
  87. return sprintf('%s [%s] %s', date(\DateTime::RFC3339), $level, $message).\PHP_EOL;
  88. }
  89. }