Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
3.7 KiB

  1. <?php
  2. namespace Friendica\Util\Logger;
  3. use Friendica\Util\DateTimeFormat;
  4. use Friendica\Util\FileSystem;
  5. use Friendica\Util\Introspection;
  6. use Psr\Log\LogLevel;
  7. /**
  8. * A Logger instance for logging into a stream (file, stdout, stderr)
  9. */
  10. class StreamLogger extends AbstractLogger
  11. {
  12. /**
  13. * The minimum loglevel at which this logger will be triggered
  14. * @var string
  15. */
  16. private $logLevel;
  17. /**
  18. * The file URL of the stream (if needed)
  19. * @var string
  20. */
  21. private $url;
  22. /**
  23. * The stream, where the current logger is writing into
  24. * @var resource
  25. */
  26. private $stream;
  27. /**
  28. * The current process ID
  29. * @var int
  30. */
  31. private $pid;
  32. /**
  33. * @var FileSystem
  34. */
  35. private $fileSystem;
  36. /**
  37. * Translates LogLevel log levels to integer values
  38. * @var array
  39. */
  40. private $levelToInt = [
  41. LogLevel::EMERGENCY => 0,
  42. LogLevel::ALERT => 1,
  43. LogLevel::CRITICAL => 2,
  44. LogLevel::ERROR => 3,
  45. LogLevel::WARNING => 4,
  46. LogLevel::NOTICE => 5,
  47. LogLevel::INFO => 6,
  48. LogLevel::DEBUG => 7,
  49. ];
  50. /**
  51. * {@inheritdoc}
  52. * @param string|resource $stream The stream to write with this logger (either a file or a stream, i.e. stdout)
  53. * @param string $level The minimum loglevel at which this logger will be triggered
  54. *
  55. * @throws \Exception
  56. */
  57. public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, $level = LogLevel::DEBUG)
  58. {
  59. $this->fileSystem = $fileSystem;
  60. parent::__construct($channel, $introspection);
  61. if (is_resource($stream)) {
  62. $this->stream = $stream;
  63. } elseif (is_string($stream)) {
  64. $this->url = $stream;
  65. } else {
  66. throw new \InvalidArgumentException('A stream must either be a resource or a string.');
  67. }
  68. $this->pid = getmypid();
  69. if (array_key_exists($level, $this->levelToInt)) {
  70. $this->logLevel = $this->levelToInt[$level];
  71. } else {
  72. throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level));
  73. }
  74. $this->checkStream();
  75. }
  76. public function close()
  77. {
  78. if ($this->url && is_resource($this->stream)) {
  79. fclose($this->stream);
  80. }
  81. $this->stream = null;
  82. }
  83. /**
  84. * Adds a new entry to the log
  85. *
  86. * @param int $level
  87. * @param string $message
  88. * @param array $context
  89. *
  90. * @return void
  91. */
  92. protected function addEntry($level, $message, $context = [])
  93. {
  94. if (!array_key_exists($level, $this->levelToInt)) {
  95. throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level));
  96. }
  97. $logLevel = $this->levelToInt[$level];
  98. if ($logLevel > $this->logLevel) {
  99. return;
  100. }
  101. $this->checkStream();
  102. $formattedLog = $this->formatLog($level, $message, $context);
  103. fwrite($this->stream, $formattedLog);
  104. }
  105. /**
  106. * Formats a log record for the syslog output
  107. *
  108. * @param int $level The loglevel/priority
  109. * @param string $message The message
  110. * @param array $context The context of this call
  111. *
  112. * @return string the formatted syslog output
  113. */
  114. private function formatLog($level, $message, $context = [])
  115. {
  116. $record = $this->introspection->getRecord();
  117. $record = array_merge($record, ['uid' => $this->logUid, 'process_id' => $this->pid]);
  118. $logMessage = '';
  119. $logMessage .= DateTimeFormat::utcNow(DateTimeFormat::ATOM) . ' ';
  120. $logMessage .= $this->channel . ' ';
  121. $logMessage .= '[' . strtoupper($level) . ']: ';
  122. $logMessage .= $this->psrInterpolate($message, $context) . ' ';
  123. $logMessage .= @json_encode($context) . ' - ';
  124. $logMessage .= @json_encode($record);
  125. $logMessage .= PHP_EOL;
  126. return $logMessage;
  127. }
  128. private function checkStream()
  129. {
  130. if (is_resource($this->stream)) {
  131. return;
  132. }
  133. if (empty($this->url)) {
  134. throw new \LogicException('Missing stream URL.');
  135. }
  136. $this->stream = $this->fileSystem->createStream($this->url);
  137. }
  138. }