/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php |
@@ -0,0 +1,186 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\FormatterInterface; |
use Monolog\Formatter\LineFormatter; |
|
/** |
* Base Handler class providing the Handler structure |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
abstract class AbstractHandler implements HandlerInterface |
{ |
protected $level = Logger::DEBUG; |
protected $bubble = true; |
|
/** |
* @var FormatterInterface |
*/ |
protected $formatter; |
protected $processors = array(); |
|
/** |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($level = Logger::DEBUG, $bubble = true) |
{ |
$this->setLevel($level); |
$this->bubble = $bubble; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function isHandling(array $record) |
{ |
return $record['level'] >= $this->level; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
foreach ($records as $record) { |
$this->handle($record); |
} |
} |
|
/** |
* Closes the handler. |
* |
* This will be called automatically when the object is destroyed |
*/ |
public function close() |
{ |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function pushProcessor($callback) |
{ |
if (!is_callable($callback)) { |
throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); |
} |
array_unshift($this->processors, $callback); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function popProcessor() |
{ |
if (!$this->processors) { |
throw new \LogicException('You tried to pop from an empty processor stack.'); |
} |
|
return array_shift($this->processors); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function setFormatter(FormatterInterface $formatter) |
{ |
$this->formatter = $formatter; |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getFormatter() |
{ |
if (!$this->formatter) { |
$this->formatter = $this->getDefaultFormatter(); |
} |
|
return $this->formatter; |
} |
|
/** |
* Sets minimum logging level at which this handler will be triggered. |
* |
* @param int|string $level Level or level name |
* @return self |
*/ |
public function setLevel($level) |
{ |
$this->level = Logger::toMonologLevel($level); |
|
return $this; |
} |
|
/** |
* Gets minimum logging level at which this handler will be triggered. |
* |
* @return int |
*/ |
public function getLevel() |
{ |
return $this->level; |
} |
|
/** |
* Sets the bubbling behavior. |
* |
* @param Boolean $bubble true means that this handler allows bubbling. |
* false means that bubbling is not permitted. |
* @return self |
*/ |
public function setBubble($bubble) |
{ |
$this->bubble = $bubble; |
|
return $this; |
} |
|
/** |
* Gets the bubbling behavior. |
* |
* @return Boolean true means that this handler allows bubbling. |
* false means that bubbling is not permitted. |
*/ |
public function getBubble() |
{ |
return $this->bubble; |
} |
|
public function __destruct() |
{ |
try { |
$this->close(); |
} catch (\Exception $e) { |
// do nothing |
} catch (\Throwable $e) { |
// do nothing |
} |
} |
|
/** |
* Gets the default formatter. |
* |
* @return FormatterInterface |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php |
@@ -0,0 +1,101 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\LineFormatter; |
|
/** |
* Common syslog functionality |
*/ |
abstract class AbstractSyslogHandler extends AbstractProcessingHandler |
{ |
protected $facility; |
|
/** |
* Translates Monolog log levels to syslog log priorities. |
*/ |
protected $logLevels = array( |
Logger::DEBUG => LOG_DEBUG, |
Logger::INFO => LOG_INFO, |
Logger::NOTICE => LOG_NOTICE, |
Logger::WARNING => LOG_WARNING, |
Logger::ERROR => LOG_ERR, |
Logger::CRITICAL => LOG_CRIT, |
Logger::ALERT => LOG_ALERT, |
Logger::EMERGENCY => LOG_EMERG, |
); |
|
/** |
* List of valid log facility names. |
*/ |
protected $facilities = array( |
'auth' => LOG_AUTH, |
'authpriv' => LOG_AUTHPRIV, |
'cron' => LOG_CRON, |
'daemon' => LOG_DAEMON, |
'kern' => LOG_KERN, |
'lpr' => LOG_LPR, |
'mail' => LOG_MAIL, |
'news' => LOG_NEWS, |
'syslog' => LOG_SYSLOG, |
'user' => LOG_USER, |
'uucp' => LOG_UUCP, |
); |
|
/** |
* @param mixed $facility |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
|
if (!defined('PHP_WINDOWS_VERSION_BUILD')) { |
$this->facilities['local0'] = LOG_LOCAL0; |
$this->facilities['local1'] = LOG_LOCAL1; |
$this->facilities['local2'] = LOG_LOCAL2; |
$this->facilities['local3'] = LOG_LOCAL3; |
$this->facilities['local4'] = LOG_LOCAL4; |
$this->facilities['local5'] = LOG_LOCAL5; |
$this->facilities['local6'] = LOG_LOCAL6; |
$this->facilities['local7'] = LOG_LOCAL7; |
} else { |
$this->facilities['local0'] = 128; // LOG_LOCAL0 |
$this->facilities['local1'] = 136; // LOG_LOCAL1 |
$this->facilities['local2'] = 144; // LOG_LOCAL2 |
$this->facilities['local3'] = 152; // LOG_LOCAL3 |
$this->facilities['local4'] = 160; // LOG_LOCAL4 |
$this->facilities['local5'] = 168; // LOG_LOCAL5 |
$this->facilities['local6'] = 176; // LOG_LOCAL6 |
$this->facilities['local7'] = 184; // LOG_LOCAL7 |
} |
|
// convert textual description of facility to syslog constant |
if (array_key_exists(strtolower($facility), $this->facilities)) { |
$facility = $this->facilities[strtolower($facility)]; |
} elseif (!in_array($facility, array_values($this->facilities), true)) { |
throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); |
} |
|
$this->facility = $facility; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php |
@@ -0,0 +1,148 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\JsonFormatter; |
use PhpAmqpLib\Message\AMQPMessage; |
use PhpAmqpLib\Channel\AMQPChannel; |
use AMQPExchange; |
|
class AmqpHandler extends AbstractProcessingHandler |
{ |
/** |
* @var AMQPExchange|AMQPChannel $exchange |
*/ |
protected $exchange; |
|
/** |
* @var string |
*/ |
protected $exchangeName; |
|
/** |
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use |
* @param string $exchangeName |
* @param int $level |
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true) |
{ |
if ($exchange instanceof AMQPExchange) { |
$exchange->setName($exchangeName); |
} elseif ($exchange instanceof AMQPChannel) { |
$this->exchangeName = $exchangeName; |
} else { |
throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required'); |
} |
$this->exchange = $exchange; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function write(array $record) |
{ |
$data = $record["formatted"]; |
$routingKey = $this->getRoutingKey($record); |
|
if ($this->exchange instanceof AMQPExchange) { |
$this->exchange->publish( |
$data, |
$routingKey, |
0, |
array( |
'delivery_mode' => 2, |
'content_type' => 'application/json', |
) |
); |
} else { |
$this->exchange->basic_publish( |
$this->createAmqpMessage($data), |
$this->exchangeName, |
$routingKey |
); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
public function handleBatch(array $records) |
{ |
if ($this->exchange instanceof AMQPExchange) { |
parent::handleBatch($records); |
|
return; |
} |
|
foreach ($records as $record) { |
if (!$this->isHandling($record)) { |
continue; |
} |
|
$record = $this->processRecord($record); |
$data = $this->getFormatter()->format($record); |
|
$this->exchange->batch_basic_publish( |
$this->createAmqpMessage($data), |
$this->exchangeName, |
$this->getRoutingKey($record) |
); |
} |
|
$this->exchange->publish_batch(); |
} |
|
/** |
* Gets the routing key for the AMQP exchange |
* |
* @param array $record |
* @return string |
*/ |
protected function getRoutingKey(array $record) |
{ |
$routingKey = sprintf( |
'%s.%s', |
// TODO 2.0 remove substr call |
substr($record['level_name'], 0, 4), |
$record['channel'] |
); |
|
return strtolower($routingKey); |
} |
|
/** |
* @param string $data |
* @return AMQPMessage |
*/ |
private function createAmqpMessage($data) |
{ |
return new AMQPMessage( |
(string) $data, |
array( |
'delivery_mode' => 2, |
'content_type' => 'application/json', |
) |
); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php |
@@ -0,0 +1,230 @@ |
<?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; |
|
use Monolog\Formatter\LineFormatter; |
|
/** |
* Handler sending logs to browser's javascript console with no browser extension required |
* |
* @author Olivier Poitrey <rs@dailymotion.com> |
*/ |
class BrowserConsoleHandler extends AbstractProcessingHandler |
{ |
protected static $initialized = false; |
protected static $records = array(); |
|
/** |
* {@inheritDoc} |
* |
* Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. |
* |
* Example of formatted string: |
* |
* You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function write(array $record) |
{ |
// Accumulate records |
self::$records[] = $record; |
|
// Register shutdown handler if not already done |
if (!self::$initialized) { |
self::$initialized = true; |
$this->registerShutdownFunction(); |
} |
} |
|
/** |
* Convert records to javascript console commands and send it to the browser. |
* This method is automatically called on PHP shutdown if output is HTML or Javascript. |
*/ |
public static function send() |
{ |
$format = self::getResponseFormat(); |
if ($format === 'unknown') { |
return; |
} |
|
if (count(self::$records)) { |
if ($format === 'html') { |
self::writeOutput('<script>' . self::generateScript() . '</script>'); |
} elseif ($format === 'js') { |
self::writeOutput(self::generateScript()); |
} |
self::reset(); |
} |
} |
|
/** |
* Forget all logged records |
*/ |
public static function reset() |
{ |
self::$records = array(); |
} |
|
/** |
* Wrapper for register_shutdown_function to allow overriding |
*/ |
protected function registerShutdownFunction() |
{ |
if (PHP_SAPI !== 'cli') { |
register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send')); |
} |
} |
|
/** |
* Wrapper for echo to allow overriding |
* |
* @param string $str |
*/ |
protected static function writeOutput($str) |
{ |
echo $str; |
} |
|
/** |
* Checks the format of the response |
* |
* If Content-Type is set to application/javascript or text/javascript -> js |
* If Content-Type is set to text/html, or is unset -> html |
* If Content-Type is anything else -> unknown |
* |
* @return string One of 'js', 'html' or 'unknown' |
*/ |
protected static function getResponseFormat() |
{ |
// Check content type |
foreach (headers_list() as $header) { |
if (stripos($header, 'content-type:') === 0) { |
// This handler only works with HTML and javascript outputs |
// text/javascript is obsolete in favour of application/javascript, but still used |
if (stripos($header, 'application/javascript') !== false || stripos($header, 'text/javascript') !== false) { |
return 'js'; |
} |
if (stripos($header, 'text/html') === false) { |
return 'unknown'; |
} |
break; |
} |
} |
|
return 'html'; |
} |
|
private static function generateScript() |
{ |
$script = array(); |
foreach (self::$records as $record) { |
$context = self::dump('Context', $record['context']); |
$extra = self::dump('Extra', $record['extra']); |
|
if (empty($context) && empty($extra)) { |
$script[] = self::call_array('log', self::handleStyles($record['formatted'])); |
} else { |
$script = array_merge($script, |
array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))), |
$context, |
$extra, |
array(self::call('groupEnd')) |
); |
} |
} |
|
return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; |
} |
|
private static function handleStyles($formatted) |
{ |
$args = array(self::quote('font-weight: normal')); |
$format = '%c' . $formatted; |
preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); |
|
foreach (array_reverse($matches) as $match) { |
$args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); |
$args[] = '"font-weight: normal"'; |
|
$pos = $match[0][1]; |
$format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); |
} |
|
array_unshift($args, self::quote($format)); |
|
return $args; |
} |
|
private static function handleCustomStyles($style, $string) |
{ |
static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'); |
static $labels = array(); |
|
return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) { |
if (trim($m[1]) === 'autolabel') { |
// Format the string as a label with consistent auto assigned background color |
if (!isset($labels[$string])) { |
$labels[$string] = $colors[count($labels) % count($colors)]; |
} |
$color = $labels[$string]; |
|
return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px"; |
} |
|
return $m[1]; |
}, $style); |
} |
|
private static function dump($title, array $dict) |
{ |
$script = array(); |
$dict = array_filter($dict); |
if (empty($dict)) { |
return $script; |
} |
$script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); |
foreach ($dict as $key => $value) { |
$value = json_encode($value); |
if (empty($value)) { |
$value = self::quote(''); |
} |
$script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value); |
} |
|
return $script; |
} |
|
private static function quote($arg) |
{ |
return '"' . addcslashes($arg, "\"\n\\") . '"'; |
} |
|
private static function call() |
{ |
$args = func_get_args(); |
$method = array_shift($args); |
|
return self::call_array($method, $args); |
} |
|
private static function call_array($method, array $args) |
{ |
return 'c.' . $method . '(' . implode(', ', $args) . ');'; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php |
@@ -0,0 +1,117 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Buffers all records until closing the handler and then pass them as batch. |
* |
* This is useful for a MailHandler to send only one mail per request instead of |
* sending one per log message. |
* |
* @author Christophe Coevoet <stof@notk.org> |
*/ |
class BufferHandler extends AbstractHandler |
{ |
protected $handler; |
protected $bufferSize = 0; |
protected $bufferLimit; |
protected $flushOnOverflow; |
protected $buffer = array(); |
protected $initialized = false; |
|
/** |
* @param HandlerInterface $handler Handler. |
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded |
*/ |
public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false) |
{ |
parent::__construct($level, $bubble); |
$this->handler = $handler; |
$this->bufferLimit = (int) $bufferLimit; |
$this->flushOnOverflow = $flushOnOverflow; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handle(array $record) |
{ |
if ($record['level'] < $this->level) { |
return false; |
} |
|
if (!$this->initialized) { |
// __destructor() doesn't get called on Fatal errors |
register_shutdown_function(array($this, 'close')); |
$this->initialized = true; |
} |
|
if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { |
if ($this->flushOnOverflow) { |
$this->flush(); |
} else { |
array_shift($this->buffer); |
$this->bufferSize--; |
} |
} |
|
if ($this->processors) { |
foreach ($this->processors as $processor) { |
$record = call_user_func($processor, $record); |
} |
} |
|
$this->buffer[] = $record; |
$this->bufferSize++; |
|
return false === $this->bubble; |
} |
|
public function flush() |
{ |
if ($this->bufferSize === 0) { |
return; |
} |
|
$this->handler->handleBatch($this->buffer); |
$this->clear(); |
} |
|
public function __destruct() |
{ |
// suppress the parent behavior since we already have register_shutdown_function() |
// to call close(), and the reference contained there will prevent this from being |
// GC'd until the end of the request |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
$this->flush(); |
} |
|
/** |
* Clears the buffer without flushing any messages down to the wrapped handler. |
*/ |
public function clear() |
{ |
$this->bufferSize = 0; |
$this->buffer = array(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php |
@@ -0,0 +1,211 @@ |
<?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; |
|
use Monolog\Formatter\ChromePHPFormatter; |
use Monolog\Logger; |
|
/** |
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) |
* |
* This also works out of the box with Firefox 43+ |
* |
* @author Christophe Coevoet <stof@notk.org> |
*/ |
class ChromePHPHandler extends AbstractProcessingHandler |
{ |
/** |
* Version of the extension |
*/ |
const VERSION = '4.0'; |
|
/** |
* Header name |
*/ |
const HEADER_NAME = 'X-ChromeLogger-Data'; |
|
/** |
* Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) |
*/ |
const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; |
|
protected static $initialized = false; |
|
/** |
* Tracks whether we sent too much data |
* |
* Chrome limits the headers to 256KB, so when we sent 240KB we stop sending |
* |
* @var Boolean |
*/ |
protected static $overflowed = false; |
|
protected static $json = array( |
'version' => self::VERSION, |
'columns' => array('label', 'log', 'backtrace', 'type'), |
'rows' => array(), |
); |
|
protected static $sendHeaders = true; |
|
/** |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
if (!function_exists('json_encode')) { |
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
$messages = array(); |
|
foreach ($records as $record) { |
if ($record['level'] < $this->level) { |
continue; |
} |
$messages[] = $this->processRecord($record); |
} |
|
if (!empty($messages)) { |
$messages = $this->getFormatter()->formatBatch($messages); |
self::$json['rows'] = array_merge(self::$json['rows'], $messages); |
$this->send(); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new ChromePHPFormatter(); |
} |
|
/** |
* Creates & sends header for a record |
* |
* @see sendHeader() |
* @see send() |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
self::$json['rows'][] = $record['formatted']; |
|
$this->send(); |
} |
|
/** |
* Sends the log header |
* |
* @see sendHeader() |
*/ |
protected function send() |
{ |
if (self::$overflowed || !self::$sendHeaders) { |
return; |
} |
|
if (!self::$initialized) { |
self::$initialized = true; |
|
self::$sendHeaders = $this->headersAccepted(); |
if (!self::$sendHeaders) { |
return; |
} |
|
self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; |
} |
|
$json = @json_encode(self::$json); |
$data = base64_encode(utf8_encode($json)); |
if (strlen($data) > 240 * 1024) { |
self::$overflowed = true; |
|
$record = array( |
'message' => 'Incomplete logs, chrome header size limit reached', |
'context' => array(), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'monolog', |
'datetime' => new \DateTime(), |
'extra' => array(), |
); |
self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); |
$json = @json_encode(self::$json); |
$data = base64_encode(utf8_encode($json)); |
} |
|
if (trim($data) !== '') { |
$this->sendHeader(self::HEADER_NAME, $data); |
} |
} |
|
/** |
* Send header string to the client |
* |
* @param string $header |
* @param string $content |
*/ |
protected function sendHeader($header, $content) |
{ |
if (!headers_sent() && self::$sendHeaders) { |
header(sprintf('%s: %s', $header, $content)); |
} |
} |
|
/** |
* Verifies if the headers are accepted by the current user agent |
* |
* @return Boolean |
*/ |
protected function headersAccepted() |
{ |
if (empty($_SERVER['HTTP_USER_AGENT'])) { |
return false; |
} |
|
return preg_match(self::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']); |
} |
|
/** |
* BC getter for the sendHeaders property that has been made static |
*/ |
public function __get($property) |
{ |
if ('sendHeaders' !== $property) { |
throw new \InvalidArgumentException('Undefined property '.$property); |
} |
|
return static::$sendHeaders; |
} |
|
/** |
* BC setter for the sendHeaders property that has been made static |
*/ |
public function __set($property, $value) |
{ |
if ('sendHeaders' !== $property) { |
throw new \InvalidArgumentException('Undefined property '.$property); |
} |
|
static::$sendHeaders = $value; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php |
@@ -0,0 +1,151 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Logs to Cube. |
* |
* @link http://square.github.com/cube/ |
* @author Wan Chen <kami@kamisama.me> |
*/ |
class CubeHandler extends AbstractProcessingHandler |
{ |
private $udpConnection; |
private $httpConnection; |
private $scheme; |
private $host; |
private $port; |
private $acceptedSchemes = array('http', 'udp'); |
|
/** |
* Create a Cube handler |
* |
* @throws \UnexpectedValueException when given url is not a valid url. |
* A valid url must consist of three parts : protocol://host:port |
* Only valid protocols used by Cube are http and udp |
*/ |
public function __construct($url, $level = Logger::DEBUG, $bubble = true) |
{ |
$urlInfo = parse_url($url); |
|
if (!isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { |
throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); |
} |
|
if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) { |
throw new \UnexpectedValueException( |
'Invalid protocol (' . $urlInfo['scheme'] . ').' |
. ' Valid options are ' . implode(', ', $this->acceptedSchemes)); |
} |
|
$this->scheme = $urlInfo['scheme']; |
$this->host = $urlInfo['host']; |
$this->port = $urlInfo['port']; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* Establish a connection to an UDP socket |
* |
* @throws \LogicException when unable to connect to the socket |
* @throws MissingExtensionException when there is no socket extension |
*/ |
protected function connectUdp() |
{ |
if (!extension_loaded('sockets')) { |
throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); |
} |
|
$this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0); |
if (!$this->udpConnection) { |
throw new \LogicException('Unable to create a socket'); |
} |
|
if (!socket_connect($this->udpConnection, $this->host, $this->port)) { |
throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); |
} |
} |
|
/** |
* Establish a connection to a http server |
* @throws \LogicException when no curl extension |
*/ |
protected function connectHttp() |
{ |
if (!extension_loaded('curl')) { |
throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler'); |
} |
|
$this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put'); |
|
if (!$this->httpConnection) { |
throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); |
} |
|
curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST"); |
curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
$date = $record['datetime']; |
|
$data = array('time' => $date->format('Y-m-d\TH:i:s.uO')); |
unset($record['datetime']); |
|
if (isset($record['context']['type'])) { |
$data['type'] = $record['context']['type']; |
unset($record['context']['type']); |
} else { |
$data['type'] = $record['channel']; |
} |
|
$data['data'] = $record['context']; |
$data['data']['level'] = $record['level']; |
|
if ($this->scheme === 'http') { |
$this->writeHttp(json_encode($data)); |
} else { |
$this->writeUdp(json_encode($data)); |
} |
} |
|
private function writeUdp($data) |
{ |
if (!$this->udpConnection) { |
$this->connectUdp(); |
} |
|
socket_send($this->udpConnection, $data, strlen($data), 0); |
} |
|
private function writeHttp($data) |
{ |
if (!$this->httpConnection) { |
$this->connectHttp(); |
} |
|
curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); |
curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array( |
'Content-Type: application/json', |
'Content-Length: ' . strlen('['.$data.']'), |
)); |
|
Curl\Util::execute($this->httpConnection, 5, false); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php |
@@ -0,0 +1,169 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Simple handler wrapper that deduplicates log records across multiple requests |
* |
* It also includes the BufferHandler functionality and will buffer |
* all messages until the end of the request or flush() is called. |
* |
* This works by storing all log records' messages above $deduplicationLevel |
* to the file specified by $deduplicationStore. When further logs come in at the end of the |
* request (or when flush() is called), all those above $deduplicationLevel are checked |
* against the existing stored logs. If they match and the timestamps in the stored log is |
* not older than $time seconds, the new log record is discarded. If no log record is new, the |
* whole data set is discarded. |
* |
* This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers |
* that send messages to people, to avoid spamming with the same message over and over in case of |
* a major component failure like a database server being down which makes all requests fail in the |
* same way. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class DeduplicationHandler extends BufferHandler |
{ |
/** |
* @var string |
*/ |
protected $deduplicationStore; |
|
/** |
* @var int |
*/ |
protected $deduplicationLevel; |
|
/** |
* @var int |
*/ |
protected $time; |
|
/** |
* @var bool |
*/ |
private $gc = false; |
|
/** |
* @param HandlerInterface $handler Handler. |
* @param string $deduplicationStore The file/path where the deduplication log should be kept |
* @param int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes |
* @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true) |
{ |
parent::__construct($handler, 0, Logger::DEBUG, $bubble, false); |
|
$this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore; |
$this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); |
$this->time = $time; |
} |
|
public function flush() |
{ |
if ($this->bufferSize === 0) { |
return; |
} |
|
$passthru = null; |
|
foreach ($this->buffer as $record) { |
if ($record['level'] >= $this->deduplicationLevel) { |
|
$passthru = $passthru || !$this->isDuplicate($record); |
if ($passthru) { |
$this->appendRecord($record); |
} |
} |
} |
|
// default of null is valid as well as if no record matches duplicationLevel we just pass through |
if ($passthru === true || $passthru === null) { |
$this->handler->handleBatch($this->buffer); |
} |
|
$this->clear(); |
|
if ($this->gc) { |
$this->collectLogs(); |
} |
} |
|
private function isDuplicate(array $record) |
{ |
if (!file_exists($this->deduplicationStore)) { |
return false; |
} |
|
$store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); |
if (!is_array($store)) { |
return false; |
} |
|
$yesterday = time() - 86400; |
$timestampValidity = $record['datetime']->getTimestamp() - $this->time; |
$expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']); |
|
for ($i = count($store) - 1; $i >= 0; $i--) { |
list($timestamp, $level, $message) = explode(':', $store[$i], 3); |
|
if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { |
return true; |
} |
|
if ($timestamp < $yesterday) { |
$this->gc = true; |
} |
} |
|
return false; |
} |
|
private function collectLogs() |
{ |
if (!file_exists($this->deduplicationStore)) { |
return false; |
} |
|
$handle = fopen($this->deduplicationStore, 'rw+'); |
flock($handle, LOCK_EX); |
$validLogs = array(); |
|
$timestampValidity = time() - $this->time; |
|
while (!feof($handle)) { |
$log = fgets($handle); |
if (substr($log, 0, 10) >= $timestampValidity) { |
$validLogs[] = $log; |
} |
} |
|
ftruncate($handle, 0); |
rewind($handle); |
foreach ($validLogs as $log) { |
fwrite($handle, $log); |
} |
|
flock($handle, LOCK_UN); |
fclose($handle); |
|
$this->gc = false; |
} |
|
private function appendRecord(array $record) |
{ |
file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php |
@@ -0,0 +1,107 @@ |
<?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; |
|
use Aws\Sdk; |
use Aws\DynamoDb\DynamoDbClient; |
use Aws\DynamoDb\Marshaler; |
use Monolog\Formatter\ScalarFormatter; |
use Monolog\Logger; |
|
/** |
* Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) |
* |
* @link https://github.com/aws/aws-sdk-php/ |
* @author Andrew Lawson <adlawson@gmail.com> |
*/ |
class DynamoDbHandler extends AbstractProcessingHandler |
{ |
const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; |
|
/** |
* @var DynamoDbClient |
*/ |
protected $client; |
|
/** |
* @var string |
*/ |
protected $table; |
|
/** |
* @var int |
*/ |
protected $version; |
|
/** |
* @var Marshaler |
*/ |
protected $marshaler; |
|
/** |
* @param DynamoDbClient $client |
* @param string $table |
* @param int $level |
* @param bool $bubble |
*/ |
public function __construct(DynamoDbClient $client, $table, $level = Logger::DEBUG, $bubble = true) |
{ |
if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) { |
$this->version = 3; |
$this->marshaler = new Marshaler; |
} else { |
$this->version = 2; |
} |
|
$this->client = $client; |
$this->table = $table; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
$filtered = $this->filterEmptyFields($record['formatted']); |
if ($this->version === 3) { |
$formatted = $this->marshaler->marshalItem($filtered); |
} else { |
$formatted = $this->client->formatAttributes($filtered); |
} |
|
$this->client->putItem(array( |
'TableName' => $this->table, |
'Item' => $formatted, |
)); |
} |
|
/** |
* @param array $record |
* @return array |
*/ |
protected function filterEmptyFields(array $record) |
{ |
return array_filter($record, function ($value) { |
return !empty($value) || false === $value || 0 === $value; |
}); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new ScalarFormatter(self::DATE_FORMAT); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php |
@@ -0,0 +1,128 @@ |
<?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; |
|
use Monolog\Formatter\FormatterInterface; |
use Monolog\Formatter\ElasticaFormatter; |
use Monolog\Logger; |
use Elastica\Client; |
use Elastica\Exception\ExceptionInterface; |
|
/** |
* Elastic Search handler |
* |
* Usage example: |
* |
* $client = new \Elastica\Client(); |
* $options = array( |
* 'index' => 'elastic_index_name', |
* 'type' => 'elastic_doc_type', |
* ); |
* $handler = new ElasticSearchHandler($client, $options); |
* $log = new Logger('application'); |
* $log->pushHandler($handler); |
* |
* @author Jelle Vink <jelle.vink@gmail.com> |
*/ |
class ElasticSearchHandler extends AbstractProcessingHandler |
{ |
/** |
* @var Client |
*/ |
protected $client; |
|
/** |
* @var array Handler config options |
*/ |
protected $options = array(); |
|
/** |
* @param Client $client Elastica Client object |
* @param array $options Handler configuration |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
$this->client = $client; |
$this->options = array_merge( |
array( |
'index' => 'monolog', // Elastic index name |
'type' => 'record', // Elastic document type |
'ignore_error' => false, // Suppress Elastica exceptions |
), |
$options |
); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function write(array $record) |
{ |
$this->bulkSend(array($record['formatted'])); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function setFormatter(FormatterInterface $formatter) |
{ |
if ($formatter instanceof ElasticaFormatter) { |
return parent::setFormatter($formatter); |
} |
throw new \InvalidArgumentException('ElasticSearchHandler is only compatible with ElasticaFormatter'); |
} |
|
/** |
* Getter options |
* @return array |
*/ |
public function getOptions() |
{ |
return $this->options; |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new ElasticaFormatter($this->options['index'], $this->options['type']); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
$documents = $this->getFormatter()->formatBatch($records); |
$this->bulkSend($documents); |
} |
|
/** |
* Use Elasticsearch bulk API to send list of documents |
* @param array $documents |
* @throws \RuntimeException |
*/ |
protected function bulkSend(array $documents) |
{ |
try { |
$this->client->addDocuments($documents); |
} catch (ExceptionInterface $e) { |
if (!$this->options['ignore_error']) { |
throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); |
} |
} |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php |
@@ -0,0 +1,82 @@ |
<?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; |
|
use Monolog\Formatter\LineFormatter; |
use Monolog\Logger; |
|
/** |
* Stores to PHP error_log() handler. |
* |
* @author Elan Ruusamäe <glen@delfi.ee> |
*/ |
class ErrorLogHandler extends AbstractProcessingHandler |
{ |
const OPERATING_SYSTEM = 0; |
const SAPI = 4; |
|
protected $messageType; |
protected $expandNewlines; |
|
/** |
* @param int $messageType Says where the error should go. |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param Boolean $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries |
*/ |
public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false) |
{ |
parent::__construct($level, $bubble); |
|
if (false === in_array($messageType, self::getAvailableTypes())) { |
$message = sprintf('The given message type "%s" is not supported', print_r($messageType, true)); |
throw new \InvalidArgumentException($message); |
} |
|
$this->messageType = $messageType; |
$this->expandNewlines = $expandNewlines; |
} |
|
/** |
* @return array With all available types |
*/ |
public static function getAvailableTypes() |
{ |
return array( |
self::OPERATING_SYSTEM, |
self::SAPI, |
); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
if ($this->expandNewlines) { |
$lines = preg_split('{[\r\n]+}', (string) $record['formatted']); |
foreach ($lines as $line) { |
error_log($line, $this->messageType); |
} |
} else { |
error_log((string) $record['formatted'], $this->messageType); |
} |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php |
@@ -0,0 +1,140 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Simple handler wrapper that filters records based on a list of levels |
* |
* It can be configured with an exact list of levels to allow, or a min/max level. |
* |
* @author Hennadiy Verkh |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class FilterHandler extends AbstractHandler |
{ |
/** |
* Handler or factory callable($record, $this) |
* |
* @var callable|\Monolog\Handler\HandlerInterface |
*/ |
protected $handler; |
|
/** |
* Minimum level for logs that are passed to handler |
* |
* @var int[] |
*/ |
protected $acceptedLevels; |
|
/** |
* Whether the messages that are handled can bubble up the stack or not |
* |
* @var Boolean |
*/ |
protected $bubble; |
|
/** |
* @param callable|HandlerInterface $handler Handler or factory callable($record, $this). |
* @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided |
* @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true) |
{ |
$this->handler = $handler; |
$this->bubble = $bubble; |
$this->setAcceptedLevels($minLevelOrList, $maxLevel); |
|
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { |
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); |
} |
} |
|
/** |
* @return array |
*/ |
public function getAcceptedLevels() |
{ |
return array_flip($this->acceptedLevels); |
} |
|
/** |
* @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided |
* @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array |
*/ |
public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY) |
{ |
if (is_array($minLevelOrList)) { |
$acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList); |
} else { |
$minLevelOrList = Logger::toMonologLevel($minLevelOrList); |
$maxLevel = Logger::toMonologLevel($maxLevel); |
$acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) { |
return $level >= $minLevelOrList && $level <= $maxLevel; |
})); |
} |
$this->acceptedLevels = array_flip($acceptedLevels); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function isHandling(array $record) |
{ |
return isset($this->acceptedLevels[$record['level']]); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handle(array $record) |
{ |
if (!$this->isHandling($record)) { |
return false; |
} |
|
// The same logic as in FingersCrossedHandler |
if (!$this->handler instanceof HandlerInterface) { |
$this->handler = call_user_func($this->handler, $record, $this); |
if (!$this->handler instanceof HandlerInterface) { |
throw new \RuntimeException("The factory callable should return a HandlerInterface"); |
} |
} |
|
if ($this->processors) { |
foreach ($this->processors as $processor) { |
$record = call_user_func($processor, $record); |
} |
} |
|
$this->handler->handle($record); |
|
return false === $this->bubble; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
$filtered = array(); |
foreach ($records as $record) { |
if ($this->isHandling($record)) { |
$filtered[] = $record; |
} |
} |
|
$this->handler->handleBatch($filtered); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php |
@@ -0,0 +1,163 @@ |
<?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; |
|
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; |
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; |
use Monolog\Logger; |
|
/** |
* Buffers all records until a certain level is reached |
* |
* The advantage of this approach is that you don't get any clutter in your log files. |
* Only requests which actually trigger an error (or whatever your actionLevel is) will be |
* in the logs, but they will contain all records, not only those above the level threshold. |
* |
* You can find the various activation strategies in the |
* Monolog\Handler\FingersCrossed\ namespace. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class FingersCrossedHandler extends AbstractHandler |
{ |
protected $handler; |
protected $activationStrategy; |
protected $buffering = true; |
protected $bufferSize; |
protected $buffer = array(); |
protected $stopBuffering; |
protected $passthruLevel; |
|
/** |
* @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). |
* @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action |
* @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) |
* @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered |
*/ |
public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null) |
{ |
if (null === $activationStrategy) { |
$activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); |
} |
|
// convert simple int activationStrategy to an object |
if (!$activationStrategy instanceof ActivationStrategyInterface) { |
$activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); |
} |
|
$this->handler = $handler; |
$this->activationStrategy = $activationStrategy; |
$this->bufferSize = $bufferSize; |
$this->bubble = $bubble; |
$this->stopBuffering = $stopBuffering; |
|
if ($passthruLevel !== null) { |
$this->passthruLevel = Logger::toMonologLevel($passthruLevel); |
} |
|
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { |
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function isHandling(array $record) |
{ |
return true; |
} |
|
/** |
* Manually activate this logger regardless of the activation strategy |
*/ |
public function activate() |
{ |
if ($this->stopBuffering) { |
$this->buffering = false; |
} |
if (!$this->handler instanceof HandlerInterface) { |
$record = end($this->buffer) ?: null; |
|
$this->handler = call_user_func($this->handler, $record, $this); |
if (!$this->handler instanceof HandlerInterface) { |
throw new \RuntimeException("The factory callable should return a HandlerInterface"); |
} |
} |
$this->handler->handleBatch($this->buffer); |
$this->buffer = array(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handle(array $record) |
{ |
if ($this->processors) { |
foreach ($this->processors as $processor) { |
$record = call_user_func($processor, $record); |
} |
} |
|
if ($this->buffering) { |
$this->buffer[] = $record; |
if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { |
array_shift($this->buffer); |
} |
if ($this->activationStrategy->isHandlerActivated($record)) { |
$this->activate(); |
} |
} else { |
$this->handler->handle($record); |
} |
|
return false === $this->bubble; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
if (null !== $this->passthruLevel) { |
$level = $this->passthruLevel; |
$this->buffer = array_filter($this->buffer, function ($record) use ($level) { |
return $record['level'] >= $level; |
}); |
if (count($this->buffer) > 0) { |
$this->handler->handleBatch($this->buffer); |
$this->buffer = array(); |
} |
} |
} |
|
/** |
* Resets the state of the handler. Stops forwarding records to the wrapped handler. |
*/ |
public function reset() |
{ |
$this->buffering = true; |
} |
|
/** |
* Clears the buffer without flushing any messages down to the wrapped handler. |
* |
* It also resets the handler to its initial buffering state. |
*/ |
public function clear() |
{ |
$this->buffer = array(); |
$this->reset(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php |
@@ -0,0 +1,195 @@ |
<?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; |
|
use Monolog\Formatter\WildfireFormatter; |
|
/** |
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. |
* |
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com> |
*/ |
class FirePHPHandler extends AbstractProcessingHandler |
{ |
/** |
* WildFire JSON header message format |
*/ |
const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; |
|
/** |
* FirePHP structure for parsing messages & their presentation |
*/ |
const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; |
|
/** |
* Must reference a "known" plugin, otherwise headers won't display in FirePHP |
*/ |
const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; |
|
/** |
* Header prefix for Wildfire to recognize & parse headers |
*/ |
const HEADER_PREFIX = 'X-Wf'; |
|
/** |
* Whether or not Wildfire vendor-specific headers have been generated & sent yet |
*/ |
protected static $initialized = false; |
|
/** |
* Shared static message index between potentially multiple handlers |
* @var int |
*/ |
protected static $messageIndex = 1; |
|
protected static $sendHeaders = true; |
|
/** |
* Base header creation function used by init headers & record headers |
* |
* @param array $meta Wildfire Plugin, Protocol & Structure Indexes |
* @param string $message Log message |
* @return array Complete header string ready for the client as key and message as value |
*/ |
protected function createHeader(array $meta, $message) |
{ |
$header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta)); |
|
return array($header => $message); |
} |
|
/** |
* Creates message header from record |
* |
* @see createHeader() |
* @param array $record |
* @return string |
*/ |
protected function createRecordHeader(array $record) |
{ |
// Wildfire is extensible to support multiple protocols & plugins in a single request, |
// but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. |
return $this->createHeader( |
array(1, 1, 1, self::$messageIndex++), |
$record['formatted'] |
); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new WildfireFormatter(); |
} |
|
/** |
* Wildfire initialization headers to enable message parsing |
* |
* @see createHeader() |
* @see sendHeader() |
* @return array |
*/ |
protected function getInitHeaders() |
{ |
// Initial payload consists of required headers for Wildfire |
return array_merge( |
$this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), |
$this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), |
$this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI) |
); |
} |
|
/** |
* Send header string to the client |
* |
* @param string $header |
* @param string $content |
*/ |
protected function sendHeader($header, $content) |
{ |
if (!headers_sent() && self::$sendHeaders) { |
header(sprintf('%s: %s', $header, $content)); |
} |
} |
|
/** |
* Creates & sends header for a record, ensuring init headers have been sent prior |
* |
* @see sendHeader() |
* @see sendInitHeaders() |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
if (!self::$sendHeaders) { |
return; |
} |
|
// WildFire-specific headers must be sent prior to any messages |
if (!self::$initialized) { |
self::$initialized = true; |
|
self::$sendHeaders = $this->headersAccepted(); |
if (!self::$sendHeaders) { |
return; |
} |
|
foreach ($this->getInitHeaders() as $header => $content) { |
$this->sendHeader($header, $content); |
} |
} |
|
$header = $this->createRecordHeader($record); |
if (trim(current($header)) !== '') { |
$this->sendHeader(key($header), current($header)); |
} |
} |
|
/** |
* Verifies if the headers are accepted by the current user agent |
* |
* @return Boolean |
*/ |
protected function headersAccepted() |
{ |
if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { |
return true; |
} |
|
return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); |
} |
|
/** |
* BC getter for the sendHeaders property that has been made static |
*/ |
public function __get($property) |
{ |
if ('sendHeaders' !== $property) { |
throw new \InvalidArgumentException('Undefined property '.$property); |
} |
|
return static::$sendHeaders; |
} |
|
/** |
* BC setter for the sendHeaders property that has been made static |
*/ |
public function __set($property, $value) |
{ |
if ('sendHeaders' !== $property) { |
throw new \InvalidArgumentException('Undefined property '.$property); |
} |
|
static::$sendHeaders = $value; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php |
@@ -0,0 +1,126 @@ |
<?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; |
|
use Monolog\Formatter\LineFormatter; |
use Monolog\Logger; |
|
/** |
* Sends logs to Fleep.io using Webhook integrations |
* |
* You'll need a Fleep.io account to use this handler. |
* |
* @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation |
* @author Ando Roots <ando@sqroot.eu> |
*/ |
class FleepHookHandler extends SocketHandler |
{ |
const FLEEP_HOST = 'fleep.io'; |
|
const FLEEP_HOOK_URI = '/hook/'; |
|
/** |
* @var string Webhook token (specifies the conversation where logs are sent) |
*/ |
protected $token; |
|
/** |
* Construct a new Fleep.io Handler. |
* |
* For instructions on how to create a new web hook in your conversations |
* see https://fleep.io/integrations/webhooks/ |
* |
* @param string $token Webhook token |
* @param bool|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 |
* @throws MissingExtensionException |
*/ |
public function __construct($token, $level = Logger::DEBUG, $bubble = true) |
{ |
if (!extension_loaded('openssl')) { |
throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); |
} |
|
$this->token = $token; |
|
$connectionString = 'ssl://' . self::FLEEP_HOST . ':443'; |
parent::__construct($connectionString, $level, $bubble); |
} |
|
/** |
* Returns the default formatter to use with this handler |
* |
* Overloaded to remove empty context and extra arrays from the end of the log message. |
* |
* @return LineFormatter |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter(null, null, true, true); |
} |
|
/** |
* Handles a log record |
* |
* @param array $record |
*/ |
public function write(array $record) |
{ |
parent::write($record); |
$this->closeSocket(); |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
* @return string |
*/ |
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
/** |
* Builds the header of the API Call |
* |
* @param string $content |
* @return string |
*/ |
private function buildHeader($content) |
{ |
$header = "POST " . self::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; |
$header .= "Host: " . self::FLEEP_HOST . "\r\n"; |
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
|
/** |
* Builds the body of API call |
* |
* @param array $record |
* @return string |
*/ |
private function buildContent($record) |
{ |
$dataArray = array( |
'message' => $record['formatted'], |
); |
|
return http_build_query($dataArray); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php |
@@ -0,0 +1,127 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\FlowdockFormatter; |
use Monolog\Formatter\FormatterInterface; |
|
/** |
* Sends notifications through the Flowdock push API |
* |
* This must be configured with a FlowdockFormatter instance via setFormatter() |
* |
* Notes: |
* API token - Flowdock API token |
* |
* @author Dominik Liebler <liebler.dominik@gmail.com> |
* @see https://www.flowdock.com/api/push |
*/ |
class FlowdockHandler extends SocketHandler |
{ |
/** |
* @var string |
*/ |
protected $apiToken; |
|
/** |
* @param string $apiToken |
* @param bool|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 |
* |
* @throws MissingExtensionException if OpenSSL is missing |
*/ |
public function __construct($apiToken, $level = Logger::DEBUG, $bubble = true) |
{ |
if (!extension_loaded('openssl')) { |
throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); |
} |
|
parent::__construct('ssl://api.flowdock.com:443', $level, $bubble); |
$this->apiToken = $apiToken; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function setFormatter(FormatterInterface $formatter) |
{ |
if (!$formatter instanceof FlowdockFormatter) { |
throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); |
} |
|
return parent::setFormatter($formatter); |
} |
|
/** |
* Gets the default formatter. |
* |
* @return FormatterInterface |
*/ |
protected function getDefaultFormatter() |
{ |
throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
parent::write($record); |
|
$this->closeSocket(); |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
* @return string |
*/ |
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
/** |
* Builds the body of API call |
* |
* @param array $record |
* @return string |
*/ |
private function buildContent($record) |
{ |
return json_encode($record['formatted']['flowdock']); |
} |
|
/** |
* Builds the header of the API Call |
* |
* @param string $content |
* @return string |
*/ |
private function buildHeader($content) |
{ |
$header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; |
$header .= "Host: api.flowdock.com\r\n"; |
$header .= "Content-Type: application/json\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php |
@@ -0,0 +1,104 @@ |
<?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; |
|
use Monolog\Formatter\FormatterInterface; |
|
/** |
* Forwards records to multiple handlers |
* |
* @author Lenar Lõhmus <lenar@city.ee> |
*/ |
class GroupHandler extends AbstractHandler |
{ |
protected $handlers; |
|
/** |
* @param array $handlers Array of Handlers. |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct(array $handlers, $bubble = true) |
{ |
foreach ($handlers as $handler) { |
if (!$handler instanceof HandlerInterface) { |
throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); |
} |
} |
|
$this->handlers = $handlers; |
$this->bubble = $bubble; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function isHandling(array $record) |
{ |
foreach ($this->handlers as $handler) { |
if ($handler->isHandling($record)) { |
return true; |
} |
} |
|
return false; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handle(array $record) |
{ |
if ($this->processors) { |
foreach ($this->processors as $processor) { |
$record = call_user_func($processor, $record); |
} |
} |
|
foreach ($this->handlers as $handler) { |
$handler->handle($record); |
} |
|
return false === $this->bubble; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
if ($this->processors) { |
$processed = array(); |
foreach ($records as $record) { |
foreach ($this->processors as $processor) { |
$processed[] = call_user_func($processor, $record); |
} |
} |
$records = $processed; |
} |
|
foreach ($this->handlers as $handler) { |
$handler->handleBatch($records); |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function setFormatter(FormatterInterface $formatter) |
{ |
foreach ($this->handlers as $handler) { |
$handler->setFormatter($formatter); |
} |
|
return $this; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php |
@@ -0,0 +1,90 @@ |
<?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; |
|
use Monolog\Formatter\FormatterInterface; |
|
/** |
* Interface that all Monolog Handlers must implement |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
interface HandlerInterface |
{ |
/** |
* Checks whether the given record will be handled by this handler. |
* |
* This is mostly done for performance reasons, to avoid calling processors for nothing. |
* |
* Handlers should still check the record levels within handle(), returning false in isHandling() |
* is no guarantee that handle() will not be called, and isHandling() might not be called |
* for a given record. |
* |
* @param array $record Partial log record containing only a level key |
* |
* @return Boolean |
*/ |
public function isHandling(array $record); |
|
/** |
* Handles a record. |
* |
* All records may be passed to this method, and the handler should discard |
* those that it does not want to handle. |
* |
* The return value of this function controls the bubbling process of the handler stack. |
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on |
* calling further handlers in the stack with a given log record. |
* |
* @param array $record The record to handle |
* @return Boolean true means that this handler handled the record, and that bubbling is not permitted. |
* false means the record was either not processed or that this handler allows bubbling. |
*/ |
public function handle(array $record); |
|
/** |
* Handles a set of records at once. |
* |
* @param array $records The records to handle (an array of record arrays) |
*/ |
public function handleBatch(array $records); |
|
/** |
* Adds a processor in the stack. |
* |
* @param callable $callback |
* @return self |
*/ |
public function pushProcessor($callback); |
|
/** |
* Removes the processor on top of the stack and returns it. |
* |
* @return callable |
*/ |
public function popProcessor(); |
|
/** |
* Sets the formatter. |
* |
* @param FormatterInterface $formatter |
* @return self |
*/ |
public function setFormatter(FormatterInterface $formatter); |
|
/** |
* Gets the formatter. |
* |
* @return FormatterInterface |
*/ |
public function getFormatter(); |
} |
/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php |
@@ -0,0 +1,108 @@ |
<?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; |
|
use Monolog\Formatter\FormatterInterface; |
|
/** |
* This simple wrapper class can be used to extend handlers functionality. |
* |
* Example: A custom filtering that can be applied to any handler. |
* |
* Inherit from this class and override handle() like this: |
* |
* public function handle(array $record) |
* { |
* if ($record meets certain conditions) { |
* return false; |
* } |
* return $this->handler->handle($record); |
* } |
* |
* @author Alexey Karapetov <alexey@karapetov.com> |
*/ |
class HandlerWrapper implements HandlerInterface |
{ |
/** |
* @var HandlerInterface |
*/ |
protected $handler; |
|
/** |
* HandlerWrapper constructor. |
* @param HandlerInterface $handler |
*/ |
public function __construct(HandlerInterface $handler) |
{ |
$this->handler = $handler; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function isHandling(array $record) |
{ |
return $this->handler->isHandling($record); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handle(array $record) |
{ |
return $this->handler->handle($record); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
return $this->handler->handleBatch($records); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function pushProcessor($callback) |
{ |
$this->handler->pushProcessor($callback); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function popProcessor() |
{ |
return $this->handler->popProcessor(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function setFormatter(FormatterInterface $formatter) |
{ |
$this->handler->setFormatter($formatter); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getFormatter() |
{ |
return $this->handler->getFormatter(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php |
@@ -0,0 +1,350 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Sends notifications through the hipchat api to a hipchat room |
* |
* Notes: |
* API token - HipChat API token |
* Room - HipChat Room Id or name, where messages are sent |
* Name - Name used to send the message (from) |
* notify - Should the message trigger a notification in the clients |
* version - The API version to use (HipChatHandler::API_V1 | HipChatHandler::API_V2) |
* |
* @author Rafael Dohms <rafael@doh.ms> |
* @see https://www.hipchat.com/docs/api |
*/ |
class HipChatHandler extends SocketHandler |
{ |
/** |
* Use API version 1 |
*/ |
const API_V1 = 'v1'; |
|
/** |
* Use API version v2 |
*/ |
const API_V2 = 'v2'; |
|
/** |
* The maximum allowed length for the name used in the "from" field. |
*/ |
const MAXIMUM_NAME_LENGTH = 15; |
|
/** |
* The maximum allowed length for the message. |
*/ |
const MAXIMUM_MESSAGE_LENGTH = 9500; |
|
/** |
* @var string |
*/ |
private $token; |
|
/** |
* @var string |
*/ |
private $room; |
|
/** |
* @var string |
*/ |
private $name; |
|
/** |
* @var bool |
*/ |
private $notify; |
|
/** |
* @var string |
*/ |
private $format; |
|
/** |
* @var string |
*/ |
private $host; |
|
/** |
* @var string |
*/ |
private $version; |
|
/** |
* @param string $token HipChat API Token |
* @param string $room The room that should be alerted of the message (Id or Name) |
* @param string $name Name used in the "from" field. |
* @param bool $notify Trigger a notification in clients or not |
* @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 bool $useSSL Whether to connect via SSL. |
* @param string $format The format of the messages (default to text, can be set to html if you have html in the messages) |
* @param string $host The HipChat server hostname. |
* @param string $version The HipChat API version (default HipChatHandler::API_V1) |
*/ |
public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1) |
{ |
if ($version == self::API_V1 && !$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) { |
throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); |
} |
|
$connectionString = $useSSL ? 'ssl://'.$host.':443' : $host.':80'; |
parent::__construct($connectionString, $level, $bubble); |
|
$this->token = $token; |
$this->name = $name; |
$this->notify = $notify; |
$this->room = $room; |
$this->format = $format; |
$this->host = $host; |
$this->version = $version; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
* @return string |
*/ |
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
/** |
* Builds the body of API call |
* |
* @param array $record |
* @return string |
*/ |
private function buildContent($record) |
{ |
$dataArray = array( |
'notify' => $this->version == self::API_V1 ? |
($this->notify ? 1 : 0) : |
($this->notify ? 'true' : 'false'), |
'message' => $record['formatted'], |
'message_format' => $this->format, |
'color' => $this->getAlertColor($record['level']), |
); |
|
if (!$this->validateStringLength($dataArray['message'], static::MAXIMUM_MESSAGE_LENGTH)) { |
if (function_exists('mb_substr')) { |
$dataArray['message'] = mb_substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]'; |
} else { |
$dataArray['message'] = substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]'; |
} |
} |
|
// if we are using the legacy API then we need to send some additional information |
if ($this->version == self::API_V1) { |
$dataArray['room_id'] = $this->room; |
} |
|
// append the sender name if it is set |
// always append it if we use the v1 api (it is required in v1) |
if ($this->version == self::API_V1 || $this->name !== null) { |
$dataArray['from'] = (string) $this->name; |
} |
|
return http_build_query($dataArray); |
} |
|
/** |
* Builds the header of the API Call |
* |
* @param string $content |
* @return string |
*/ |
private function buildHeader($content) |
{ |
if ($this->version == self::API_V1) { |
$header = "POST /v1/rooms/message?format=json&auth_token={$this->token} HTTP/1.1\r\n"; |
} else { |
// needed for rooms with special (spaces, etc) characters in the name |
$room = rawurlencode($this->room); |
$header = "POST /v2/room/{$room}/notification?auth_token={$this->token} HTTP/1.1\r\n"; |
} |
|
$header .= "Host: {$this->host}\r\n"; |
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
|
/** |
* Assigns a color to each level of log records. |
* |
* @param int $level |
* @return string |
*/ |
protected function getAlertColor($level) |
{ |
switch (true) { |
case $level >= Logger::ERROR: |
return 'red'; |
case $level >= Logger::WARNING: |
return 'yellow'; |
case $level >= Logger::INFO: |
return 'green'; |
case $level == Logger::DEBUG: |
return 'gray'; |
default: |
return 'yellow'; |
} |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
parent::write($record); |
$this->closeSocket(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
if (count($records) == 0) { |
return true; |
} |
|
$batchRecords = $this->combineRecords($records); |
|
$handled = false; |
foreach ($batchRecords as $batchRecord) { |
if ($this->isHandling($batchRecord)) { |
$this->write($batchRecord); |
$handled = true; |
} |
} |
|
if (!$handled) { |
return false; |
} |
|
return false === $this->bubble; |
} |
|
/** |
* Combines multiple records into one. Error level of the combined record |
* will be the highest level from the given records. Datetime will be taken |
* from the first record. |
* |
* @param $records |
* @return array |
*/ |
private function combineRecords($records) |
{ |
$batchRecord = null; |
$batchRecords = array(); |
$messages = array(); |
$formattedMessages = array(); |
$level = 0; |
$levelName = null; |
$datetime = null; |
|
foreach ($records as $record) { |
$record = $this->processRecord($record); |
|
if ($record['level'] > $level) { |
$level = $record['level']; |
$levelName = $record['level_name']; |
} |
|
if (null === $datetime) { |
$datetime = $record['datetime']; |
} |
|
$messages[] = $record['message']; |
$messageStr = implode(PHP_EOL, $messages); |
$formattedMessages[] = $this->getFormatter()->format($record); |
$formattedMessageStr = implode('', $formattedMessages); |
|
$batchRecord = array( |
'message' => $messageStr, |
'formatted' => $formattedMessageStr, |
'context' => array(), |
'extra' => array(), |
); |
|
if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) { |
// Pop the last message and implode the remaining messages |
$lastMessage = array_pop($messages); |
$lastFormattedMessage = array_pop($formattedMessages); |
$batchRecord['message'] = implode(PHP_EOL, $messages); |
$batchRecord['formatted'] = implode('', $formattedMessages); |
|
$batchRecords[] = $batchRecord; |
$messages = array($lastMessage); |
$formattedMessages = array($lastFormattedMessage); |
|
$batchRecord = null; |
} |
} |
|
if (null !== $batchRecord) { |
$batchRecords[] = $batchRecord; |
} |
|
// Set the max level and datetime for all records |
foreach ($batchRecords as &$batchRecord) { |
$batchRecord = array_merge( |
$batchRecord, |
array( |
'level' => $level, |
'level_name' => $levelName, |
'datetime' => $datetime, |
) |
); |
} |
|
return $batchRecords; |
} |
|
/** |
* Validates the length of a string. |
* |
* If the `mb_strlen()` function is available, it will use that, as HipChat |
* allows UTF-8 characters. Otherwise, it will fall back to `strlen()`. |
* |
* Note that this might cause false failures in the specific case of using |
* a valid name with less than 16 characters, but 16 or more bytes, on a |
* system where `mb_strlen()` is unavailable. |
* |
* @param string $str |
* @param int $length |
* |
* @return bool |
*/ |
private function validateStringLength($str, $length) |
{ |
if (function_exists('mb_strlen')) { |
return (mb_strlen($str) <= $length); |
} |
|
return (strlen($str) <= $length); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php |
@@ -0,0 +1,69 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* IFTTTHandler uses cURL to trigger IFTTT Maker actions |
* |
* Register a secret key and trigger/event name at https://ifttt.com/maker |
* |
* value1 will be the channel from monolog's Logger constructor, |
* value2 will be the level name (ERROR, WARNING, ..) |
* value3 will be the log record's message |
* |
* @author Nehal Patel <nehal@nehalpatel.me> |
*/ |
class IFTTTHandler extends AbstractProcessingHandler |
{ |
private $eventName; |
private $secretKey; |
|
/** |
* @param string $eventName The name of the IFTTT Maker event that should be triggered |
* @param string $secretKey A valid IFTTT secret key |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true) |
{ |
$this->eventName = $eventName; |
$this->secretKey = $secretKey; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function write(array $record) |
{ |
$postData = array( |
"value1" => $record["channel"], |
"value2" => $record["level_name"], |
"value3" => $record["message"], |
); |
$postString = json_encode($postData); |
|
$ch = curl_init(); |
curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); |
curl_setopt($ch, CURLOPT_POST, true); |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
curl_setopt($ch, CURLOPT_POSTFIELDS, $postString); |
curl_setopt($ch, CURLOPT_HTTPHEADER, array( |
"Content-Type: application/json", |
)); |
|
Curl\Util::execute($ch); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php |
@@ -0,0 +1,102 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\LogglyFormatter; |
|
/** |
* Sends errors to Loggly. |
* |
* @author Przemek Sobstel <przemek@sobstel.org> |
* @author Adam Pancutt <adam@pancutt.com> |
* @author Gregory Barchard <gregory@barchard.net> |
*/ |
class LogglyHandler extends AbstractProcessingHandler |
{ |
const HOST = 'logs-01.loggly.com'; |
const ENDPOINT_SINGLE = 'inputs'; |
const ENDPOINT_BATCH = 'bulk'; |
|
protected $token; |
|
protected $tag = array(); |
|
public function __construct($token, $level = Logger::DEBUG, $bubble = true) |
{ |
if (!extension_loaded('curl')) { |
throw new \LogicException('The curl extension is needed to use the LogglyHandler'); |
} |
|
$this->token = $token; |
|
parent::__construct($level, $bubble); |
} |
|
public function setTag($tag) |
{ |
$tag = !empty($tag) ? $tag : array(); |
$this->tag = is_array($tag) ? $tag : array($tag); |
} |
|
public function addTag($tag) |
{ |
if (!empty($tag)) { |
$tag = is_array($tag) ? $tag : array($tag); |
$this->tag = array_unique(array_merge($this->tag, $tag)); |
} |
} |
|
protected function write(array $record) |
{ |
$this->send($record["formatted"], self::ENDPOINT_SINGLE); |
} |
|
public function handleBatch(array $records) |
{ |
$level = $this->level; |
|
$records = array_filter($records, function ($record) use ($level) { |
return ($record['level'] >= $level); |
}); |
|
if ($records) { |
$this->send($this->getFormatter()->formatBatch($records), self::ENDPOINT_BATCH); |
} |
} |
|
protected function send($data, $endpoint) |
{ |
$url = sprintf("https://%s/%s/%s/", self::HOST, $endpoint, $this->token); |
|
$headers = array('Content-Type: application/json'); |
|
if (!empty($this->tag)) { |
$headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag); |
} |
|
$ch = curl_init(); |
|
curl_setopt($ch, CURLOPT_URL, $url); |
curl_setopt($ch, CURLOPT_POST, true); |
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
|
Curl\Util::execute($ch); |
} |
|
protected function getDefaultFormatter() |
{ |
return new LogglyFormatter(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php |
@@ -0,0 +1,68 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* MandrillHandler uses cURL to send the emails to the Mandrill API |
* |
* @author Adam Nicholson <adamnicholson10@gmail.com> |
*/ |
class MandrillHandler extends MailHandler |
{ |
protected $message; |
protected $apiKey; |
|
/** |
* @param string $apiKey A valid Mandrill API key |
* @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
|
if (!$message instanceof \Swift_Message && is_callable($message)) { |
$message = call_user_func($message); |
} |
if (!$message instanceof \Swift_Message) { |
throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); |
} |
$this->message = $message; |
$this->apiKey = $apiKey; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function send($content, array $records) |
{ |
$message = clone $this->message; |
$message->setBody($content); |
$message->setDate(time()); |
|
$ch = curl_init(); |
|
curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); |
curl_setopt($ch, CURLOPT_POST, 1); |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( |
'key' => $this->apiKey, |
'raw_message' => (string) $message, |
'async' => false, |
))); |
|
Curl\Util::execute($ch); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php |
@@ -0,0 +1,185 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\LineFormatter; |
|
/** |
* NativeMailerHandler uses the mail() function to send the emails |
* |
* @author Christophe Coevoet <stof@notk.org> |
* @author Mark Garrett <mark@moderndeveloperllc.com> |
*/ |
class NativeMailerHandler extends MailHandler |
{ |
/** |
* The email addresses to which the message will be sent |
* @var array |
*/ |
protected $to; |
|
/** |
* The subject of the email |
* @var string |
*/ |
protected $subject; |
|
/** |
* Optional headers for the message |
* @var array |
*/ |
protected $headers = array(); |
|
/** |
* Optional parameters for the message |
* @var array |
*/ |
protected $parameters = array(); |
|
/** |
* The wordwrap length for the message |
* @var int |
*/ |
protected $maxColumnWidth; |
|
/** |
* The Content-type for the message |
* @var string |
*/ |
protected $contentType = 'text/plain'; |
|
/** |
* The encoding for the message |
* @var string |
*/ |
protected $encoding = 'utf-8'; |
|
/** |
* @param string|array $to The receiver of the mail |
* @param string $subject The subject of the mail |
* @param string $from The sender of the mail |
* @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 int $maxColumnWidth The maximum column width that the message lines will have |
*/ |
public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true, $maxColumnWidth = 70) |
{ |
parent::__construct($level, $bubble); |
$this->to = is_array($to) ? $to : array($to); |
$this->subject = $subject; |
$this->addHeader(sprintf('From: %s', $from)); |
$this->maxColumnWidth = $maxColumnWidth; |
} |
|
/** |
* Add headers to the message |
* |
* @param string|array $headers Custom added headers |
* @return self |
*/ |
public function addHeader($headers) |
{ |
foreach ((array) $headers as $header) { |
if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) { |
throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); |
} |
$this->headers[] = $header; |
} |
|
return $this; |
} |
|
/** |
* Add parameters to the message |
* |
* @param string|array $parameters Custom added parameters |
* @return self |
*/ |
public function addParameter($parameters) |
{ |
$this->parameters = array_merge($this->parameters, (array) $parameters); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function send($content, array $records) |
{ |
$content = wordwrap($content, $this->maxColumnWidth); |
$headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); |
$headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n"; |
if ($this->getContentType() == 'text/html' && false === strpos($headers, 'MIME-Version:')) { |
$headers .= 'MIME-Version: 1.0' . "\r\n"; |
} |
|
$subject = $this->subject; |
if ($records) { |
$subjectFormatter = new LineFormatter($this->subject); |
$subject = $subjectFormatter->format($this->getHighestRecord($records)); |
} |
|
$parameters = implode(' ', $this->parameters); |
foreach ($this->to as $to) { |
mail($to, $subject, $content, $headers, $parameters); |
} |
} |
|
/** |
* @return string $contentType |
*/ |
public function getContentType() |
{ |
return $this->contentType; |
} |
|
/** |
* @return string $encoding |
*/ |
public function getEncoding() |
{ |
return $this->encoding; |
} |
|
/** |
* @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML |
* messages. |
* @return self |
*/ |
public function setContentType($contentType) |
{ |
if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) { |
throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); |
} |
|
$this->contentType = $contentType; |
|
return $this; |
} |
|
/** |
* @param string $encoding |
* @return self |
*/ |
public function setEncoding($encoding) |
{ |
if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) { |
throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); |
} |
|
$this->encoding = $encoding; |
|
return $this; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php |
@@ -0,0 +1,202 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\NormalizerFormatter; |
|
/** |
* Class to record a log on a NewRelic application. |
* Enabling New Relic High Security mode may prevent capture of useful information. |
* |
* @see https://docs.newrelic.com/docs/agents/php-agent |
* @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security |
*/ |
class NewRelicHandler extends AbstractProcessingHandler |
{ |
/** |
* Name of the New Relic application that will receive logs from this handler. |
* |
* @var string |
*/ |
protected $appName; |
|
/** |
* Name of the current transaction |
* |
* @var string |
*/ |
protected $transactionName; |
|
/** |
* Some context and extra data is passed into the handler as arrays of values. Do we send them as is |
* (useful if we are using the API), or explode them for display on the NewRelic RPM website? |
* |
* @var bool |
*/ |
protected $explodeArrays; |
|
/** |
* {@inheritDoc} |
* |
* @param string $appName |
* @param bool $explodeArrays |
* @param string $transactionName |
*/ |
public function __construct( |
$level = Logger::ERROR, |
$bubble = true, |
$appName = null, |
$explodeArrays = false, |
$transactionName = null |
) { |
parent::__construct($level, $bubble); |
|
$this->appName = $appName; |
$this->explodeArrays = $explodeArrays; |
$this->transactionName = $transactionName; |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function write(array $record) |
{ |
if (!$this->isNewRelicEnabled()) { |
throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); |
} |
|
if ($appName = $this->getAppName($record['context'])) { |
$this->setNewRelicAppName($appName); |
} |
|
if ($transactionName = $this->getTransactionName($record['context'])) { |
$this->setNewRelicTransactionName($transactionName); |
unset($record['formatted']['context']['transaction_name']); |
} |
|
if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { |
newrelic_notice_error($record['message'], $record['context']['exception']); |
unset($record['formatted']['context']['exception']); |
} else { |
newrelic_notice_error($record['message']); |
} |
|
if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) { |
foreach ($record['formatted']['context'] as $key => $parameter) { |
if (is_array($parameter) && $this->explodeArrays) { |
foreach ($parameter as $paramKey => $paramValue) { |
$this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); |
} |
} else { |
$this->setNewRelicParameter('context_' . $key, $parameter); |
} |
} |
} |
|
if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) { |
foreach ($record['formatted']['extra'] as $key => $parameter) { |
if (is_array($parameter) && $this->explodeArrays) { |
foreach ($parameter as $paramKey => $paramValue) { |
$this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); |
} |
} else { |
$this->setNewRelicParameter('extra_' . $key, $parameter); |
} |
} |
} |
} |
|
/** |
* Checks whether the NewRelic extension is enabled in the system. |
* |
* @return bool |
*/ |
protected function isNewRelicEnabled() |
{ |
return extension_loaded('newrelic'); |
} |
|
/** |
* Returns the appname where this log should be sent. Each log can override the default appname, set in this |
* handler's constructor, by providing the appname in it's context. |
* |
* @param array $context |
* @return null|string |
*/ |
protected function getAppName(array $context) |
{ |
if (isset($context['appname'])) { |
return $context['appname']; |
} |
|
return $this->appName; |
} |
|
/** |
* Returns the name of the current transaction. Each log can override the default transaction name, set in this |
* handler's constructor, by providing the transaction_name in it's context |
* |
* @param array $context |
* |
* @return null|string |
*/ |
protected function getTransactionName(array $context) |
{ |
if (isset($context['transaction_name'])) { |
return $context['transaction_name']; |
} |
|
return $this->transactionName; |
} |
|
/** |
* Sets the NewRelic application that should receive this log. |
* |
* @param string $appName |
*/ |
protected function setNewRelicAppName($appName) |
{ |
newrelic_set_appname($appName); |
} |
|
/** |
* Overwrites the name of the current transaction |
* |
* @param string $transactionName |
*/ |
protected function setNewRelicTransactionName($transactionName) |
{ |
newrelic_name_transaction($transactionName); |
} |
|
/** |
* @param string $key |
* @param mixed $value |
*/ |
protected function setNewRelicParameter($key, $value) |
{ |
if (null === $value || is_scalar($value)) { |
newrelic_add_custom_parameter($key, $value); |
} else { |
newrelic_add_custom_parameter($key, @json_encode($value)); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new NormalizerFormatter(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php |
@@ -0,0 +1,242 @@ |
<?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; |
|
use Exception; |
use Monolog\Formatter\LineFormatter; |
use Monolog\Logger; |
use PhpConsole\Connector; |
use PhpConsole\Handler; |
use PhpConsole\Helper; |
|
/** |
* Monolog handler for Google Chrome extension "PHP Console" |
* |
* Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely |
* |
* Usage: |
* 1. Install Google Chrome extension https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef |
* 2. See overview https://github.com/barbushin/php-console#overview |
* 3. Install PHP Console library https://github.com/barbushin/php-console#installation |
* 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) |
* |
* $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); |
* \Monolog\ErrorHandler::register($logger); |
* echo $undefinedVar; |
* $logger->addDebug('SELECT * FROM users', array('db', 'time' => 0.012)); |
* PC::debug($_SERVER); // PHP Console debugger for any type of vars |
* |
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin |
*/ |
class PHPConsoleHandler extends AbstractProcessingHandler |
{ |
private $options = array( |
'enabled' => true, // bool Is PHP Console server enabled |
'classesPartialsTraceIgnore' => array('Monolog\\'), // array Hide calls of classes started with... |
'debugTagsKeysInContext' => array(0, 'tag'), // bool Is PHP Console server enabled |
'useOwnErrorsHandler' => false, // bool Enable errors handling |
'useOwnExceptionsHandler' => false, // bool Enable exceptions handling |
'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths |
'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') |
'serverEncoding' => null, // string|null Server internal encoding |
'headersLimit' => null, // int|null Set headers size limit for your web-server |
'password' => null, // string|null Protect PHP Console connection by password |
'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed |
'ipMasks' => array(), // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') |
'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) |
'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings |
'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level |
'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number |
'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item |
'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON |
'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug |
'dataStorage' => null, // PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) |
); |
|
/** @var Connector */ |
private $connector; |
|
/** |
* @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details |
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) |
* @param int $level |
* @param bool $bubble |
* @throws Exception |
*/ |
public function __construct(array $options = array(), Connector $connector = null, $level = Logger::DEBUG, $bubble = true) |
{ |
if (!class_exists('PhpConsole\Connector')) { |
throw new Exception('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); |
} |
parent::__construct($level, $bubble); |
$this->options = $this->initOptions($options); |
$this->connector = $this->initConnector($connector); |
} |
|
private function initOptions(array $options) |
{ |
$wrongOptions = array_diff(array_keys($options), array_keys($this->options)); |
if ($wrongOptions) { |
throw new Exception('Unknown options: ' . implode(', ', $wrongOptions)); |
} |
|
return array_replace($this->options, $options); |
} |
|
private function initConnector(Connector $connector = null) |
{ |
if (!$connector) { |
if ($this->options['dataStorage']) { |
Connector::setPostponeStorage($this->options['dataStorage']); |
} |
$connector = Connector::getInstance(); |
} |
|
if ($this->options['registerHelper'] && !Helper::isRegistered()) { |
Helper::register(); |
} |
|
if ($this->options['enabled'] && $connector->isActiveClient()) { |
if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { |
$handler = Handler::getInstance(); |
$handler->setHandleErrors($this->options['useOwnErrorsHandler']); |
$handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); |
$handler->start(); |
} |
if ($this->options['sourcesBasePath']) { |
$connector->setSourcesBasePath($this->options['sourcesBasePath']); |
} |
if ($this->options['serverEncoding']) { |
$connector->setServerEncoding($this->options['serverEncoding']); |
} |
if ($this->options['password']) { |
$connector->setPassword($this->options['password']); |
} |
if ($this->options['enableSslOnlyMode']) { |
$connector->enableSslOnlyMode(); |
} |
if ($this->options['ipMasks']) { |
$connector->setAllowedIpMasks($this->options['ipMasks']); |
} |
if ($this->options['headersLimit']) { |
$connector->setHeadersLimit($this->options['headersLimit']); |
} |
if ($this->options['detectDumpTraceAndSource']) { |
$connector->getDebugDispatcher()->detectTraceAndSource = true; |
} |
$dumper = $connector->getDumper(); |
$dumper->levelLimit = $this->options['dumperLevelLimit']; |
$dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; |
$dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; |
$dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; |
$dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; |
if ($this->options['enableEvalListener']) { |
$connector->startEvalRequestsListener(); |
} |
} |
|
return $connector; |
} |
|
public function getConnector() |
{ |
return $this->connector; |
} |
|
public function getOptions() |
{ |
return $this->options; |
} |
|
public function handle(array $record) |
{ |
if ($this->options['enabled'] && $this->connector->isActiveClient()) { |
return parent::handle($record); |
} |
|
return !$this->bubble; |
} |
|
/** |
* Writes the record down to the log of the implementing handler |
* |
* @param array $record |
* @return void |
*/ |
protected function write(array $record) |
{ |
if ($record['level'] < Logger::NOTICE) { |
$this->handleDebugRecord($record); |
} elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof Exception) { |
$this->handleExceptionRecord($record); |
} else { |
$this->handleErrorRecord($record); |
} |
} |
|
private function handleDebugRecord(array $record) |
{ |
$tags = $this->getRecordTags($record); |
$message = $record['message']; |
if ($record['context']) { |
$message .= ' ' . json_encode($this->connector->getDumper()->dump(array_filter($record['context']))); |
} |
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); |
} |
|
private function handleExceptionRecord(array $record) |
{ |
$this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); |
} |
|
private function handleErrorRecord(array $record) |
{ |
$context = $record['context']; |
|
$this->connector->getErrorsDispatcher()->dispatchError( |
isset($context['code']) ? $context['code'] : null, |
isset($context['message']) ? $context['message'] : $record['message'], |
isset($context['file']) ? $context['file'] : null, |
isset($context['line']) ? $context['line'] : null, |
$this->options['classesPartialsTraceIgnore'] |
); |
} |
|
private function getRecordTags(array &$record) |
{ |
$tags = null; |
if (!empty($record['context'])) { |
$context = & $record['context']; |
foreach ($this->options['debugTagsKeysInContext'] as $key) { |
if (!empty($context[$key])) { |
$tags = $context[$key]; |
if ($key === 0) { |
array_shift($context); |
} else { |
unset($context[$key]); |
} |
break; |
} |
} |
} |
|
return $tags ?: strtolower($record['level_name']); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('%message%'); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php |
@@ -0,0 +1,185 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Sends notifications through the pushover api to mobile phones |
* |
* @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com> |
* @see https://www.pushover.net/api |
*/ |
class PushoverHandler extends SocketHandler |
{ |
private $token; |
private $users; |
private $title; |
private $user; |
private $retry; |
private $expire; |
|
private $highPriorityLevel; |
private $emergencyLevel; |
private $useFormattedMessage = false; |
|
/** |
* All parameters that can be sent to Pushover |
* @see https://pushover.net/api |
* @var array |
*/ |
private $parameterNames = array( |
'token' => true, |
'user' => true, |
'message' => true, |
'device' => true, |
'title' => true, |
'url' => true, |
'url_title' => true, |
'priority' => true, |
'timestamp' => true, |
'sound' => true, |
'retry' => true, |
'expire' => true, |
'callback' => true, |
); |
|
/** |
* Sounds the api supports by default |
* @see https://pushover.net/api#sounds |
* @var array |
*/ |
private $sounds = array( |
'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', |
'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', |
'persistent', 'echo', 'updown', 'none', |
); |
|
/** |
* @param string $token Pushover api token |
* @param string|array $users Pushover user id or array of ids the message will be sent to |
* @param string $title Title sent to the Pushover API |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not |
* the pushover.net app owner. OpenSSL is required for this option. |
* @param int $highPriorityLevel The minimum logging level at which this handler will start |
* sending "high priority" requests to the Pushover API |
* @param int $emergencyLevel The minimum logging level at which this handler will start |
* sending "emergency" requests to the Pushover API |
* @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user. |
* @param int $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds). |
*/ |
public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200) |
{ |
$connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; |
parent::__construct($connectionString, $level, $bubble); |
|
$this->token = $token; |
$this->users = (array) $users; |
$this->title = $title ?: gethostname(); |
$this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); |
$this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); |
$this->retry = $retry; |
$this->expire = $expire; |
} |
|
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
private function buildContent($record) |
{ |
// Pushover has a limit of 512 characters on title and message combined. |
$maxMessageLength = 512 - strlen($this->title); |
|
$message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message']; |
$message = substr($message, 0, $maxMessageLength); |
|
$timestamp = $record['datetime']->getTimestamp(); |
|
$dataArray = array( |
'token' => $this->token, |
'user' => $this->user, |
'message' => $message, |
'title' => $this->title, |
'timestamp' => $timestamp, |
); |
|
if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { |
$dataArray['priority'] = 2; |
$dataArray['retry'] = $this->retry; |
$dataArray['expire'] = $this->expire; |
} elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { |
$dataArray['priority'] = 1; |
} |
|
// First determine the available parameters |
$context = array_intersect_key($record['context'], $this->parameterNames); |
$extra = array_intersect_key($record['extra'], $this->parameterNames); |
|
// Least important info should be merged with subsequent info |
$dataArray = array_merge($extra, $context, $dataArray); |
|
// Only pass sounds that are supported by the API |
if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) { |
unset($dataArray['sound']); |
} |
|
return http_build_query($dataArray); |
} |
|
private function buildHeader($content) |
{ |
$header = "POST /1/messages.json HTTP/1.1\r\n"; |
$header .= "Host: api.pushover.net\r\n"; |
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
|
protected function write(array $record) |
{ |
foreach ($this->users as $user) { |
$this->user = $user; |
|
parent::write($record); |
$this->closeSocket(); |
} |
|
$this->user = null; |
} |
|
public function setHighPriorityLevel($value) |
{ |
$this->highPriorityLevel = $value; |
} |
|
public function setEmergencyLevel($value) |
{ |
$this->emergencyLevel = $value; |
} |
|
/** |
* Use the formatted message? |
* @param bool $value |
*/ |
public function useFormattedMessage($value) |
{ |
$this->useFormattedMessage = (boolean) $value; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php |
@@ -0,0 +1,232 @@ |
<?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; |
|
use Monolog\Formatter\LineFormatter; |
use Monolog\Formatter\FormatterInterface; |
use Monolog\Logger; |
use Raven_Client; |
|
/** |
* Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server |
* using raven-php (https://github.com/getsentry/raven-php) |
* |
* @author Marc Abramowitz <marc@marc-abramowitz.com> |
*/ |
class RavenHandler extends AbstractProcessingHandler |
{ |
/** |
* Translates Monolog log levels to Raven log levels. |
*/ |
private $logLevels = array( |
Logger::DEBUG => Raven_Client::DEBUG, |
Logger::INFO => Raven_Client::INFO, |
Logger::NOTICE => Raven_Client::INFO, |
Logger::WARNING => Raven_Client::WARNING, |
Logger::ERROR => Raven_Client::ERROR, |
Logger::CRITICAL => Raven_Client::FATAL, |
Logger::ALERT => Raven_Client::FATAL, |
Logger::EMERGENCY => Raven_Client::FATAL, |
); |
|
/** |
* @var string should represent the current version of the calling |
* software. Can be any string (git commit, version number) |
*/ |
private $release; |
|
/** |
* @var Raven_Client the client object that sends the message to the server |
*/ |
protected $ravenClient; |
|
/** |
* @var LineFormatter The formatter to use for the logs generated via handleBatch() |
*/ |
protected $batchFormatter; |
|
/** |
* @param Raven_Client $ravenClient |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
|
$this->ravenClient = $ravenClient; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
$level = $this->level; |
|
// filter records based on their level |
$records = array_filter($records, function ($record) use ($level) { |
return $record['level'] >= $level; |
}); |
|
if (!$records) { |
return; |
} |
|
// the record with the highest severity is the "main" one |
$record = array_reduce($records, function ($highest, $record) { |
if ($record['level'] > $highest['level']) { |
return $record; |
} |
|
return $highest; |
}); |
|
// the other ones are added as a context item |
$logs = array(); |
foreach ($records as $r) { |
$logs[] = $this->processRecord($r); |
} |
|
if ($logs) { |
$record['context']['logs'] = (string) $this->getBatchFormatter()->formatBatch($logs); |
} |
|
$this->handle($record); |
} |
|
/** |
* Sets the formatter for the logs generated by handleBatch(). |
* |
* @param FormatterInterface $formatter |
*/ |
public function setBatchFormatter(FormatterInterface $formatter) |
{ |
$this->batchFormatter = $formatter; |
} |
|
/** |
* Gets the formatter for the logs generated by handleBatch(). |
* |
* @return FormatterInterface |
*/ |
public function getBatchFormatter() |
{ |
if (!$this->batchFormatter) { |
$this->batchFormatter = $this->getDefaultBatchFormatter(); |
} |
|
return $this->batchFormatter; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
$previousUserContext = false; |
$options = array(); |
$options['level'] = $this->logLevels[$record['level']]; |
$options['tags'] = array(); |
if (!empty($record['extra']['tags'])) { |
$options['tags'] = array_merge($options['tags'], $record['extra']['tags']); |
unset($record['extra']['tags']); |
} |
if (!empty($record['context']['tags'])) { |
$options['tags'] = array_merge($options['tags'], $record['context']['tags']); |
unset($record['context']['tags']); |
} |
if (!empty($record['context']['fingerprint'])) { |
$options['fingerprint'] = $record['context']['fingerprint']; |
unset($record['context']['fingerprint']); |
} |
if (!empty($record['context']['logger'])) { |
$options['logger'] = $record['context']['logger']; |
unset($record['context']['logger']); |
} else { |
$options['logger'] = $record['channel']; |
} |
foreach ($this->getExtraParameters() as $key) { |
foreach (array('extra', 'context') as $source) { |
if (!empty($record[$source][$key])) { |
$options[$key] = $record[$source][$key]; |
unset($record[$source][$key]); |
} |
} |
} |
if (!empty($record['context'])) { |
$options['extra']['context'] = $record['context']; |
if (!empty($record['context']['user'])) { |
$previousUserContext = $this->ravenClient->context->user; |
$this->ravenClient->user_context($record['context']['user']); |
unset($options['extra']['context']['user']); |
} |
} |
if (!empty($record['extra'])) { |
$options['extra']['extra'] = $record['extra']; |
} |
|
if (!empty($this->release) && !isset($options['release'])) { |
$options['release'] = $this->release; |
} |
|
if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { |
$options['extra']['message'] = $record['formatted']; |
$this->ravenClient->captureException($record['context']['exception'], $options); |
} else { |
$this->ravenClient->captureMessage($record['formatted'], array(), $options); |
} |
|
if ($previousUserContext !== false) { |
$this->ravenClient->user_context($previousUserContext); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('[%channel%] %message%'); |
} |
|
/** |
* Gets the default formatter for the logs generated by handleBatch(). |
* |
* @return FormatterInterface |
*/ |
protected function getDefaultBatchFormatter() |
{ |
return new LineFormatter(); |
} |
|
/** |
* Gets extra parameters supported by Raven that can be found in "extra" and "context" |
* |
* @return array |
*/ |
protected function getExtraParameters() |
{ |
return array('checksum', 'release', 'event_id'); |
} |
|
/** |
* @param string $value |
* @return self |
*/ |
public function setRelease($value) |
{ |
$this->release = $value; |
|
return $this; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php |
@@ -0,0 +1,97 @@ |
<?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; |
|
use Monolog\Formatter\LineFormatter; |
use Monolog\Logger; |
|
/** |
* Logs to a Redis key using rpush |
* |
* usage example: |
* |
* $log = new Logger('application'); |
* $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); |
* $log->pushHandler($redis); |
* |
* @author Thomas Tourlourat <thomas@tourlourat.com> |
*/ |
class RedisHandler extends AbstractProcessingHandler |
{ |
private $redisClient; |
private $redisKey; |
protected $capSize; |
|
/** |
* @param \Predis\Client|\Redis $redis The redis instance |
* @param string $key The key name to push records to |
* @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 int $capSize Number of entries to limit list size to |
*/ |
public function __construct($redis, $key, $level = Logger::DEBUG, $bubble = true, $capSize = false) |
{ |
if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { |
throw new \InvalidArgumentException('Predis\Client or Redis instance required'); |
} |
|
$this->redisClient = $redis; |
$this->redisKey = $key; |
$this->capSize = $capSize; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function write(array $record) |
{ |
if ($this->capSize) { |
$this->writeCapped($record); |
} else { |
$this->redisClient->rpush($this->redisKey, $record["formatted"]); |
} |
} |
|
/** |
* Write and cap the collection |
* Writes the record to the redis list and caps its |
* |
* @param array $record associative record array |
* @return void |
*/ |
protected function writeCapped(array $record) |
{ |
if ($this->redisClient instanceof \Redis) { |
$this->redisClient->multi() |
->rpush($this->redisKey, $record["formatted"]) |
->ltrim($this->redisKey, -$this->capSize, -1) |
->exec(); |
} else { |
$redisKey = $this->redisKey; |
$capSize = $this->capSize; |
$this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { |
$tx->rpush($redisKey, $record["formatted"]); |
$tx->ltrim($redisKey, -$capSize, -1); |
}); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php |
@@ -0,0 +1,132 @@ |
<?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; |
|
use RollbarNotifier; |
use Exception; |
use Monolog\Logger; |
|
/** |
* Sends errors to Rollbar |
* |
* If the context data contains a `payload` key, that is used as an array |
* of payload options to RollbarNotifier's report_message/report_exception methods. |
* |
* Rollbar's context info will contain the context + extra keys from the log record |
* merged, and then on top of that a few keys: |
* |
* - level (rollbar level name) |
* - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) |
* - channel |
* - datetime (unix timestamp) |
* |
* @author Paul Statezny <paulstatezny@gmail.com> |
*/ |
class RollbarHandler extends AbstractProcessingHandler |
{ |
/** |
* Rollbar notifier |
* |
* @var RollbarNotifier |
*/ |
protected $rollbarNotifier; |
|
protected $levelMap = array( |
Logger::DEBUG => 'debug', |
Logger::INFO => 'info', |
Logger::NOTICE => 'info', |
Logger::WARNING => 'warning', |
Logger::ERROR => 'error', |
Logger::CRITICAL => 'critical', |
Logger::ALERT => 'critical', |
Logger::EMERGENCY => 'critical', |
); |
|
/** |
* Records whether any log records have been added since the last flush of the rollbar notifier |
* |
* @var bool |
*/ |
private $hasRecords = false; |
|
protected $initialized = false; |
|
/** |
* @param RollbarNotifier $rollbarNotifier RollbarNotifier object constructed with valid token |
* @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 |
*/ |
public function __construct(RollbarNotifier $rollbarNotifier, $level = Logger::ERROR, $bubble = true) |
{ |
$this->rollbarNotifier = $rollbarNotifier; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
if (!$this->initialized) { |
// __destructor() doesn't get called on Fatal errors |
register_shutdown_function(array($this, 'close')); |
$this->initialized = true; |
} |
|
$context = $record['context']; |
$payload = array(); |
if (isset($context['payload'])) { |
$payload = $context['payload']; |
unset($context['payload']); |
} |
$context = array_merge($context, $record['extra'], array( |
'level' => $this->levelMap[$record['level']], |
'monolog_level' => $record['level_name'], |
'channel' => $record['channel'], |
'datetime' => $record['datetime']->format('U'), |
)); |
|
if (isset($context['exception']) && $context['exception'] instanceof Exception) { |
$payload['level'] = $context['level']; |
$exception = $context['exception']; |
unset($context['exception']); |
|
$this->rollbarNotifier->report_exception($exception, $context, $payload); |
} else { |
$this->rollbarNotifier->report_message( |
$record['message'], |
$context['level'], |
$context, |
$payload |
); |
} |
|
$this->hasRecords = true; |
} |
|
public function flush() |
{ |
if ($this->hasRecords) { |
$this->rollbarNotifier->flush(); |
$this->hasRecords = false; |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
$this->flush(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php |
@@ -0,0 +1,178 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Stores logs to files that are rotated every day and a limited number of files are kept. |
* |
* This rotation is only intended to be used as a workaround. Using logrotate to |
* handle the rotation is strongly encouraged when you can use it. |
* |
* @author Christophe Coevoet <stof@notk.org> |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class RotatingFileHandler extends StreamHandler |
{ |
const FILE_PER_DAY = 'Y-m-d'; |
const FILE_PER_MONTH = 'Y-m'; |
const FILE_PER_YEAR = 'Y'; |
|
protected $filename; |
protected $maxFiles; |
protected $mustRotate; |
protected $nextRotation; |
protected $filenameFormat; |
protected $dateFormat; |
|
/** |
* @param string $filename |
* @param int $maxFiles The maximal amount of files to keep (0 means unlimited) |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) |
* @param Boolean $useLocking Try to lock log file before doing any writes |
*/ |
public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) |
{ |
$this->filename = $filename; |
$this->maxFiles = (int) $maxFiles; |
$this->nextRotation = new \DateTime('tomorrow'); |
$this->filenameFormat = '{filename}-{date}'; |
$this->dateFormat = 'Y-m-d'; |
|
parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
parent::close(); |
|
if (true === $this->mustRotate) { |
$this->rotate(); |
} |
} |
|
public function setFilenameFormat($filenameFormat, $dateFormat) |
{ |
if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { |
trigger_error( |
'Invalid date format - format must be one of '. |
'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '. |
'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '. |
'date formats using slashes, underscores and/or dots instead of dashes.', |
E_USER_DEPRECATED |
); |
} |
if (substr_count($filenameFormat, '{date}') === 0) { |
trigger_error( |
'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.', |
E_USER_DEPRECATED |
); |
} |
$this->filenameFormat = $filenameFormat; |
$this->dateFormat = $dateFormat; |
$this->url = $this->getTimedFilename(); |
$this->close(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
// on the first record written, if the log is new, we should rotate (once per day) |
if (null === $this->mustRotate) { |
$this->mustRotate = !file_exists($this->url); |
} |
|
if ($this->nextRotation < $record['datetime']) { |
$this->mustRotate = true; |
$this->close(); |
} |
|
parent::write($record); |
} |
|
/** |
* Rotates the files. |
*/ |
protected function rotate() |
{ |
// update filename |
$this->url = $this->getTimedFilename(); |
$this->nextRotation = new \DateTime('tomorrow'); |
|
// skip GC of old logs if files are unlimited |
if (0 === $this->maxFiles) { |
return; |
} |
|
$logFiles = glob($this->getGlobPattern()); |
if ($this->maxFiles >= count($logFiles)) { |
// no files to remove |
return; |
} |
|
// Sorting the files by name to remove the older ones |
usort($logFiles, function ($a, $b) { |
return strcmp($b, $a); |
}); |
|
foreach (array_slice($logFiles, $this->maxFiles) as $file) { |
if (is_writable($file)) { |
// suppress errors here as unlink() might fail if two processes |
// are cleaning up/rotating at the same time |
set_error_handler(function ($errno, $errstr, $errfile, $errline) {}); |
unlink($file); |
restore_error_handler(); |
} |
} |
|
$this->mustRotate = false; |
} |
|
protected function getTimedFilename() |
{ |
$fileInfo = pathinfo($this->filename); |
$timedFilename = str_replace( |
array('{filename}', '{date}'), |
array($fileInfo['filename'], date($this->dateFormat)), |
$fileInfo['dirname'] . '/' . $this->filenameFormat |
); |
|
if (!empty($fileInfo['extension'])) { |
$timedFilename .= '.'.$fileInfo['extension']; |
} |
|
return $timedFilename; |
} |
|
protected function getGlobPattern() |
{ |
$fileInfo = pathinfo($this->filename); |
$glob = str_replace( |
array('{filename}', '{date}'), |
array($fileInfo['filename'], '*'), |
$fileInfo['dirname'] . '/' . $this->filenameFormat |
); |
if (!empty($fileInfo['extension'])) { |
$glob .= '.'.$fileInfo['extension']; |
} |
|
return $glob; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php |
@@ -0,0 +1,82 @@ |
<?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; |
|
/** |
* Sampling handler |
* |
* A sampled event stream can be useful for logging high frequency events in |
* a production environment where you only need an idea of what is happening |
* and are not concerned with capturing every occurrence. Since the decision to |
* handle or not handle a particular event is determined randomly, the |
* resulting sampled log is not guaranteed to contain 1/N of the events that |
* occurred in the application, but based on the Law of large numbers, it will |
* tend to be close to this ratio with a large number of attempts. |
* |
* @author Bryan Davis <bd808@wikimedia.org> |
* @author Kunal Mehta <legoktm@gmail.com> |
*/ |
class SamplingHandler extends AbstractHandler |
{ |
/** |
* @var callable|HandlerInterface $handler |
*/ |
protected $handler; |
|
/** |
* @var int $factor |
*/ |
protected $factor; |
|
/** |
* @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). |
* @param int $factor Sample factor |
*/ |
public function __construct($handler, $factor) |
{ |
parent::__construct(); |
$this->handler = $handler; |
$this->factor = $factor; |
|
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { |
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); |
} |
} |
|
public function isHandling(array $record) |
{ |
return $this->handler->isHandling($record); |
} |
|
public function handle(array $record) |
{ |
if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { |
// The same logic as in FingersCrossedHandler |
if (!$this->handler instanceof HandlerInterface) { |
$this->handler = call_user_func($this->handler, $record, $this); |
if (!$this->handler instanceof HandlerInterface) { |
throw new \RuntimeException("The factory callable should return a HandlerInterface"); |
} |
} |
|
if ($this->processors) { |
foreach ($this->processors as $processor) { |
$record = call_user_func($processor, $record); |
} |
} |
|
$this->handler->handle($record); |
} |
|
return false === $this->bubble; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php |
@@ -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; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php |
@@ -0,0 +1,215 @@ |
<?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; |
|
use Monolog\Formatter\FormatterInterface; |
use Monolog\Logger; |
use Monolog\Handler\Slack\SlackRecord; |
|
/** |
* Sends notifications through Slack API |
* |
* @author Greg Kedzierski <greg@gregkedzierski.com> |
* @see https://api.slack.com/ |
*/ |
class SlackHandler extends SocketHandler |
{ |
/** |
* Slack API token |
* @var string |
*/ |
private $token; |
|
/** |
* Instance of the SlackRecord util class preparing data for Slack API. |
* @var SlackRecord |
*/ |
private $slackRecord; |
|
/** |
* @param string $token Slack API token |
* @param string $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 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 bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style |
* @param bool $includeContextAndExtra Whether the attachment should include context and extra data |
* @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] |
* @throws MissingExtensionException If no OpenSSL PHP extension configured |
*/ |
public function __construct($token, $channel, $username = null, $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array()) |
{ |
if (!extension_loaded('openssl')) { |
throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); |
} |
|
parent::__construct('ssl://slack.com:443', $level, $bubble); |
|
$this->slackRecord = new SlackRecord( |
$channel, |
$username, |
$useAttachment, |
$iconEmoji, |
$useShortAttachment, |
$includeContextAndExtra, |
$excludeFields, |
$this->formatter |
); |
|
$this->token = $token; |
} |
|
public function getSlackRecord() |
{ |
return $this->slackRecord; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
* @return string |
*/ |
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
/** |
* Builds the body of API call |
* |
* @param array $record |
* @return string |
*/ |
private function buildContent($record) |
{ |
$dataArray = $this->prepareContentData($record); |
|
return http_build_query($dataArray); |
} |
|
/** |
* Prepares content data |
* |
* @param array $record |
* @return array |
*/ |
protected function prepareContentData($record) |
{ |
$dataArray = $this->slackRecord->getSlackData($record); |
$dataArray['token'] = $this->token; |
|
if (!empty($dataArray['attachments'])) { |
$dataArray['attachments'] = json_encode($dataArray['attachments']); |
} |
|
return $dataArray; |
} |
|
/** |
* Builds the header of the API Call |
* |
* @param string $content |
* @return string |
*/ |
private function buildHeader($content) |
{ |
$header = "POST /api/chat.postMessage HTTP/1.1\r\n"; |
$header .= "Host: slack.com\r\n"; |
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
parent::write($record); |
$this->finalizeWrite(); |
} |
|
/** |
* Finalizes the request by reading some bytes and then closing the socket |
* |
* If we do not read some but close the socket too early, slack sometimes |
* drops the request entirely. |
*/ |
protected function finalizeWrite() |
{ |
$res = $this->getResource(); |
if (is_resource($res)) { |
@fread($res, 2048); |
} |
$this->closeSocket(); |
} |
|
/** |
* Returned a Slack message attachment color associated with |
* provided level. |
* |
* @param int $level |
* @return string |
* @deprecated Use underlying SlackRecord instead |
*/ |
protected function getAttachmentColor($level) |
{ |
trigger_error( |
'SlackHandler::getAttachmentColor() is deprecated. Use underlying SlackRecord instead.', |
E_USER_DEPRECATED |
); |
|
return $this->slackRecord->getAttachmentColor($level); |
} |
|
/** |
* Stringifies an array of key/value pairs to be used in attachment fields |
* |
* @param array $fields |
* @return string |
* @deprecated Use underlying SlackRecord instead |
*/ |
protected function stringify($fields) |
{ |
trigger_error( |
'SlackHandler::stringify() is deprecated. Use underlying SlackRecord instead.', |
E_USER_DEPRECATED |
); |
|
return $this->slackRecord->stringify($fields); |
} |
|
public function setFormatter(FormatterInterface $formatter) |
{ |
parent::setFormatter($formatter); |
$this->slackRecord->setFormatter($formatter); |
|
return $this; |
} |
|
public function getFormatter() |
{ |
$formatter = parent::getFormatter(); |
$this->slackRecord->setFormatter($formatter); |
|
return $formatter; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php |
@@ -0,0 +1,115 @@ |
<?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; |
|
use Monolog\Formatter\FormatterInterface; |
use Monolog\Logger; |
use Monolog\Handler\Slack\SlackRecord; |
|
/** |
* Sends notifications through Slack Webhooks |
* |
* @author Haralan Dobrev <hkdobrev@gmail.com> |
* @see https://api.slack.com/incoming-webhooks |
*/ |
class SlackWebhookHandler extends AbstractProcessingHandler |
{ |
/** |
* Slack Webhook token |
* @var string |
*/ |
private $webhookUrl; |
|
/** |
* Instance of the SlackRecord util class preparing data for Slack API. |
* @var SlackRecord |
*/ |
private $slackRecord; |
|
/** |
* @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 $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style |
* @param bool $includeContextAndExtra Whether the attachment should include context and extra data |
* @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, $useShortAttachment = false, $includeContextAndExtra = false, $level = Logger::CRITICAL, $bubble = true, array $excludeFields = array()) |
{ |
parent::__construct($level, $bubble); |
|
$this->webhookUrl = $webhookUrl; |
|
$this->slackRecord = new SlackRecord( |
$channel, |
$username, |
$useAttachment, |
$iconEmoji, |
$useShortAttachment, |
$includeContextAndExtra, |
$excludeFields, |
$this->formatter |
); |
} |
|
public function getSlackRecord() |
{ |
return $this->slackRecord; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
$postData = $this->slackRecord->getSlackData($record); |
$postString = json_encode($postData); |
|
$ch = curl_init(); |
$options = array( |
CURLOPT_URL => $this->webhookUrl, |
CURLOPT_POST => true, |
CURLOPT_RETURNTRANSFER => true, |
CURLOPT_HTTPHEADER => array('Content-type: application/json'), |
CURLOPT_POSTFIELDS => $postString |
); |
if (defined('CURLOPT_SAFE_UPLOAD')) { |
$options[CURLOPT_SAFE_UPLOAD] = true; |
} |
|
curl_setopt_array($ch, $options); |
|
Curl\Util::execute($ch); |
} |
|
public function setFormatter(FormatterInterface $formatter) |
{ |
parent::setFormatter($formatter); |
$this->slackRecord->setFormatter($formatter); |
|
return $this; |
} |
|
public function getFormatter() |
{ |
$formatter = parent::getFormatter(); |
$this->slackRecord->setFormatter($formatter); |
|
return $formatter; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SlackbotHandler.php |
@@ -0,0 +1,80 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Sends notifications through Slack's Slackbot |
* |
* @author Haralan Dobrev <hkdobrev@gmail.com> |
* @see https://slack.com/apps/A0F81R8ET-slackbot |
*/ |
class SlackbotHandler extends AbstractProcessingHandler |
{ |
/** |
* The slug of the Slack team |
* @var string |
*/ |
private $slackTeam; |
|
/** |
* Slackbot token |
* @var string |
*/ |
private $token; |
|
/** |
* Slack channel name |
* @var string |
*/ |
private $channel; |
|
/** |
* @param string $slackTeam Slack team slug |
* @param string $token Slackbot token |
* @param string $channel Slack channel (encoded ID or name) |
* @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 |
*/ |
public function __construct($slackTeam, $token, $channel, $level = Logger::CRITICAL, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
|
$this->slackTeam = $slackTeam; |
$this->token = $token; |
$this->channel = $channel; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
$slackbotUrl = sprintf( |
'https://%s.slack.com/services/hooks/slackbot?token=%s&channel=%s', |
$this->slackTeam, |
$this->token, |
$this->channel |
); |
|
$ch = curl_init(); |
curl_setopt($ch, CURLOPT_URL, $slackbotUrl); |
curl_setopt($ch, CURLOPT_POST, true); |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
curl_setopt($ch, CURLOPT_POSTFIELDS, $record['message']); |
|
Curl\Util::execute($ch); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php |
@@ -0,0 +1,346 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Stores to any socket - uses fsockopen() or pfsockopen(). |
* |
* @author Pablo de Leon Belloc <pablolb@gmail.com> |
* @see http://php.net/manual/en/function.fsockopen.php |
*/ |
class SocketHandler extends AbstractProcessingHandler |
{ |
private $connectionString; |
private $connectionTimeout; |
private $resource; |
private $timeout = 0; |
private $writingTimeout = 10; |
private $lastSentBytes = null; |
private $persistent = false; |
private $errno; |
private $errstr; |
private $lastWritingAt; |
|
/** |
* @param string $connectionString Socket connection string |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
$this->connectionString = $connectionString; |
$this->connectionTimeout = (float) ini_get('default_socket_timeout'); |
} |
|
/** |
* Connect (if necessary) and write to the socket |
* |
* @param array $record |
* |
* @throws \UnexpectedValueException |
* @throws \RuntimeException |
*/ |
protected function write(array $record) |
{ |
$this->connectIfNotConnected(); |
$data = $this->generateDataStream($record); |
$this->writeToSocket($data); |
} |
|
/** |
* We will not close a PersistentSocket instance so it can be reused in other requests. |
*/ |
public function close() |
{ |
if (!$this->isPersistent()) { |
$this->closeSocket(); |
} |
} |
|
/** |
* Close socket, if open |
*/ |
public function closeSocket() |
{ |
if (is_resource($this->resource)) { |
fclose($this->resource); |
$this->resource = null; |
} |
} |
|
/** |
* Set socket connection to nbe persistent. It only has effect before the connection is initiated. |
* |
* @param bool $persistent |
*/ |
public function setPersistent($persistent) |
{ |
$this->persistent = (boolean) $persistent; |
} |
|
/** |
* Set connection timeout. Only has effect before we connect. |
* |
* @param float $seconds |
* |
* @see http://php.net/manual/en/function.fsockopen.php |
*/ |
public function setConnectionTimeout($seconds) |
{ |
$this->validateTimeout($seconds); |
$this->connectionTimeout = (float) $seconds; |
} |
|
/** |
* Set write timeout. Only has effect before we connect. |
* |
* @param float $seconds |
* |
* @see http://php.net/manual/en/function.stream-set-timeout.php |
*/ |
public function setTimeout($seconds) |
{ |
$this->validateTimeout($seconds); |
$this->timeout = (float) $seconds; |
} |
|
/** |
* Set writing timeout. Only has effect during connection in the writing cycle. |
* |
* @param float $seconds 0 for no timeout |
*/ |
public function setWritingTimeout($seconds) |
{ |
$this->validateTimeout($seconds); |
$this->writingTimeout = (float) $seconds; |
} |
|
/** |
* Get current connection string |
* |
* @return string |
*/ |
public function getConnectionString() |
{ |
return $this->connectionString; |
} |
|
/** |
* Get persistent setting |
* |
* @return bool |
*/ |
public function isPersistent() |
{ |
return $this->persistent; |
} |
|
/** |
* Get current connection timeout setting |
* |
* @return float |
*/ |
public function getConnectionTimeout() |
{ |
return $this->connectionTimeout; |
} |
|
/** |
* Get current in-transfer timeout |
* |
* @return float |
*/ |
public function getTimeout() |
{ |
return $this->timeout; |
} |
|
/** |
* Get current local writing timeout |
* |
* @return float |
*/ |
public function getWritingTimeout() |
{ |
return $this->writingTimeout; |
} |
|
/** |
* Check to see if the socket is currently available. |
* |
* UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. |
* |
* @return bool |
*/ |
public function isConnected() |
{ |
return is_resource($this->resource) |
&& !feof($this->resource); // on TCP - other party can close connection. |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function pfsockopen() |
{ |
return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function fsockopen() |
{ |
return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); |
} |
|
/** |
* Wrapper to allow mocking |
* |
* @see http://php.net/manual/en/function.stream-set-timeout.php |
*/ |
protected function streamSetTimeout() |
{ |
$seconds = floor($this->timeout); |
$microseconds = round(($this->timeout - $seconds) * 1e6); |
|
return stream_set_timeout($this->resource, $seconds, $microseconds); |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function fwrite($data) |
{ |
return @fwrite($this->resource, $data); |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function streamGetMetadata() |
{ |
return stream_get_meta_data($this->resource); |
} |
|
private function validateTimeout($value) |
{ |
$ok = filter_var($value, FILTER_VALIDATE_FLOAT); |
if ($ok === false || $value < 0) { |
throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)"); |
} |
} |
|
private function connectIfNotConnected() |
{ |
if ($this->isConnected()) { |
return; |
} |
$this->connect(); |
} |
|
protected function generateDataStream($record) |
{ |
return (string) $record['formatted']; |
} |
|
/** |
* @return resource|null |
*/ |
protected function getResource() |
{ |
return $this->resource; |
} |
|
private function connect() |
{ |
$this->createSocketResource(); |
$this->setSocketTimeout(); |
} |
|
private function createSocketResource() |
{ |
if ($this->isPersistent()) { |
$resource = $this->pfsockopen(); |
} else { |
$resource = $this->fsockopen(); |
} |
if (!$resource) { |
throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); |
} |
$this->resource = $resource; |
} |
|
private function setSocketTimeout() |
{ |
if (!$this->streamSetTimeout()) { |
throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); |
} |
} |
|
private function writeToSocket($data) |
{ |
$length = strlen($data); |
$sent = 0; |
$this->lastSentBytes = $sent; |
while ($this->isConnected() && $sent < $length) { |
if (0 == $sent) { |
$chunk = $this->fwrite($data); |
} else { |
$chunk = $this->fwrite(substr($data, $sent)); |
} |
if ($chunk === false) { |
throw new \RuntimeException("Could not write to socket"); |
} |
$sent += $chunk; |
$socketInfo = $this->streamGetMetadata(); |
if ($socketInfo['timed_out']) { |
throw new \RuntimeException("Write timed-out"); |
} |
|
if ($this->writingIsTimedOut($sent)) { |
throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)"); |
} |
} |
if (!$this->isConnected() && $sent < $length) { |
throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); |
} |
} |
|
private function writingIsTimedOut($sent) |
{ |
$writingTimeout = (int) floor($this->writingTimeout); |
if (0 === $writingTimeout) { |
return false; |
} |
|
if ($sent !== $this->lastSentBytes) { |
$this->lastWritingAt = time(); |
$this->lastSentBytes = $sent; |
|
return false; |
} else { |
usleep(100); |
} |
|
if ((time() - $this->lastWritingAt) >= $writingTimeout) { |
$this->closeSocket(); |
|
return true; |
} |
|
return false; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php |
@@ -0,0 +1,176 @@ |
<?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; |
|
use Monolog\Logger; |
|
/** |
* Stores to any stream resource |
* |
* Can be used to store into php://stderr, remote and local files, etc. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class StreamHandler extends AbstractProcessingHandler |
{ |
protected $stream; |
protected $url; |
private $errorMessage; |
protected $filePermission; |
protected $useLocking; |
private $dirCreated; |
|
/** |
* @param resource|string $stream |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) |
* @param Boolean $useLocking Try to lock log file before doing any writes |
* |
* @throws \Exception If a missing directory is not buildable |
* @throws \InvalidArgumentException If stream is not a resource or string |
*/ |
public function __construct($stream, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) |
{ |
parent::__construct($level, $bubble); |
if (is_resource($stream)) { |
$this->stream = $stream; |
} elseif (is_string($stream)) { |
$this->url = $stream; |
} else { |
throw new \InvalidArgumentException('A stream must either be a resource or a string.'); |
} |
|
$this->filePermission = $filePermission; |
$this->useLocking = $useLocking; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
if ($this->url && is_resource($this->stream)) { |
fclose($this->stream); |
} |
$this->stream = null; |
} |
|
/** |
* Return the currently active stream if it is open |
* |
* @return resource|null |
*/ |
public function getStream() |
{ |
return $this->stream; |
} |
|
/** |
* Return the stream URL if it was configured with a URL and not an active resource |
* |
* @return string|null |
*/ |
public function getUrl() |
{ |
return $this->url; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
if (!is_resource($this->stream)) { |
if (null === $this->url || '' === $this->url) { |
throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); |
} |
$this->createDir(); |
$this->errorMessage = null; |
set_error_handler(array($this, 'customErrorHandler')); |
$this->stream = fopen($this->url, 'a'); |
if ($this->filePermission !== null) { |
@chmod($this->url, $this->filePermission); |
} |
restore_error_handler(); |
if (!is_resource($this->stream)) { |
$this->stream = null; |
throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url)); |
} |
} |
|
if ($this->useLocking) { |
// ignoring errors here, there's not much we can do about them |
flock($this->stream, LOCK_EX); |
} |
|
$this->streamWrite($this->stream, $record); |
|
if ($this->useLocking) { |
flock($this->stream, LOCK_UN); |
} |
} |
|
/** |
* Write to stream |
* @param resource $stream |
* @param array $record |
*/ |
protected function streamWrite($stream, array $record) |
{ |
fwrite($stream, (string) $record['formatted']); |
} |
|
private function customErrorHandler($code, $msg) |
{ |
$this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); |
} |
|
/** |
* @param string $stream |
* |
* @return null|string |
*/ |
private function getDirFromStream($stream) |
{ |
$pos = strpos($stream, '://'); |
if ($pos === false) { |
return dirname($stream); |
} |
|
if ('file://' === substr($stream, 0, 7)) { |
return dirname(substr($stream, 7)); |
} |
|
return; |
} |
|
private function createDir() |
{ |
// Do not try to create dir if it has already been tried. |
if ($this->dirCreated) { |
return; |
} |
|
$dir = $this->getDirFromStream($this->url); |
if (null !== $dir && !is_dir($dir)) { |
$this->errorMessage = null; |
set_error_handler(array($this, 'customErrorHandler')); |
$status = mkdir($dir, 0777, true); |
restore_error_handler(); |
if (false === $status) { |
throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); |
} |
} |
$this->dirCreated = true; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php |
@@ -0,0 +1,99 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Formatter\LineFormatter; |
use Swift; |
|
/** |
* SwiftMailerHandler uses Swift_Mailer to send the emails |
* |
* @author Gyula Sallai |
*/ |
class SwiftMailerHandler extends MailHandler |
{ |
protected $mailer; |
private $messageTemplate; |
|
/** |
* @param \Swift_Mailer $mailer The mailer to use |
* @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
|
$this->mailer = $mailer; |
$this->messageTemplate = $message; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function send($content, array $records) |
{ |
$this->mailer->send($this->buildMessage($content, $records)); |
} |
|
/** |
* Creates instance of Swift_Message to be sent |
* |
* @param string $content formatted email body to be sent |
* @param array $records Log records that formed the content |
* @return \Swift_Message |
*/ |
protected function buildMessage($content, array $records) |
{ |
$message = null; |
if ($this->messageTemplate instanceof \Swift_Message) { |
$message = clone $this->messageTemplate; |
$message->generateId(); |
} elseif (is_callable($this->messageTemplate)) { |
$message = call_user_func($this->messageTemplate, $content, $records); |
} |
|
if (!$message instanceof \Swift_Message) { |
throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it'); |
} |
|
if ($records) { |
$subjectFormatter = new LineFormatter($message->getSubject()); |
$message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); |
} |
|
$message->setBody($content); |
if (version_compare(Swift::VERSION, '6.0.0', '>=')) { |
$message->setDate(new \DateTimeImmutable()); |
} else { |
$message->setDate(time()); |
} |
|
return $message; |
} |
|
/** |
* BC getter, to be removed in 2.0 |
*/ |
public function __get($name) |
{ |
if ($name === 'message') { |
trigger_error('SwiftMailerHandler->message is deprecated, use ->buildMessage() instead to retrieve the message', E_USER_DEPRECATED); |
|
return $this->buildMessage(null, array()); |
} |
|
throw new \InvalidArgumentException('Invalid property '.$name); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php |
@@ -0,0 +1,103 @@ |
<?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; |
|
use Monolog\Logger; |
use Monolog\Handler\SyslogUdp\UdpSocket; |
|
/** |
* A Handler for logging to a remote syslogd server. |
* |
* @author Jesper Skovgaard Nielsen <nulpunkt@gmail.com> |
*/ |
class SyslogUdpHandler extends AbstractSyslogHandler |
{ |
protected $socket; |
protected $ident; |
|
/** |
* @param string $host |
* @param int $port |
* @param mixed $facility |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param string $ident Program name or tag for each log message. |
*/ |
public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php') |
{ |
parent::__construct($facility, $level, $bubble); |
|
$this->ident = $ident; |
|
$this->socket = new UdpSocket($host, $port ?: 514); |
} |
|
protected function write(array $record) |
{ |
$lines = $this->splitMessageIntoLines($record['formatted']); |
|
$header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']]); |
|
foreach ($lines as $line) { |
$this->socket->write($line, $header); |
} |
} |
|
public function close() |
{ |
$this->socket->close(); |
} |
|
private function splitMessageIntoLines($message) |
{ |
if (is_array($message)) { |
$message = implode("\n", $message); |
} |
|
return preg_split('/$\R?^/m', $message, -1, PREG_SPLIT_NO_EMPTY); |
} |
|
/** |
* Make common syslog header (see rfc5424) |
*/ |
protected function makeCommonSyslogHeader($severity) |
{ |
$priority = $severity + $this->facility; |
|
if (!$pid = getmypid()) { |
$pid = '-'; |
} |
|
if (!$hostname = gethostname()) { |
$hostname = '-'; |
} |
|
return "<$priority>1 " . |
$this->getDateTime() . " " . |
$hostname . " " . |
$this->ident . " " . |
$pid . " - - "; |
} |
|
protected function getDateTime() |
{ |
return date(\DateTime::RFC3339); |
} |
|
/** |
* Inject your own socket, mainly used for testing |
*/ |
public function setSocket($socket) |
{ |
$this->socket = $socket; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php |
@@ -0,0 +1,154 @@ |
<?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; |
|
/** |
* Used for testing purposes. |
* |
* It records all records and gives you access to them for verification. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
* |
* @method bool hasEmergency($record) |
* @method bool hasAlert($record) |
* @method bool hasCritical($record) |
* @method bool hasError($record) |
* @method bool hasWarning($record) |
* @method bool hasNotice($record) |
* @method bool hasInfo($record) |
* @method bool hasDebug($record) |
* |
* @method bool hasEmergencyRecords() |
* @method bool hasAlertRecords() |
* @method bool hasCriticalRecords() |
* @method bool hasErrorRecords() |
* @method bool hasWarningRecords() |
* @method bool hasNoticeRecords() |
* @method bool hasInfoRecords() |
* @method bool hasDebugRecords() |
* |
* @method bool hasEmergencyThatContains($message) |
* @method bool hasAlertThatContains($message) |
* @method bool hasCriticalThatContains($message) |
* @method bool hasErrorThatContains($message) |
* @method bool hasWarningThatContains($message) |
* @method bool hasNoticeThatContains($message) |
* @method bool hasInfoThatContains($message) |
* @method bool hasDebugThatContains($message) |
* |
* @method bool hasEmergencyThatMatches($message) |
* @method bool hasAlertThatMatches($message) |
* @method bool hasCriticalThatMatches($message) |
* @method bool hasErrorThatMatches($message) |
* @method bool hasWarningThatMatches($message) |
* @method bool hasNoticeThatMatches($message) |
* @method bool hasInfoThatMatches($message) |
* @method bool hasDebugThatMatches($message) |
* |
* @method bool hasEmergencyThatPasses($message) |
* @method bool hasAlertThatPasses($message) |
* @method bool hasCriticalThatPasses($message) |
* @method bool hasErrorThatPasses($message) |
* @method bool hasWarningThatPasses($message) |
* @method bool hasNoticeThatPasses($message) |
* @method bool hasInfoThatPasses($message) |
* @method bool hasDebugThatPasses($message) |
*/ |
class TestHandler extends AbstractProcessingHandler |
{ |
protected $records = array(); |
protected $recordsByLevel = array(); |
|
public function getRecords() |
{ |
return $this->records; |
} |
|
public function clear() |
{ |
$this->records = array(); |
$this->recordsByLevel = array(); |
} |
|
public function hasRecords($level) |
{ |
return isset($this->recordsByLevel[$level]); |
} |
|
public function hasRecord($record, $level) |
{ |
if (is_array($record)) { |
$record = $record['message']; |
} |
|
return $this->hasRecordThatPasses(function ($rec) use ($record) { |
return $rec['message'] === $record; |
}, $level); |
} |
|
public function hasRecordThatContains($message, $level) |
{ |
return $this->hasRecordThatPasses(function ($rec) use ($message) { |
return strpos($rec['message'], $message) !== false; |
}, $level); |
} |
|
public function hasRecordThatMatches($regex, $level) |
{ |
return $this->hasRecordThatPasses(function ($rec) use ($regex) { |
return preg_match($regex, $rec['message']) > 0; |
}, $level); |
} |
|
public function hasRecordThatPasses($predicate, $level) |
{ |
if (!is_callable($predicate)) { |
throw new \InvalidArgumentException("Expected a callable for hasRecordThatSucceeds"); |
} |
|
if (!isset($this->recordsByLevel[$level])) { |
return false; |
} |
|
foreach ($this->recordsByLevel[$level] as $i => $rec) { |
if (call_user_func($predicate, $rec, $i)) { |
return true; |
} |
} |
|
return false; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
$this->recordsByLevel[$record['level']][] = $record; |
$this->records[] = $record; |
} |
|
public function __call($method, $args) |
{ |
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { |
$genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; |
$level = constant('Monolog\Logger::' . strtoupper($matches[2])); |
if (method_exists($this, $genericMethod)) { |
$args[] = $level; |
|
return call_user_func_array(array($this, $genericMethod), $args); |
} |
} |
|
throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); |
} |
} |