1<?php declare(strict_types=1); 2 3/* 4 * This file is part of the Monolog package. 5 * 6 * (c) Jordi Boggiano <j.boggiano@seld.be> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Monolog\Handler; 13 14use Monolog\Formatter\FormatterInterface; 15use Monolog\Logger; 16use Monolog\Utils; 17use Monolog\Handler\Slack\SlackRecord; 18 19/** 20 * Sends notifications through Slack Webhooks 21 * 22 * @author Haralan Dobrev <hkdobrev@gmail.com> 23 * @see https://api.slack.com/incoming-webhooks 24 */ 25class SlackWebhookHandler extends AbstractProcessingHandler 26{ 27 /** 28 * Slack Webhook token 29 * @var string 30 */ 31 private $webhookUrl; 32 33 /** 34 * Instance of the SlackRecord util class preparing data for Slack API. 35 * @var SlackRecord 36 */ 37 private $slackRecord; 38 39 /** 40 * @param string $webhookUrl Slack Webhook URL 41 * @param string|null $channel Slack channel (encoded ID or name) 42 * @param string|null $username Name of a bot 43 * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) 44 * @param string|null $iconEmoji The emoji name to use (or null) 45 * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style 46 * @param bool $includeContextAndExtra Whether the attachment should include context and extra data 47 * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] 48 */ 49 public function __construct( 50 string $webhookUrl, 51 ?string $channel = null, 52 ?string $username = null, 53 bool $useAttachment = true, 54 ?string $iconEmoji = null, 55 bool $useShortAttachment = false, 56 bool $includeContextAndExtra = false, 57 $level = Logger::CRITICAL, 58 bool $bubble = true, 59 array $excludeFields = array() 60 ) { 61 if (!extension_loaded('curl')) { 62 throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler'); 63 } 64 65 parent::__construct($level, $bubble); 66 67 $this->webhookUrl = $webhookUrl; 68 69 $this->slackRecord = new SlackRecord( 70 $channel, 71 $username, 72 $useAttachment, 73 $iconEmoji, 74 $useShortAttachment, 75 $includeContextAndExtra, 76 $excludeFields 77 ); 78 } 79 80 public function getSlackRecord(): SlackRecord 81 { 82 return $this->slackRecord; 83 } 84 85 public function getWebhookUrl(): string 86 { 87 return $this->webhookUrl; 88 } 89 90 /** 91 * {@inheritDoc} 92 */ 93 protected function write(array $record): void 94 { 95 $postData = $this->slackRecord->getSlackData($record); 96 $postString = Utils::jsonEncode($postData); 97 98 $ch = curl_init(); 99 $options = array( 100 CURLOPT_URL => $this->webhookUrl, 101 CURLOPT_POST => true, 102 CURLOPT_RETURNTRANSFER => true, 103 CURLOPT_HTTPHEADER => array('Content-type: application/json'), 104 CURLOPT_POSTFIELDS => $postString, 105 ); 106 if (defined('CURLOPT_SAFE_UPLOAD')) { 107 $options[CURLOPT_SAFE_UPLOAD] = true; 108 } 109 110 curl_setopt_array($ch, $options); 111 112 Curl\Util::execute($ch); 113 } 114 115 public function setFormatter(FormatterInterface $formatter): HandlerInterface 116 { 117 parent::setFormatter($formatter); 118 $this->slackRecord->setFormatter($formatter); 119 120 return $this; 121 } 122 123 public function getFormatter(): FormatterInterface 124 { 125 $formatter = parent::getFormatter(); 126 $this->slackRecord->setFormatter($formatter); 127 128 return $formatter; 129 } 130} 131