scratch – Blame information for rev

Subversion Repositories:
Rev:
Rev Author Line No. Line
115 office 1 <?php
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  
12 namespace Monolog\Handler\Slack;
13  
14 use Monolog\Logger;
15 use Monolog\Formatter\NormalizerFormatter;
16 use Monolog\Formatter\FormatterInterface;
17  
18 /**
19 * Slack record utility helping to log to Slack webhooks or API.
20 *
21 * @author Greg Kedzierski <greg@gregkedzierski.com>
22 * @author Haralan Dobrev <hkdobrev@gmail.com>
23 * @see https://api.slack.com/incoming-webhooks
24 * @see https://api.slack.com/docs/message-attachments
25 */
26 class SlackRecord
27 {
28 const COLOR_DANGER = 'danger';
29  
30 const COLOR_WARNING = 'warning';
31  
32 const COLOR_GOOD = 'good';
33  
34 const COLOR_DEFAULT = '#e3e4e6';
35  
36 /**
37 * Slack channel (encoded ID or name)
38 * @var string|null
39 */
40 private $channel;
41  
42 /**
43 * Name of a bot
44 * @var string|null
45 */
46 private $username;
47  
48 /**
49 * User icon e.g. 'ghost', 'http://example.com/user.png'
50 * @var string
51 */
52 private $userIcon;
53  
54 /**
55 * Whether the message should be added to Slack as attachment (plain text otherwise)
56 * @var bool
57 */
58 private $useAttachment;
59  
60 /**
61 * Whether the the context/extra messages added to Slack as attachments are in a short style
62 * @var bool
63 */
64 private $useShortAttachment;
65  
66 /**
67 * Whether the attachment should include context and extra data
68 * @var bool
69 */
70 private $includeContextAndExtra;
71  
72 /**
73 * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
74 * @var array
75 */
76 private $excludeFields;
77  
78 /**
79 * @var FormatterInterface
80 */
81 private $formatter;
82  
83 /**
84 * @var NormalizerFormatter
85 */
86 private $normalizerFormatter;
87  
88 public function __construct($channel = null, $username = null, $useAttachment = true, $userIcon = null, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array(), FormatterInterface $formatter = null)
89 {
90 $this->channel = $channel;
91 $this->username = $username;
92 $this->userIcon = trim($userIcon, ':');
93 $this->useAttachment = $useAttachment;
94 $this->useShortAttachment = $useShortAttachment;
95 $this->includeContextAndExtra = $includeContextAndExtra;
96 $this->excludeFields = $excludeFields;
97 $this->formatter = $formatter;
98  
99 if ($this->includeContextAndExtra) {
100 $this->normalizerFormatter = new NormalizerFormatter();
101 }
102 }
103  
104 public function getSlackData(array $record)
105 {
106 $dataArray = array();
107 $record = $this->excludeFields($record);
108  
109 if ($this->username) {
110 $dataArray['username'] = $this->username;
111 }
112  
113 if ($this->channel) {
114 $dataArray['channel'] = $this->channel;
115 }
116  
117 if ($this->formatter && !$this->useAttachment) {
118 $message = $this->formatter->format($record);
119 } else {
120 $message = $record['message'];
121 }
122  
123 if ($this->useAttachment) {
124 $attachment = array(
125 'fallback' => $message,
126 'text' => $message,
127 'color' => $this->getAttachmentColor($record['level']),
128 'fields' => array(),
129 'mrkdwn_in' => array('fields'),
130 'ts' => $record['datetime']->getTimestamp()
131 );
132  
133 if ($this->useShortAttachment) {
134 $attachment['title'] = $record['level_name'];
135 } else {
136 $attachment['title'] = 'Message';
137 $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']);
138 }
139  
140  
141 if ($this->includeContextAndExtra) {
142 foreach (array('extra', 'context') as $key) {
143 if (empty($record[$key])) {
144 continue;
145 }
146  
147 if ($this->useShortAttachment) {
148 $attachment['fields'][] = $this->generateAttachmentField(
149 ucfirst($key),
150 $record[$key]
151 );
152 } else {
153 // Add all extra fields as individual fields in attachment
154 $attachment['fields'] = array_merge(
155 $attachment['fields'],
156 $this->generateAttachmentFields($record[$key])
157 );
158 }
159 }
160 }
161  
162 $dataArray['attachments'] = array($attachment);
163 } else {
164 $dataArray['text'] = $message;
165 }
166  
167 if ($this->userIcon) {
168 if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
169 $dataArray['icon_url'] = $this->userIcon;
170 } else {
171 $dataArray['icon_emoji'] = ":{$this->userIcon}:";
172 }
173 }
174  
175 return $dataArray;
176 }
177  
178 /**
179 * Returned a Slack message attachment color associated with
180 * provided level.
181 *
182 * @param int $level
183 * @return string
184 */
185 public function getAttachmentColor($level)
186 {
187 switch (true) {
188 case $level >= Logger::ERROR:
189 return self::COLOR_DANGER;
190 case $level >= Logger::WARNING:
191 return self::COLOR_WARNING;
192 case $level >= Logger::INFO:
193 return self::COLOR_GOOD;
194 default:
195 return self::COLOR_DEFAULT;
196 }
197 }
198  
199 /**
200 * Stringifies an array of key/value pairs to be used in attachment fields
201 *
202 * @param array $fields
203 *
204 * @return string
205 */
206 public function stringify($fields)
207 {
208 $normalized = $this->normalizerFormatter->format($fields);
209 $prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;
210  
211 $hasSecondDimension = count(array_filter($normalized, 'is_array'));
212 $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
213  
214 return $hasSecondDimension || $hasNonNumericKeys
215 ? json_encode($normalized, $prettyPrintFlag)
216 : json_encode($normalized);
217 }
218  
219 /**
220 * Sets the formatter
221 *
222 * @param FormatterInterface $formatter
223 */
224 public function setFormatter(FormatterInterface $formatter)
225 {
226 $this->formatter = $formatter;
227 }
228  
229 /**
230 * Generates attachment field
231 *
232 * @param string $title
233 * @param string|array $value\
234 *
235 * @return array
236 */
237 private function generateAttachmentField($title, $value)
238 {
239 $value = is_array($value)
240 ? sprintf('```%s```', $this->stringify($value))
241 : $value;
242  
243 return array(
244 'title' => $title,
245 'value' => $value,
246 'short' => false
247 );
248 }
249  
250 /**
251 * Generates a collection of attachment fields from array
252 *
253 * @param array $data
254 *
255 * @return array
256 */
257 private function generateAttachmentFields(array $data)
258 {
259 $fields = array();
260 foreach ($data as $key => $value) {
261 $fields[] = $this->generateAttachmentField($key, $value);
262 }
263  
264 return $fields;
265 }
266  
267 /**
268 * Get a copy of record with fields excluded according to $this->excludeFields
269 *
270 * @param array $record
271 *
272 * @return array
273 */
274 private function excludeFields(array $record)
275 {
276 foreach ($this->excludeFields as $field) {
277 $keys = explode('.', $field);
278 $node = &$record;
279 $lastKey = end($keys);
280 foreach ($keys as $key) {
281 if (!isset($node[$key])) {
282 break;
283 }
284 if ($lastKey === $key) {
285 unset($node[$key]);
286 break;
287 }
288 $node = &$node[$key];
289 }
290 }
291  
292 return $record;
293 }
294 }