- new map change log to Slack channel - new "rally point" logging to Slack channel - new "rally point" poke options (e.g. custom message), closed #295 - new log options for WebSocket installations - added ship "mass" logging (backend only), #313 - added map logging to Slack, #326 - added "ESI error rate" limit detection - added "Monolog" as new logging library (Composer dependency) - added "Swiftmailer" as new eMail library (Composer dependency) - added Support for Redis session hander (performance boost) - improved character select panels (visible "online" status) - improved "activity logging" (more DB columns added to check) - improved eMail logging (HTML template support) - improved "delete map" now become "inactive" for some days before delete - improved character logout handling - improved /setup page for DB bootstrap (new button for DB create if not exists) - fixed broken ship tracking (ship name re-added) - fixed broken ship tracking for multiple chars on different browser tabs - fixed broken cursor coordinates, closed #518 - fixed null pointer "charactermodel.php->isActive():925" closed #529 - fixed broken "scroll offset", closed #533 closed #534 - Updated "validation" library JS v0.10.1 -> v0.11.9 - Updated ORM Mapper _Cortex_ v1.5.0-dev -> v1.5.0 - and many more....
260 lines
7.8 KiB
PHP
260 lines
7.8 KiB
PHP
<?php
|
|
/**
|
|
* Created by PhpStorm.
|
|
* User: exodu
|
|
* Date: 22.09.2017
|
|
* Time: 20:08
|
|
*/
|
|
|
|
namespace Lib\Logging\Handler;
|
|
|
|
use lib\Config;
|
|
use Monolog\Handler;
|
|
use Monolog\Logger;
|
|
|
|
abstract class AbstractSlackWebhookHandler extends Handler\AbstractProcessingHandler {
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $webhookUrl;
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
protected $useAttachment;
|
|
|
|
/**
|
|
* Whether the attachment should include context
|
|
* @var bool
|
|
*/
|
|
protected $includeContext;
|
|
|
|
/**
|
|
* Whether the attachment should include extra
|
|
* @var bool
|
|
*/
|
|
protected $includeExtra;
|
|
|
|
/**
|
|
* Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
|
|
* @var array
|
|
*/
|
|
private $excludeFields;
|
|
|
|
/**
|
|
* Max attachment count per message (20 is max)
|
|
* @var int
|
|
*/
|
|
private $maxAttachments = 15;
|
|
|
|
/**
|
|
* @param string $webhookUrl Slack Webhook URL
|
|
* @param string|null $channel Slack channel (encoded ID or name)
|
|
* @param string|null $username Name of a bot
|
|
* @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise)
|
|
* @param string|null $iconEmoji The emoji name to use (or null)
|
|
* @param bool $includeContext Whether the context data added to Slack as attachments are in a short style
|
|
* @param bool $includeExtra Whether the extra data added to Slack as attachments are in a short style
|
|
* @param int $level The minimum logging level at which this handler will be triggered
|
|
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
|
* @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
|
|
*/
|
|
public function __construct($webhookUrl, $channel = null, $username = null, $useAttachment = true, $iconEmoji = null, $includeContext = true, $includeExtra = false, $level = Logger::CRITICAL, $bubble = true, array $excludeFields = []){
|
|
$this->webhookUrl = $webhookUrl;
|
|
$this->channel = $channel;
|
|
$this->username = $username;
|
|
$this->userIcon = trim($iconEmoji, ':');
|
|
$this->useAttachment = $useAttachment;
|
|
$this->includeContext = $includeContext;
|
|
$this->includeExtra = $includeExtra;
|
|
$this->excludeFields = $excludeFields;
|
|
|
|
parent::__construct($level, $bubble);
|
|
|
|
}
|
|
|
|
/**
|
|
* format
|
|
* @param array $record
|
|
* @return array
|
|
*/
|
|
protected function getSlackData(array $record): array {
|
|
$postData = [];
|
|
|
|
if ($this->username) {
|
|
$postData['username'] = $this->username;
|
|
}
|
|
|
|
if ($this->channel) {
|
|
$postData['channel'] = $this->channel;
|
|
}
|
|
|
|
$postData['text'] = (string)$record['message'];
|
|
|
|
if ($this->userIcon) {
|
|
if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
|
|
$postData['icon_url'] = $this->userIcon;
|
|
} else {
|
|
$postData['icon_emoji'] = ":{$this->userIcon}:";
|
|
}
|
|
}
|
|
|
|
return $postData;
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*
|
|
* @param array $record
|
|
*/
|
|
protected function write(array $record){
|
|
$record = $this->excludeFields($record);
|
|
|
|
$postData = $this->getSlackData($record);
|
|
|
|
$postData = $this->cleanAttachments($postData);
|
|
|
|
$postString = json_encode($postData);
|
|
|
|
$ch = curl_init();
|
|
$options = [
|
|
CURLOPT_URL => $this->webhookUrl,
|
|
CURLOPT_CUSTOMREQUEST => 'POST',
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ['Content-type: application/json'],
|
|
CURLOPT_POSTFIELDS => $postString
|
|
];
|
|
if (defined('CURLOPT_SAFE_UPLOAD')) {
|
|
$options[CURLOPT_SAFE_UPLOAD] = true;
|
|
}
|
|
|
|
curl_setopt_array($ch, $options);
|
|
|
|
Handler\Curl\Util::execute($ch);
|
|
}
|
|
|
|
/**
|
|
* @param array $postData
|
|
* @return array
|
|
*/
|
|
protected function cleanAttachments(array $postData): array{
|
|
$attachmentCount = count($postData['attachments']);
|
|
if( $attachmentCount > $this->maxAttachments){
|
|
$text = 'To many attachments! ' . ($attachmentCount - $this->maxAttachments) . ' of ' . $attachmentCount . ' attachments not visible';
|
|
$postData['attachments'] = array_slice($postData['attachments'], 0, $this->maxAttachments);
|
|
|
|
$attachment = [
|
|
'title' => $text,
|
|
'fallback' => $text,
|
|
'color' => $this->getAttachmentColor('information')
|
|
];
|
|
|
|
$postData['attachments'][] = $attachment;
|
|
}
|
|
|
|
return $postData;
|
|
}
|
|
|
|
/**
|
|
* @param array $attachment
|
|
* @param array $characterData
|
|
* @return array
|
|
*/
|
|
protected function setAuthor(array $attachment, array $characterData): array {
|
|
if( !empty($characterData['id']) && !empty($characterData['name'])){
|
|
$attachment['author_name'] = $characterData['name'] . ' #' . $characterData['id'];
|
|
$attachment['author_link'] = Config::getPathfinderData('api.z_killboard') . '/character/' . $characterData['id'] . '/';
|
|
$attachment['author_icon'] = Config::getPathfinderData('api.ccp_image_server') . '/Character/' . $characterData['id'] . '_32.jpg';
|
|
}
|
|
|
|
return $attachment;
|
|
}
|
|
|
|
/**
|
|
* @param array $attachment
|
|
* @param array $thumbData
|
|
* @return array
|
|
*/
|
|
protected function setThumb(array $attachment, array $thumbData): array {
|
|
if( !empty($thumbData['url'])) {
|
|
$attachment['thumb_url'] = $thumbData['url'];
|
|
}
|
|
|
|
return $attachment;
|
|
}
|
|
|
|
/**
|
|
* @param $title
|
|
* @param $value
|
|
* @param bool $format
|
|
* @param bool $short
|
|
* @return array
|
|
*/
|
|
protected function generateAttachmentField($title, $value, $format = false, $short = true){
|
|
return [
|
|
'title' => $title,
|
|
'value' => !empty($value) ? ( $format ? sprintf('`%s`', $value) : $value ) : '',
|
|
'short' => $short
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param string $tag
|
|
* @return string
|
|
*/
|
|
protected function getAttachmentColor(string $tag): string {
|
|
switch($tag){
|
|
case 'information': $color = '#428bca'; break;
|
|
case 'success': $color = '#4f9e4f'; break;
|
|
case 'warning': $color = '#e28a0d'; break;
|
|
case 'danger': $color = '#a52521'; break;
|
|
default: $color = '#313335'; break;
|
|
}
|
|
return $color;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
} |