LogglyHandler.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <?php declare(strict_types=1);
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  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 Monolog\Handler;
  11. use Monolog\Level;
  12. use Monolog\Formatter\FormatterInterface;
  13. use Monolog\Formatter\LogglyFormatter;
  14. use function array_key_exists;
  15. use CurlHandle;
  16. use Monolog\LogRecord;
  17. /**
  18. * Sends errors to Loggly.
  19. *
  20. * @author Przemek Sobstel <przemek@sobstel.org>
  21. * @author Adam Pancutt <adam@pancutt.com>
  22. * @author Gregory Barchard <gregory@barchard.net>
  23. */
  24. class LogglyHandler extends AbstractProcessingHandler
  25. {
  26. protected const HOST = 'logs-01.loggly.com';
  27. protected const ENDPOINT_SINGLE = 'inputs';
  28. protected const ENDPOINT_BATCH = 'bulk';
  29. /**
  30. * Caches the curl handlers for every given endpoint.
  31. *
  32. * @var CurlHandle[]
  33. */
  34. protected $curlHandlers = [];
  35. /** @var string */
  36. protected $token;
  37. /** @var string[] */
  38. protected $tag = [];
  39. /**
  40. * @param string $token API token supplied by Loggly
  41. *
  42. * @throws MissingExtensionException If the curl extension is missing
  43. */
  44. public function __construct(string $token, $level = Level::Debug, bool $bubble = true)
  45. {
  46. if (!extension_loaded('curl')) {
  47. throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler');
  48. }
  49. $this->token = $token;
  50. parent::__construct($level, $bubble);
  51. }
  52. /**
  53. * Loads and returns the shared curl handler for the given endpoint.
  54. *
  55. * @param string $endpoint
  56. *
  57. * @return CurlHandle
  58. */
  59. protected function getCurlHandler(string $endpoint)
  60. {
  61. if (!array_key_exists($endpoint, $this->curlHandlers)) {
  62. $this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint);
  63. }
  64. return $this->curlHandlers[$endpoint];
  65. }
  66. /**
  67. * Starts a fresh curl session for the given endpoint and returns its handler.
  68. *
  69. * @param string $endpoint
  70. *
  71. * @return CurlHandle
  72. */
  73. private function loadCurlHandle(string $endpoint)
  74. {
  75. $url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token);
  76. $ch = curl_init();
  77. curl_setopt($ch, CURLOPT_URL, $url);
  78. curl_setopt($ch, CURLOPT_POST, true);
  79. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  80. return $ch;
  81. }
  82. /**
  83. * @param string[]|string $tag
  84. */
  85. public function setTag($tag): self
  86. {
  87. $tag = !empty($tag) ? $tag : [];
  88. $this->tag = is_array($tag) ? $tag : [$tag];
  89. return $this;
  90. }
  91. /**
  92. * @param string[]|string $tag
  93. */
  94. public function addTag($tag): self
  95. {
  96. if (!empty($tag)) {
  97. $tag = is_array($tag) ? $tag : [$tag];
  98. $this->tag = array_unique(array_merge($this->tag, $tag));
  99. }
  100. return $this;
  101. }
  102. protected function write(LogRecord $record): void
  103. {
  104. $this->send($record->formatted, static::ENDPOINT_SINGLE);
  105. }
  106. public function handleBatch(array $records): void
  107. {
  108. $level = $this->level;
  109. $records = array_filter($records, function ($record) use ($level) {
  110. return ($record->level >= $level);
  111. });
  112. if ($records) {
  113. $this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH);
  114. }
  115. }
  116. protected function send(string $data, string $endpoint): void
  117. {
  118. $ch = $this->getCurlHandler($endpoint);
  119. $headers = ['Content-Type: application/json'];
  120. if (!empty($this->tag)) {
  121. $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag);
  122. }
  123. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  124. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  125. Curl\Util::execute($ch, 5, false);
  126. }
  127. protected function getDefaultFormatter(): FormatterInterface
  128. {
  129. return new LogglyFormatter();
  130. }
  131. }