/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php

https://gitlab.com/4gdevs/online-class-record-system · PHP · 284 lines · 150 code · 33 blank · 101 comment · 15 complexity · a42c874dc0fa8adff4513a54c51b6700 MD5 · raw file

  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\Logger;
  12. use Monolog\Formatter\LineFormatter;
  13. /**
  14. * Sends notifications through Slack API
  15. *
  16. * @author Greg Kedzierski <greg@gregkedzierski.com>
  17. * @see https://api.slack.com/
  18. */
  19. class SlackHandler extends SocketHandler
  20. {
  21. /**
  22. * Slack API token
  23. * @var string
  24. */
  25. private $token;
  26. /**
  27. * Slack channel (encoded ID or name)
  28. * @var string
  29. */
  30. private $channel;
  31. /**
  32. * Name of a bot
  33. * @var string
  34. */
  35. private $username;
  36. /**
  37. * Emoji icon name
  38. * @var string
  39. */
  40. private $iconEmoji;
  41. /**
  42. * Whether the message should be added to Slack as attachment (plain text otherwise)
  43. * @var bool
  44. */
  45. private $useAttachment;
  46. /**
  47. * Whether the the context/extra messages added to Slack as attachments are in a short style
  48. * @var bool
  49. */
  50. private $useShortAttachment;
  51. /**
  52. * Whether the attachment should include context and extra data
  53. * @var bool
  54. */
  55. private $includeContextAndExtra;
  56. /**
  57. * @var LineFormatter
  58. */
  59. private $lineFormatter;
  60. /**
  61. * @param string $token Slack API token
  62. * @param string $channel Slack channel (encoded ID or name)
  63. * @param string $username Name of a bot
  64. * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise)
  65. * @param string|null $iconEmoji The emoji name to use (or null)
  66. * @param int $level The minimum logging level at which this handler will be triggered
  67. * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
  68. * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style
  69. * @param bool $includeContextAndExtra Whether the attachment should include context and extra data
  70. */
  71. public function __construct($token, $channel, $username = 'Monolog', $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false)
  72. {
  73. if (!extension_loaded('openssl')) {
  74. throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler');
  75. }
  76. parent::__construct('ssl://slack.com:443', $level, $bubble);
  77. $this->token = $token;
  78. $this->channel = $channel;
  79. $this->username = $username;
  80. $this->iconEmoji = trim($iconEmoji, ':');
  81. $this->useAttachment = $useAttachment;
  82. $this->useShortAttachment = $useShortAttachment;
  83. $this->includeContextAndExtra = $includeContextAndExtra;
  84. if ($this->includeContextAndExtra) {
  85. $this->lineFormatter = new LineFormatter;
  86. }
  87. }
  88. /**
  89. * {@inheritdoc}
  90. *
  91. * @param array $record
  92. * @return string
  93. */
  94. protected function generateDataStream($record)
  95. {
  96. $content = $this->buildContent($record);
  97. return $this->buildHeader($content) . $content;
  98. }
  99. /**
  100. * Builds the body of API call
  101. *
  102. * @param array $record
  103. * @return string
  104. */
  105. private function buildContent($record)
  106. {
  107. $dataArray = $this->prepareContentData($record);
  108. return http_build_query($dataArray);
  109. }
  110. /**
  111. * Prepares content data
  112. *
  113. * @param array $record
  114. * @return array
  115. */
  116. protected function prepareContentData($record)
  117. {
  118. $dataArray = array(
  119. 'token' => $this->token,
  120. 'channel' => $this->channel,
  121. 'username' => $this->username,
  122. 'text' => '',
  123. 'attachments' => array()
  124. );
  125. if ($this->useAttachment) {
  126. $attachment = array(
  127. 'fallback' => $record['message'],
  128. 'color' => $this->getAttachmentColor($record['level']),
  129. 'fields' => array()
  130. );
  131. if ($this->useShortAttachment) {
  132. $attachment['title'] = $record['level_name'];
  133. $attachment['text'] = $record['message'];
  134. } else {
  135. $attachment['title'] = 'Message';
  136. $attachment['text'] = $record['message'];
  137. $attachment['fields'][] = array(
  138. 'title' => 'Level',
  139. 'value' => $record['level_name'],
  140. 'short' => true
  141. );
  142. }
  143. if ($this->includeContextAndExtra) {
  144. if (!empty($record['extra'])) {
  145. if ($this->useShortAttachment) {
  146. $attachment['fields'][] = array(
  147. 'title' => "Extra",
  148. 'value' => $this->stringify($record['extra']),
  149. 'short' => $this->useShortAttachment
  150. );
  151. } else {
  152. // Add all extra fields as individual fields in attachment
  153. foreach ($record['extra'] as $var => $val) {
  154. $attachment['fields'][] = array(
  155. 'title' => $var,
  156. 'value' => $val,
  157. 'short' => $this->useShortAttachment
  158. );
  159. }
  160. }
  161. }
  162. if (!empty($record['context'])) {
  163. if ($this->useShortAttachment) {
  164. $attachment['fields'][] = array(
  165. 'title' => "Context",
  166. 'value' => $this->stringify($record['context']),
  167. 'short' => $this->useShortAttachment
  168. );
  169. } else {
  170. // Add all context fields as individual fields in attachment
  171. foreach ($record['context'] as $var => $val) {
  172. $attachment['fields'][] = array(
  173. 'title' => $var,
  174. 'value' => $val,
  175. 'short' => $this->useShortAttachment
  176. );
  177. }
  178. }
  179. }
  180. }
  181. $dataArray['attachments'] = json_encode(array($attachment));
  182. } else {
  183. $dataArray['text'] = $record['message'];
  184. }
  185. if ($this->iconEmoji) {
  186. $dataArray['icon_emoji'] = ":{$this->iconEmoji}:";
  187. }
  188. return $dataArray;
  189. }
  190. /**
  191. * Builds the header of the API Call
  192. *
  193. * @param string $content
  194. * @return string
  195. */
  196. private function buildHeader($content)
  197. {
  198. $header = "POST /api/chat.postMessage HTTP/1.1\r\n";
  199. $header .= "Host: slack.com\r\n";
  200. $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
  201. $header .= "Content-Length: " . strlen($content) . "\r\n";
  202. $header .= "\r\n";
  203. return $header;
  204. }
  205. /**
  206. * {@inheritdoc}
  207. *
  208. * @param array $record
  209. */
  210. protected function write(array $record)
  211. {
  212. parent::write($record);
  213. $this->closeSocket();
  214. }
  215. /**
  216. * Returned a Slack message attachment color associated with
  217. * provided level.
  218. *
  219. * @param int $level
  220. * @return string
  221. */
  222. protected function getAttachmentColor($level)
  223. {
  224. switch (true) {
  225. case $level >= Logger::ERROR:
  226. return 'danger';
  227. case $level >= Logger::WARNING:
  228. return 'warning';
  229. case $level >= Logger::INFO:
  230. return 'good';
  231. default:
  232. return '#e3e4e6';
  233. }
  234. }
  235. /**
  236. * Stringifies an array of key/value pairs to be used in attachment fields
  237. *
  238. * @param array $fields
  239. * @access protected
  240. * @return string
  241. */
  242. protected function stringify($fields)
  243. {
  244. $string = '';
  245. foreach ($fields as $var => $val) {
  246. $string .= $var.': '.$this->lineFormatter->stringify($val)." | ";
  247. }
  248. $string = rtrim($string, " |");
  249. return $string;
  250. }
  251. }