ServerLogHandler.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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\Bridge\Monolog\Handler;
  11. use Monolog\Formatter\FormatterInterface;
  12. use Monolog\Handler\AbstractProcessingHandler;
  13. use Monolog\Handler\FormattableHandlerTrait;
  14. use Monolog\Logger;
  15. use Symfony\Bridge\Monolog\Formatter\VarDumperFormatter;
  16. if (trait_exists(FormattableHandlerTrait::class)) {
  17. class ServerLogHandler extends AbstractProcessingHandler
  18. {
  19. use ServerLogHandlerTrait;
  20. /**
  21. * {@inheritdoc}
  22. */
  23. protected function getDefaultFormatter(): FormatterInterface
  24. {
  25. return new VarDumperFormatter();
  26. }
  27. }
  28. } else {
  29. class ServerLogHandler extends AbstractProcessingHandler
  30. {
  31. use ServerLogHandlerTrait;
  32. /**
  33. * {@inheritdoc}
  34. */
  35. protected function getDefaultFormatter()
  36. {
  37. return new VarDumperFormatter();
  38. }
  39. }
  40. }
  41. /**
  42. * @author Grégoire Pineau <lyrixx@lyrixx.info>
  43. */
  44. trait ServerLogHandlerTrait
  45. {
  46. private $host;
  47. private $context;
  48. private $socket;
  49. /**
  50. * @param string|int $level The minimum logging level at which this handler will be triggered
  51. */
  52. public function __construct(string $host, $level = Logger::DEBUG, bool $bubble = true, array $context = [])
  53. {
  54. parent::__construct($level, $bubble);
  55. if (false === strpos($host, '://')) {
  56. $host = 'tcp://'.$host;
  57. }
  58. $this->host = $host;
  59. $this->context = stream_context_create($context);
  60. }
  61. /**
  62. * {@inheritdoc}
  63. */
  64. public function handle(array $record): bool
  65. {
  66. if (!$this->isHandling($record)) {
  67. return false;
  68. }
  69. set_error_handler(self::class.'::nullErrorHandler');
  70. try {
  71. if (!$this->socket = $this->socket ?: $this->createSocket()) {
  72. return false === $this->bubble;
  73. }
  74. } finally {
  75. restore_error_handler();
  76. }
  77. return parent::handle($record);
  78. }
  79. protected function write(array $record): void
  80. {
  81. $recordFormatted = $this->formatRecord($record);
  82. set_error_handler(self::class.'::nullErrorHandler');
  83. try {
  84. if (-1 === stream_socket_sendto($this->socket, $recordFormatted)) {
  85. stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR);
  86. // Let's retry: the persistent connection might just be stale
  87. if ($this->socket = $this->createSocket()) {
  88. stream_socket_sendto($this->socket, $recordFormatted);
  89. }
  90. }
  91. } finally {
  92. restore_error_handler();
  93. }
  94. }
  95. /**
  96. * {@inheritdoc}
  97. */
  98. protected function getDefaultFormatter(): FormatterInterface
  99. {
  100. return new VarDumperFormatter();
  101. }
  102. private static function nullErrorHandler()
  103. {
  104. }
  105. private function createSocket()
  106. {
  107. $socket = stream_socket_client($this->host, $errno, $errstr, 0, \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT | \STREAM_CLIENT_PERSISTENT, $this->context);
  108. if ($socket) {
  109. stream_set_blocking($socket, false);
  110. }
  111. return $socket;
  112. }
  113. private function formatRecord(array $record): string
  114. {
  115. $recordFormatted = $record['formatted'];
  116. foreach (['log_uuid', 'uuid', 'uid'] as $key) {
  117. if (isset($record['extra'][$key])) {
  118. $recordFormatted['log_id'] = $record['extra'][$key];
  119. break;
  120. }
  121. }
  122. return base64_encode(serialize($recordFormatted))."\n";
  123. }
  124. }