SlackHandler.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <?php
  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\Formatter\FormatterInterface;
  12. use Monolog\Logger;
  13. use Monolog\Handler\Slack\SlackRecord;
  14. /**
  15. * Sends notifications through Slack API
  16. *
  17. * @author Greg Kedzierski <greg@gregkedzierski.com>
  18. * @see https://api.slack.com/
  19. */
  20. class SlackHandler extends SocketHandler
  21. {
  22. /**
  23. * Slack API token
  24. * @var string
  25. */
  26. private $token;
  27. /**
  28. * Instance of the SlackRecord util class preparing data for Slack API.
  29. * @var SlackRecord
  30. */
  31. private $slackRecord;
  32. /**
  33. * @param string $token Slack API token
  34. * @param string $channel Slack channel (encoded ID or name)
  35. * @param string|null $username Name of a bot
  36. * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise)
  37. * @param string|null $iconEmoji The emoji name to use (or null)
  38. * @param int $level The minimum logging level at which this handler will be triggered
  39. * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
  40. * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style
  41. * @param bool $includeContextAndExtra Whether the attachment should include context and extra data
  42. * @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
  43. * @throws MissingExtensionException If no OpenSSL PHP extension configured
  44. */
  45. public function __construct($token, $channel, $username = null, $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array())
  46. {
  47. if (!extension_loaded('openssl')) {
  48. throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler');
  49. }
  50. parent::__construct('ssl://slack.com:443', $level, $bubble);
  51. $this->slackRecord = new SlackRecord(
  52. $channel,
  53. $username,
  54. $useAttachment,
  55. $iconEmoji,
  56. $useShortAttachment,
  57. $includeContextAndExtra,
  58. $excludeFields,
  59. $this->formatter
  60. );
  61. $this->token = $token;
  62. }
  63. public function getSlackRecord()
  64. {
  65. return $this->slackRecord;
  66. }
  67. /**
  68. * {@inheritdoc}
  69. *
  70. * @param array $record
  71. * @return string
  72. */
  73. protected function generateDataStream($record)
  74. {
  75. $content = $this->buildContent($record);
  76. return $this->buildHeader($content) . $content;
  77. }
  78. /**
  79. * Builds the body of API call
  80. *
  81. * @param array $record
  82. * @return string
  83. */
  84. private function buildContent($record)
  85. {
  86. $dataArray = $this->prepareContentData($record);
  87. return http_build_query($dataArray);
  88. }
  89. /**
  90. * Prepares content data
  91. *
  92. * @param array $record
  93. * @return array
  94. */
  95. protected function prepareContentData($record)
  96. {
  97. $dataArray = $this->slackRecord->getSlackData($record);
  98. $dataArray['token'] = $this->token;
  99. if (!empty($dataArray['attachments'])) {
  100. $dataArray['attachments'] = json_encode($dataArray['attachments']);
  101. }
  102. return $dataArray;
  103. }
  104. /**
  105. * Builds the header of the API Call
  106. *
  107. * @param string $content
  108. * @return string
  109. */
  110. private function buildHeader($content)
  111. {
  112. $header = "POST /api/chat.postMessage HTTP/1.1\r\n";
  113. $header .= "Host: slack.com\r\n";
  114. $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
  115. $header .= "Content-Length: " . strlen($content) . "\r\n";
  116. $header .= "\r\n";
  117. return $header;
  118. }
  119. /**
  120. * {@inheritdoc}
  121. *
  122. * @param array $record
  123. */
  124. protected function write(array $record)
  125. {
  126. parent::write($record);
  127. $this->finalizeWrite();
  128. }
  129. /**
  130. * Finalizes the request by reading some bytes and then closing the socket
  131. *
  132. * If we do not read some but close the socket too early, slack sometimes
  133. * drops the request entirely.
  134. */
  135. protected function finalizeWrite()
  136. {
  137. $res = $this->getResource();
  138. if (is_resource($res)) {
  139. @fread($res, 2048);
  140. }
  141. $this->closeSocket();
  142. }
  143. /**
  144. * Returned a Slack message attachment color associated with
  145. * provided level.
  146. *
  147. * @param int $level
  148. * @return string
  149. * @deprecated Use underlying SlackRecord instead
  150. */
  151. protected function getAttachmentColor($level)
  152. {
  153. trigger_error(
  154. 'SlackHandler::getAttachmentColor() is deprecated. Use underlying SlackRecord instead.',
  155. E_USER_DEPRECATED
  156. );
  157. return $this->slackRecord->getAttachmentColor($level);
  158. }
  159. /**
  160. * Stringifies an array of key/value pairs to be used in attachment fields
  161. *
  162. * @param array $fields
  163. * @return string
  164. * @deprecated Use underlying SlackRecord instead
  165. */
  166. protected function stringify($fields)
  167. {
  168. trigger_error(
  169. 'SlackHandler::stringify() is deprecated. Use underlying SlackRecord instead.',
  170. E_USER_DEPRECATED
  171. );
  172. return $this->slackRecord->stringify($fields);
  173. }
  174. public function setFormatter(FormatterInterface $formatter)
  175. {
  176. parent::setFormatter($formatter);
  177. $this->slackRecord->setFormatter($formatter);
  178. return $this;
  179. }
  180. public function getFormatter()
  181. {
  182. $formatter = parent::getFormatter();
  183. $this->slackRecord->setFormatter($formatter);
  184. return $formatter;
  185. }
  186. }