]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / vendor / monolog / monolog / src / Monolog / Handler / Slack / SlackRecord.php
diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php b/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php
new file mode 100644 (file)
index 0000000..38bc838
--- /dev/null
@@ -0,0 +1,294 @@
+<?php
+
+/*
+ * This file is part of the Monolog package.
+ *
+ * (c) Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Monolog\Handler\Slack;
+
+use Monolog\Logger;
+use Monolog\Formatter\NormalizerFormatter;
+use Monolog\Formatter\FormatterInterface;
+
+/**
+ * Slack record utility helping to log to Slack webhooks or API.
+ *
+ * @author Greg Kedzierski <greg@gregkedzierski.com>
+ * @author Haralan Dobrev <hkdobrev@gmail.com>
+ * @see    https://api.slack.com/incoming-webhooks
+ * @see    https://api.slack.com/docs/message-attachments
+ */
+class SlackRecord
+{
+    const COLOR_DANGER = 'danger';
+
+    const COLOR_WARNING = 'warning';
+
+    const COLOR_GOOD = 'good';
+
+    const COLOR_DEFAULT = '#e3e4e6';
+
+    /**
+     * Slack channel (encoded ID or name)
+     * @var string|null
+     */
+    private $channel;
+
+    /**
+     * Name of a bot
+     * @var string|null
+     */
+    private $username;
+
+    /**
+     * User icon e.g. 'ghost', 'http://example.com/user.png'
+     * @var string
+     */
+    private $userIcon;
+
+    /**
+     * Whether the message should be added to Slack as attachment (plain text otherwise)
+     * @var bool
+     */
+    private $useAttachment;
+
+    /**
+     * Whether the the context/extra messages added to Slack as attachments are in a short style
+     * @var bool
+     */
+    private $useShortAttachment;
+
+    /**
+     * Whether the attachment should include context and extra data
+     * @var bool
+     */
+    private $includeContextAndExtra;
+
+    /**
+     * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
+     * @var array
+     */
+    private $excludeFields;
+
+    /**
+     * @var FormatterInterface
+     */
+    private $formatter;
+
+    /**
+     * @var NormalizerFormatter
+     */
+    private $normalizerFormatter;
+
+    public function __construct($channel = null, $username = null, $useAttachment = true, $userIcon = null, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array(), FormatterInterface $formatter = null)
+    {
+        $this->channel = $channel;
+        $this->username = $username;
+        $this->userIcon = trim($userIcon, ':');
+        $this->useAttachment = $useAttachment;
+        $this->useShortAttachment = $useShortAttachment;
+        $this->includeContextAndExtra = $includeContextAndExtra;
+        $this->excludeFields = $excludeFields;
+        $this->formatter = $formatter;
+
+        if ($this->includeContextAndExtra) {
+            $this->normalizerFormatter = new NormalizerFormatter();
+        }
+    }
+
+    public function getSlackData(array $record)
+    {
+        $dataArray = array();
+        $record = $this->excludeFields($record);
+
+        if ($this->username) {
+            $dataArray['username'] = $this->username;
+        }
+
+        if ($this->channel) {
+            $dataArray['channel'] = $this->channel;
+        }
+
+        if ($this->formatter && !$this->useAttachment) {
+            $message = $this->formatter->format($record);
+        } else {
+            $message = $record['message'];
+        }
+
+        if ($this->useAttachment) {
+            $attachment = array(
+                'fallback'  => $message,
+                'text'      => $message,
+                'color'     => $this->getAttachmentColor($record['level']),
+                'fields'    => array(),
+                'mrkdwn_in' => array('fields'),
+                'ts'        => $record['datetime']->getTimestamp()
+            );
+
+            if ($this->useShortAttachment) {
+                $attachment['title'] = $record['level_name'];
+            } else {
+                $attachment['title'] = 'Message';
+                $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']);
+            }
+
+
+            if ($this->includeContextAndExtra) {
+                foreach (array('extra', 'context') as $key) {
+                    if (empty($record[$key])) {
+                        continue;
+                    }
+
+                    if ($this->useShortAttachment) {
+                        $attachment['fields'][] = $this->generateAttachmentField(
+                            ucfirst($key),
+                            $record[$key]
+                        );
+                    } else {
+                        // Add all extra fields as individual fields in attachment
+                        $attachment['fields'] = array_merge(
+                            $attachment['fields'],
+                            $this->generateAttachmentFields($record[$key])
+                        );
+                    }
+                }
+            }
+
+            $dataArray['attachments'] = array($attachment);
+        } else {
+            $dataArray['text'] = $message;
+        }
+
+        if ($this->userIcon) {
+            if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
+                $dataArray['icon_url'] = $this->userIcon;
+            } else {
+                $dataArray['icon_emoji'] = ":{$this->userIcon}:";
+            }
+        }
+
+        return $dataArray;
+    }
+
+    /**
+     * Returned a Slack message attachment color associated with
+     * provided level.
+     *
+     * @param  int    $level
+     * @return string
+     */
+    public function getAttachmentColor($level)
+    {
+        switch (true) {
+            case $level >= Logger::ERROR:
+                return self::COLOR_DANGER;
+            case $level >= Logger::WARNING:
+                return self::COLOR_WARNING;
+            case $level >= Logger::INFO:
+                return self::COLOR_GOOD;
+            default:
+                return self::COLOR_DEFAULT;
+        }
+    }
+
+    /**
+     * Stringifies an array of key/value pairs to be used in attachment fields
+     *
+     * @param array $fields
+     *
+     * @return string
+     */
+    public function stringify($fields)
+    {
+        $normalized = $this->normalizerFormatter->format($fields);
+        $prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;
+
+        $hasSecondDimension = count(array_filter($normalized, 'is_array'));
+        $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
+
+        return $hasSecondDimension || $hasNonNumericKeys
+            ? json_encode($normalized, $prettyPrintFlag)
+            : json_encode($normalized);
+    }
+
+    /**
+     * Sets the formatter
+     *
+     * @param FormatterInterface $formatter
+     */
+    public function setFormatter(FormatterInterface $formatter)
+    {
+        $this->formatter = $formatter;
+    }
+
+    /**
+     * Generates attachment field
+     *
+     * @param string $title
+     * @param string|array $value\
+     *
+     * @return array
+     */
+    private function generateAttachmentField($title, $value)
+    {
+        $value = is_array($value)
+            ? sprintf('```%s```', $this->stringify($value))
+            : $value;
+
+        return array(
+            'title' => $title,
+            'value' => $value,
+            'short' => false
+        );
+    }
+
+    /**
+     * Generates a collection of attachment fields from array
+     *
+     * @param array $data
+     *
+     * @return array
+     */
+    private function generateAttachmentFields(array $data)
+    {
+        $fields = array();
+        foreach ($data as $key => $value) {
+            $fields[] = $this->generateAttachmentField($key, $value);
+        }
+
+        return $fields;
+    }
+
+    /**
+     * Get a copy of record with fields excluded according to $this->excludeFields
+     *
+     * @param array $record
+     *
+     * @return array
+     */
+    private function excludeFields(array $record)
+    {
+        foreach ($this->excludeFields as $field) {
+            $keys = explode('.', $field);
+            $node = &$record;
+            $lastKey = end($keys);
+            foreach ($keys as $key) {
+                if (!isset($node[$key])) {
+                    break;
+                }
+                if ($lastKey === $key) {
+                    unset($node[$key]);
+                    break;
+                }
+                $node = &$node[$key];
+            }
+        }
+
+        return $record;
+    }
+}