Файловый менеджер - Редактировать - /home/lakoyani/lakoyani.com.fj/Handler.tar
Назад
BrowserConsoleHandler.php 0000777 00000016351 14711055564 0011546 0 ustar 00 <?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 static::$records[] = $record; // Register shutdown handler if not already done if (!static::$initialized) { static::$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 = static::getResponseFormat(); if ($format === 'unknown') { return; } if (count(static::$records)) { if ($format === 'html') { static::writeOutput('<script>' . static::generateScript() . '</script>'); } elseif ($format === 'js') { static::writeOutput(static::generateScript()); } static::resetStatic(); } } public function close() { self::resetStatic(); } public function reset() { self::resetStatic(); } /** * Forget all logged records */ public static function resetStatic() { static::$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 (static::$records as $record) { $context = static::dump('Context', $record['context']); $extra = static::dump('Extra', $record['extra']); if (empty($context) && empty($extra)) { $script[] = static::call_array('log', static::handleStyles($record['formatted'])); } else { $script = array_merge($script, array(static::call_array('groupCollapsed', static::handleStyles($record['formatted']))), $context, $extra, array(static::call('groupEnd')) ); } } return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; } private static function handleStyles($formatted) { $args = array(); $format = '%c' . $formatted; preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); foreach (array_reverse($matches) as $match) { $args[] = '"font-weight: normal"'; $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); $pos = $match[0][1]; $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); } $args[] = static::quote('font-weight: normal'); $args[] = static::quote($format); return array_reverse($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[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); foreach ($dict as $key => $value) { $value = json_encode($value); if (empty($value)) { $value = static::quote(''); } $script[] = static::call('log', static::quote('%s: %o'), static::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 static::call_array($method, $args); } private static function call_array($method, array $args) { return 'c.' . $method . '(' . implode(', ', $args) . ');'; } } NewRelicHandler.php 0000777 00000014146 14711055564 0010310 0 ustar 00 <?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\Utils; 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. * * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted'] * * @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 || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { 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, Utils::jsonEncode($value, null, true)); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new NormalizerFormatter(); } } RotatingFileHandler.php 0000777 00000013460 14711055564 0011165 0 ustar 00 <?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\Utils; /** * 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 bool $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 bool $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 = Utils::canonicalizePath($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(); } } /** * {@inheritdoc} */ public function reset() { parent::reset(); 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'], '[0-9][0-9][0-9][0-9]*'), $fileInfo['dirname'] . '/' . $this->filenameFormat ); if (!empty($fileInfo['extension'])) { $glob .= '.'.$fileInfo['extension']; } return $glob; } } FlowdockHandler.php 0000777 00000006466 14711055564 0010356 0 ustar 00 <?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\Utils; 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 Utils::jsonEncode($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; } } WhatFailureGroupHandler.php 0000777 00000003461 14711055564 0012026 0 ustar 00 <?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; /** * Forwards records to multiple handlers suppressing failures of each handler * and continuing through to give every handler a chance to succeed. * * @author Craig D'Amelio <craig@damelio.ca> */ class WhatFailureGroupHandler extends GroupHandler { /** * {@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) { try { $handler->handle($record); } catch (\Exception $e) { // What failure? } catch (\Throwable $e) { // What failure? } } return false === $this->bubble; } /** * {@inheritdoc} */ public function handleBatch(array $records) { if ($this->processors) { $processed = array(); foreach ($records as $record) { foreach ($this->processors as $processor) { $record = call_user_func($processor, $record); } $processed[] = $record; } $records = $processed; } foreach ($this->handlers as $handler) { try { $handler->handleBatch($records); } catch (\Exception $e) { // What failure? } catch (\Throwable $e) { // What failure? } } } } AmqpHandler.php 0000777 00000007432 14711055564 0007476 0 ustar 00 <?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); } } DoctrineCouchDBHandler.php 0000777 00000001750 14711055564 0011534 0 ustar 00 <?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; use Doctrine\CouchDB\CouchDBClient; /** * CouchDB handler for Doctrine CouchDB ODM * * @author Markus Bachmann <markus.bachmann@bachi.biz> */ class DoctrineCouchDBHandler extends AbstractProcessingHandler { private $client; public function __construct(CouchDBClient $client, $level = Logger::DEBUG, $bubble = true) { $this->client = $client; parent::__construct($level, $bubble); } /** * {@inheritDoc} */ protected function write(array $record) { $this->client->postDocument($record['formatted']); } protected function getDefaultFormatter() { return new NormalizerFormatter; } } SamplingHandler.php 0000777 00000006324 14711055564 0010351 0 ustar 00 <?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; /** * 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|null, $samplingHandler). * @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->getHandler($record)->isHandling($record); } public function handle(array $record) { if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { if ($this->processors) { foreach ($this->processors as $processor) { $record = call_user_func($processor, $record); } } $this->getHandler($record)->handle($record); } return false === $this->bubble; } /** * Return the nested handler * * If the handler was provided as a factory callable, this will trigger the handler's instantiation. * * @return HandlerInterface */ public function getHandler(array $record = null) { 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"); } } return $this->handler; } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->getHandler()->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->getHandler()->getFormatter(); } } ProcessableHandlerTrait.php 0000777 00000003077 14711055564 0012047 0 ustar 00 <?php declare(strict_types=1); /* * 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\ResettableInterface; /** * Helper trait for implementing ProcessableInterface * * This trait is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano <j.boggiano@seld.be> */ trait ProcessableHandlerTrait { /** * @var callable[] */ protected $processors = []; /** * {@inheritdoc} * @suppress PhanTypeMismatchReturn */ public function pushProcessor($callback): HandlerInterface { array_unshift($this->processors, $callback); return $this; } /** * {@inheritdoc} */ public function popProcessor(): callable { if (!$this->processors) { throw new \LogicException('You tried to pop from an empty processor stack.'); } return array_shift($this->processors); } /** * Processes a record. */ protected function processRecord(array $record): array { foreach ($this->processors as $processor) { $record = $processor($record); } return $record; } protected function resetProcessors(): void { foreach ($this->processors as $processor) { if ($processor instanceof ResettableInterface) { $processor->reset(); } } } } SyslogUdp/UdpSocket.php 0000777 00000002571 14711055564 0011133 0 ustar 00 <?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\SyslogUdp; class UdpSocket { const DATAGRAM_MAX_LENGTH = 65023; protected $ip; protected $port; protected $socket; public function __construct($ip, $port = 514) { $this->ip = $ip; $this->port = $port; $this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); } public function write($line, $header = "") { $this->send($this->assembleMessage($line, $header)); } public function close() { if (is_resource($this->socket)) { socket_close($this->socket); $this->socket = null; } } protected function send($chunk) { if (!is_resource($this->socket)) { throw new \LogicException('The UdpSocket to '.$this->ip.':'.$this->port.' has been closed and can not be written to anymore'); } socket_sendto($this->socket, $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port); } protected function assembleMessage($line, $header) { $chunkSize = self::DATAGRAM_MAX_LENGTH - strlen($header); return $header . substr($line, 0, $chunkSize); } } SyslogHandler.php 0000777 00000003463 14711055564 0010060 0 ustar 00 <?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 syslog service. * * usage example: * * $log = new Logger('application'); * $syslog = new SyslogHandler('myfacility', 'local6'); * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); * $syslog->setFormatter($formatter); * $log->pushHandler($syslog); * * @author Sven Paulus <sven@karlsruhe.org> */ class SyslogHandler extends AbstractSyslogHandler { protected $ident; protected $logopts; /** * @param string $ident * @param mixed $facility * @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 $logopts Option flags for the openlog() call, defaults to LOG_PID */ public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID) { parent::__construct($facility, $level, $bubble); $this->ident = $ident; $this->logopts = $logopts; } /** * {@inheritdoc} */ public function close() { closelog(); } /** * {@inheritdoc} */ protected function write(array $record) { if (!openlog($this->ident, $this->logopts, $this->facility)) { throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"'); } syslog($this->logLevels[$record['level']], (string) $record['formatted']); } } CubeHandler.php 0000777 00000011066 14711055564 0007454 0 ustar 00 <?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\Utils; /** * 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(Utils::jsonEncode($data)); } else { $this->writeUdp(Utils::jsonEncode($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); } } DynamoDbHandler.php 0000777 00000004665 14711055564 0010302 0 ustar 00 <?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 { /** @phpstan-ignore-next-line */ $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); } } SlackbotHandler.php 0000777 00000004514 14711055564 0010340 0 ustar 00 <?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 * @deprecated According to Slack the API used on this handler it is deprecated. * Therefore this handler will be removed on 2.x * Slack suggests to use webhooks instead. Please contact slack for more information. */ 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) { @trigger_error('SlackbotHandler is deprecated and will be removed on 2.x', E_USER_DEPRECATED); 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); } } HandlerInterface.php 0000777 00000005037 14711055564 0010477 0 ustar 00 <?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 bool */ 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 bool 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(); } FormattableHandlerTrait.php 0000777 00000002562 14711055564 0012043 0 ustar 00 <?php declare(strict_types=1); /* * 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\LineFormatter; /** * Helper trait for implementing FormattableInterface * * This trait is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano <j.boggiano@seld.be> */ trait FormattableHandlerTrait { /** * @var FormatterInterface */ protected $formatter; /** * {@inheritdoc} * @suppress PhanTypeMismatchReturn */ public function setFormatter(FormatterInterface $formatter): HandlerInterface { $this->formatter = $formatter; return $this; } /** * {@inheritdoc} */ public function getFormatter(): FormatterInterface { if (!$this->formatter) { $this->formatter = $this->getDefaultFormatter(); } return $this->formatter; } /** * Gets the default formatter. * * Overwrite this if the LineFormatter is not a good default for your handler. */ protected function getDefaultFormatter(): FormatterInterface { return new LineFormatter(); } } StreamHandler.php 0000777 00000013121 14711055564 0010023 0 ustar 00 <?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\Utils; /** * 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 { /** @private 512KB */ const CHUNK_SIZE = 524288; /** @var resource|null */ 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 bool $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 bool $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; $this->streamSetChunkSize(); } elseif (is_string($stream)) { $this->url = Utils::canonicalizePath($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; $this->dirCreated = 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 in append mode: '.$this->errorMessage, $this->url)); } $this->streamSetChunkSize(); } 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']); } protected function streamSetChunkSize() { if (version_compare(PHP_VERSION, '5.4.0', '>=')) { return stream_set_chunk_size($this->stream, self::CHUNK_SIZE); } return false; } 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 null; } 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 && !is_dir($dir)) { throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); } } $this->dirCreated = true; } } FormattableHandlerInterface.php 0000777 00000001633 14711055564 0012656 0 ustar 00 <?php declare(strict_types=1); /* * 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 to describe loggers that have a formatter * * This interface is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano <j.boggiano@seld.be> */ interface FormattableHandlerInterface { /** * Sets the formatter. * * @param FormatterInterface $formatter * @return HandlerInterface self */ public function setFormatter(FormatterInterface $formatter): HandlerInterface; /** * Gets the formatter. * * @return FormatterInterface */ public function getFormatter(): FormatterInterface; } GroupHandler.php 0000777 00000005351 14711055564 0007672 0 ustar 00 <?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\ResettableInterface; /** * 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 bool $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) { $record = call_user_func($processor, $record); } $processed[] = $record; } $records = $processed; } foreach ($this->handlers as $handler) { $handler->handleBatch($records); } } public function reset() { parent::reset(); foreach ($this->handlers as $handler) { if ($handler instanceof ResettableInterface) { $handler->reset(); } } } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { foreach ($this->handlers as $handler) { $handler->setFormatter($formatter); } return $this; } } SocketHandler.php 0000777 00000023124 14711055564 0010024 0 ustar 00 <?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 $chunkSize = 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 bool $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 = (bool) $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; } /** * Set chunk size. Only has effect during connection in the writing cycle. * * @param float $bytes */ public function setChunkSize($bytes) { $this->chunkSize = $bytes; } /** * 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; } /** * Get current chunk size * * @return float */ public function getChunkSize() { return $this->chunkSize; } /** * 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 * * @see http://php.net/manual/en/function.stream-set-chunk-size.php */ protected function streamSetChunkSize() { return stream_set_chunk_size($this->resource, $this->chunkSize); } /** * 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(); $this->setStreamChunkSize(); } 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 setStreamChunkSize() { if ($this->chunkSize && !$this->streamSetChunkSize()) { throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); } } 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; } } HipChatHandler.php 0000777 00000025367 14711055564 0010127 0 ustar 00 <?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) { @trigger_error('The Monolog\Handler\HipChatHandler class is deprecated. You should migrate to Slack and the SlackWebhookHandler / SlackbotHandler, see https://www.atlassian.com/partnerships/slack', E_USER_DEPRECATED); 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->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, hipchat sometimes * drops the request entirely. */ protected function finalizeWrite() { $res = $this->getResource(); if (is_resource($res)) { @fread($res, 2048); } $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 array $records * @return array */ private function combineRecords(array $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); } } LogEntriesHandler.php 0000777 00000003117 14711055564 0010647 0 ustar 00 <?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; /** * @author Robert Kaufmann III <rok3@rok3.me> */ class LogEntriesHandler extends SocketHandler { /** * @var string */ protected $logToken; /** * @param string $token Log token supplied by LogEntries * @param bool $useSSL Whether or not SSL encryption should be used. * @param int $level The minimum logging level to trigger this handler * @param bool $bubble Whether or not messages that are handled should bubble up the stack. * * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing */ public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true, $host = 'data.logentries.com') { if ($useSSL && !extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); } $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; parent::__construct($endpoint, $level, $bubble); $this->logToken = $token; } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { return $this->logToken . ' ' . $record['formatted']; } } CouchDBHandler.php 0000777 00000003641 14711055564 0010045 0 ustar 00 <?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\JsonFormatter; use Monolog\Logger; /** * CouchDB handler * * @author Markus Bachmann <markus.bachmann@bachi.biz> */ class CouchDBHandler extends AbstractProcessingHandler { private $options; public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = true) { $this->options = array_merge(array( 'host' => 'localhost', 'port' => 5984, 'dbname' => 'logger', 'username' => null, 'password' => null, ), $options); parent::__construct($level, $bubble); } /** * {@inheritDoc} */ protected function write(array $record) { $basicAuth = null; if ($this->options['username']) { $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']); } $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname']; $context = stream_context_create(array( 'http' => array( 'method' => 'POST', 'content' => $record['formatted'], 'ignore_errors' => true, 'max_redirects' => 0, 'header' => 'Content-type: application/json', ), )); if (false === @file_get_contents($url, null, $context)) { throw new \RuntimeException(sprintf('Could not connect to %s', $url)); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); } } TestHandler.php 0000777 00000012400 14711055564 0007506 0 ustar 00 <?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(); private $skipReset = false; public function getRecords() { return $this->records; } public function clear() { $this->records = array(); $this->recordsByLevel = array(); } public function reset() { if (!$this->skipReset) { $this->clear(); } } public function setSkipReset($skipReset) { $this->skipReset = $skipReset; } public function hasRecords($level) { return isset($this->recordsByLevel[$level]); } /** * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records * @param int $level Logger::LEVEL constant value */ public function hasRecord($record, $level) { if (is_string($record)) { $record = array('message' => $record); } return $this->hasRecordThatPasses(function ($rec) use ($record) { if ($rec['message'] !== $record['message']) { return false; } if (isset($record['context']) && $rec['context'] !== $record['context']) { return false; } return true; }, $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 . '()'); } } ErrorLogHandler.php 0000777 00000004500 14711055564 0010324 0 ustar 00 <?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 bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $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); } } } ProcessableHandlerInterface.php 0000777 00000001771 14711055564 0012663 0 ustar 00 <?php declare(strict_types=1); /* * 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\Processor\ProcessorInterface; /** * Interface to describe loggers that have processors * * This interface is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano <j.boggiano@seld.be> */ interface ProcessableHandlerInterface { /** * Adds a processor in the stack. * * @param ProcessorInterface|callable $callback * @return HandlerInterface self */ public function pushProcessor($callback): HandlerInterface; /** * Removes the processor on top of the stack and returns it. * * @throws \LogicException In case the processor stack is empty * @return callable */ public function popProcessor(): callable; } RedisHandler.php 0000777 00000005620 14711055564 0007643 0 ustar 00 <?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|false $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) { $mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1; $this->redisClient->multi($mode) ->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(); } } Curl/Util.php 0000777 00000002731 14711055564 0007121 0 ustar 00 <?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\Curl; class Util { private static $retriableErrorCodes = array( CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_HTTP_NOT_FOUND, CURLE_READ_ERROR, CURLE_OPERATION_TIMEOUTED, CURLE_HTTP_POST_ERROR, CURLE_SSL_CONNECT_ERROR, ); /** * Executes a CURL request with optional retries and exception on failure * * @param resource $ch curl handler * @throws \RuntimeException */ public static function execute($ch, $retries = 5, $closeAfterDone = true) { while ($retries--) { if (curl_exec($ch) === false) { $curlErrno = curl_errno($ch); if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) { $curlError = curl_error($ch); if ($closeAfterDone) { curl_close($ch); } throw new \RuntimeException(sprintf('Curl error (code %s): %s', $curlErrno, $curlError)); } continue; } if ($closeAfterDone) { curl_close($ch); } break; } } } FilterHandler.php 0000777 00000012034 14711055564 0010017 0 ustar 00 <?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; /** * 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 bool */ protected $bubble; /** * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). * @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 bool $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; } if ($this->processors) { foreach ($this->processors as $processor) { $record = call_user_func($processor, $record); } } $this->getHandler($record)->handle($record); return false === $this->bubble; } /** * {@inheritdoc} */ public function handleBatch(array $records) { $filtered = array(); foreach ($records as $record) { if ($this->isHandling($record)) { $filtered[] = $record; } } if (count($filtered) > 0) { $this->getHandler($filtered[count($filtered) - 1])->handleBatch($filtered); } } /** * Return the nested handler * * If the handler was provided as a factory callable, this will trigger the handler's instantiation. * * @return HandlerInterface */ public function getHandler(array $record = null) { 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"); } } return $this->handler; } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->getHandler()->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->getHandler()->getFormatter(); } } SwiftMailerHandler.php 0000777 00000006554 14711055564 0011032 0 ustar 00 <?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; 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 bool $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)); } /** * Gets the formatter for the Swift_Message subject. * * @param string $format The format of the subject * @return FormatterInterface */ protected function getSubjectFormatter($format) { return new LineFormatter($format); } /** * 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 = $this->getSubjectFormatter($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); } } FirePHPHandler.php 0000777 00000012526 14711055564 0010035 0 ustar 00 <?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 array */ 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 bool */ 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; } } SlackWebhookHandler.php 0000777 00000007405 14711055564 0011154 0 ustar 00 <?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\Utils; 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; } public function getWebhookUrl() { return $this->webhookUrl; } /** * {@inheritdoc} * * @param array $record */ protected function write(array $record) { $postData = $this->slackRecord->getSlackData($record); $postString = Utils::jsonEncode($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; } } RollbarHandler.php 0000777 00000007546 14711055564 0010203 0 ustar 00 <?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(); } /** * {@inheritdoc} */ public function reset() { $this->flush(); parent::reset(); } } IFTTTHandler.php 0000777 00000004133 14711055564 0007465 0 ustar 00 <?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\Utils; /** * 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 bool $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 = Utils::jsonEncode($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); } } FleepHookHandler.php 0000777 00000006443 14711055564 0010455 0 ustar 00 <?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); } } HandlerWrapper.php 0000777 00000004476 14711055564 0010225 0 ustar 00 <?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\ResettableInterface; 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, ResettableInterface { /** * @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(); } public function reset() { if ($this->handler instanceof ResettableInterface) { return $this->handler->reset(); } } } AbstractSyslogHandler.php 0000777 00000006453 14711055564 0011546 0 ustar 00 <?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 bool $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%'); } } FingersCrossed/ErrorLevelActivationStrategy.php 0000777 00000001367 14711055564 0016051 0 ustar 00 <?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\FingersCrossed; use Monolog\Logger; /** * Error level based activation strategy. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ErrorLevelActivationStrategy implements ActivationStrategyInterface { private $actionLevel; public function __construct($actionLevel) { $this->actionLevel = Logger::toMonologLevel($actionLevel); } public function isHandlerActivated(array $record) { return $record['level'] >= $this->actionLevel; } } FingersCrossed/ChannelLevelActivationStrategy.php 0000777 00000003615 14711055564 0016326 0 ustar 00 <?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\FingersCrossed; use Monolog\Logger; /** * Channel and Error level based monolog activation strategy. Allows to trigger activation * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except * for records of the 'sql' channel; those should trigger activation on level 'WARN'. * * Example: * * <code> * $activationStrategy = new ChannelLevelActivationStrategy( * Logger::CRITICAL, * array( * 'request' => Logger::ALERT, * 'sensitive' => Logger::ERROR, * ) * ); * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); * </code> * * @author Mike Meessen <netmikey@gmail.com> */ class ChannelLevelActivationStrategy implements ActivationStrategyInterface { private $defaultActionLevel; private $channelToActionLevel; /** * @param int $defaultActionLevel The default action level to be used if the record's category doesn't match any * @param array $channelToActionLevel An array that maps channel names to action levels. */ public function __construct($defaultActionLevel, $channelToActionLevel = array()) { $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel); } public function isHandlerActivated(array $record) { if (isset($this->channelToActionLevel[$record['channel']])) { return $record['level'] >= $this->channelToActionLevel[$record['channel']]; } return $record['level'] >= $this->defaultActionLevel; } } FingersCrossed/ActivationStrategyInterface.php 0000777 00000001210 14711055564 0015653 0 ustar 00 <?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\FingersCrossed; /** * Interface for activation strategies for the FingersCrossedHandler. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface ActivationStrategyInterface { /** * Returns whether the given record activates the handler. * * @param array $record * @return bool */ public function isHandlerActivated(array $record); } ChromePHPHandler.php 0000777 00000013026 14711055564 0010361 0 ustar 00 <?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; use Monolog\Utils; /** * 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 4KB, so when we sent 3KB we stop sending * * @var bool */ 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 bool $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 = Utils::jsonEncode(self::$json, null, true); $data = base64_encode(utf8_encode($json)); if (strlen($data) > 3 * 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 = Utils::jsonEncode(self::$json, null, true); $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 bool */ 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; } } ElasticSearchHandler.php 0000777 00000006525 14711055564 0011314 0 ustar 00 <?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 bool $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); } } } } LogglyHandler.php 0000777 00000005073 14711055564 0010034 0 ustar 00 <?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(); } } DeduplicationHandler.php 0000777 00000012547 14711055564 0011367 0 ustar 00 <?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 bool $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); } } ZendMonitorHandler.php 0000777 00000005672 14711055564 0011054 0 ustar 00 <?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\NormalizerFormatter; use Monolog\Logger; /** * Handler sending logs to Zend Monitor * * @author Christian Bergau <cbergau86@gmail.com> * @author Jason Davis <happydude@jasondavis.net> */ class ZendMonitorHandler extends AbstractProcessingHandler { /** * Monolog level / ZendMonitor Custom Event priority map * * @var array */ protected $levelMap = array(); /** * Construct * * @param int $level * @param bool $bubble * @throws MissingExtensionException */ public function __construct($level = Logger::DEBUG, $bubble = true) { if (!function_exists('zend_monitor_custom_event')) { throw new MissingExtensionException( 'You must have Zend Server installed with Zend Monitor enabled in order to use this handler' ); } //zend monitor constants are not defined if zend monitor is not enabled. $this->levelMap = array( Logger::DEBUG => \ZEND_MONITOR_EVENT_SEVERITY_INFO, Logger::INFO => \ZEND_MONITOR_EVENT_SEVERITY_INFO, Logger::NOTICE => \ZEND_MONITOR_EVENT_SEVERITY_INFO, Logger::WARNING => \ZEND_MONITOR_EVENT_SEVERITY_WARNING, Logger::ERROR => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, Logger::CRITICAL => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, Logger::ALERT => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, Logger::EMERGENCY => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, ); parent::__construct($level, $bubble); } /** * {@inheritdoc} */ protected function write(array $record) { $this->writeZendMonitorCustomEvent( Logger::getLevelName($record['level']), $record['message'], $record['formatted'], $this->levelMap[$record['level']] ); } /** * Write to Zend Monitor Events * @param string $type Text displayed in "Class Name (custom)" field * @param string $message Text displayed in "Error String" * @param mixed $formatted Displayed in Custom Variables tab * @param int $severity Set the event severity level (-1,0,1) */ protected function writeZendMonitorCustomEvent($type, $message, $formatted, $severity) { zend_monitor_custom_event($type, $message, $formatted, $severity); } /** * {@inheritdoc} */ public function getDefaultFormatter() { return new NormalizerFormatter(); } /** * Get the level map * * @return array */ public function getLevelMap() { return $this->levelMap; } } Slack/SlackRecord.php 0000777 00000020344 14711055564 0010530 0 ustar 00 <?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\Utils; 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( $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; $flags = 0; if (PHP_VERSION_ID >= 50400) { $flags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; } $hasSecondDimension = count(array_filter($normalized, 'is_array')); $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric')); return $hasSecondDimension || $hasNonNumericKeys ? Utils::jsonEncode($normalized, $prettyPrintFlag | $flags) : Utils::jsonEncode($normalized, $flags); } /** * 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' => ucfirst($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 ($this->normalizerFormatter->format($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; } } BufferHandler.php 0000777 00000007676 14711055564 0010023 0 ustar 00 <?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\ResettableInterface; use Monolog\Formatter\FormatterInterface; /** * 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 bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $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(); } public function reset() { $this->flush(); parent::reset(); if ($this->handler instanceof ResettableInterface) { $this->handler->reset(); } } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->handler->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->handler->getFormatter(); } } NativeMailerHandler.php 0000777 00000012126 14711055564 0011154 0 ustar 00 <?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; } } AbstractHandler.php 0000777 00000010446 14711055564 0010342 0 ustar 00 <?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\LineFormatter; use Monolog\Logger; use Monolog\ResettableInterface; /** * Base Handler class providing the Handler structure * * @author Jordi Boggiano <j.boggiano@seld.be> */ abstract class AbstractHandler implements HandlerInterface, ResettableInterface { protected $level = Logger::DEBUG; protected $bubble = true; /** * @var FormatterInterface */ protected $formatter; protected $processors = array(); /** * @param int|string $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($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 bool $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 bool 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 } } public function reset() { foreach ($this->processors as $processor) { if ($processor instanceof ResettableInterface) { $processor->reset(); } } } /** * Gets the default formatter. * * @return FormatterInterface */ protected function getDefaultFormatter() { return new LineFormatter(); } } RavenHandler.php 0000777 00000016366 14711055564 0007661 0 ustar 00 <?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 sentry-php (https://github.com/getsentry/sentry-php) * * @author Marc Abramowitz <marc@marc-abramowitz.com> */ class RavenHandler extends AbstractProcessingHandler { /** * Translates Monolog log levels to Raven log levels. */ protected $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) */ protected $release; /** * @var Raven_Client the client object that sends the message to the server */ protected $ravenClient; /** * @var FormatterInterface 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 bool $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) { @trigger_error('The Monolog\Handler\RavenHandler class is deprecated. You should rather upgrade to the sentry/sentry 2.x and use Sentry\Monolog\Handler, see https://github.com/getsentry/sentry-php/blob/master/src/Monolog/Handler.php', E_USER_DEPRECATED); 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 (null === $highest || $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['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('contexts', 'checksum', 'release', 'event_id'); } /** * @param string $value * @return self */ public function setRelease($value) { $this->release = $value; return $this; } } GelfHandler.php 0000777 00000003712 14711055564 0007452 0 ustar 00 <?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 Gelf\IMessagePublisher; use Gelf\PublisherInterface; use Gelf\Publisher; use InvalidArgumentException; use Monolog\Logger; use Monolog\Formatter\GelfMessageFormatter; /** * Handler to send messages to a Graylog2 (http://www.graylog2.org) server * * @author Matt Lehner <mlehner@gmail.com> * @author Benjamin Zikarsky <benjamin@zikarsky.de> */ class GelfHandler extends AbstractProcessingHandler { /** * @var Publisher|PublisherInterface|IMessagePublisher the publisher object that sends the message to the server */ protected $publisher; /** * @param PublisherInterface|IMessagePublisher|Publisher $publisher a publisher object * @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($publisher, $level = Logger::DEBUG, $bubble = true) { parent::__construct($level, $bubble); if (!$publisher instanceof Publisher && !$publisher instanceof IMessagePublisher && !$publisher instanceof PublisherInterface) { throw new InvalidArgumentException('Invalid publisher, expected a Gelf\Publisher, Gelf\IMessagePublisher or Gelf\PublisherInterface instance'); } $this->publisher = $publisher; } /** * {@inheritdoc} */ protected function write(array $record) { $this->publisher->publish($record['formatted']); } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new GelfMessageFormatter(); } } AbstractProcessingHandler.php 0000777 00000002774 14711055564 0012404 0 ustar 00 <?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\ResettableInterface; /** * Base Handler class providing the Handler structure * * Classes extending it should (in most cases) only implement write($record) * * @author Jordi Boggiano <j.boggiano@seld.be> * @author Christophe Coevoet <stof@notk.org> */ abstract class AbstractProcessingHandler extends AbstractHandler { /** * {@inheritdoc} */ public function handle(array $record) { if (!$this->isHandling($record)) { return false; } $record = $this->processRecord($record); $record['formatted'] = $this->getFormatter()->format($record); $this->write($record); return false === $this->bubble; } /** * Writes the record down to the log of the implementing handler * * @param array $record * @return void */ abstract protected function write(array $record); /** * Processes a record. * * @param array $record * @return array */ protected function processRecord(array $record) { if ($this->processors) { foreach ($this->processors as $processor) { $record = call_user_func($processor, $record); } } return $record; } } MongoDBHandler.php 0000777 00000003106 14711055564 0010057 0 ustar 00 <?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; /** * Logs to a MongoDB database. * * usage example: * * $log = new Logger('application'); * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod"); * $log->pushHandler($mongodb); * * @author Thomas Tourlourat <thomas@tourlourat.com> */ class MongoDBHandler extends AbstractProcessingHandler { protected $mongoCollection; public function __construct($mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true) { if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo || $mongo instanceof \MongoDB\Client)) { throw new \InvalidArgumentException('MongoClient, Mongo or MongoDB\Client instance required'); } $this->mongoCollection = $mongo->selectCollection($database, $collection); parent::__construct($level, $bubble); } protected function write(array $record) { if ($this->mongoCollection instanceof \MongoDB\Collection) { $this->mongoCollection->insertOne($record["formatted"]); } else { $this->mongoCollection->save($record["formatted"]); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new NormalizerFormatter(); } } FingersCrossedHandler.php 0000777 00000014725 14711055564 0011523 0 ustar 00 <?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; use Monolog\ResettableInterface; use Monolog\Formatter\FormatterInterface; /** * 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|null, $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 bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $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; } $this->getHandler(end($this->buffer) ?: null)->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->getHandler($record)->handle($record); } return false === $this->bubble; } /** * {@inheritdoc} */ public function close() { $this->flushBuffer(); } public function reset() { $this->flushBuffer(); parent::reset(); if ($this->getHandler() instanceof ResettableInterface) { $this->getHandler()->reset(); } } /** * 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(); } /** * Resets the state of the handler. Stops forwarding records to the wrapped handler. */ private function flushBuffer() { 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->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer); } } $this->buffer = array(); $this->buffering = true; } /** * Return the nested handler * * If the handler was provided as a factory callable, this will trigger the handler's instantiation. * * @return HandlerInterface */ public function getHandler(array $record = null) { 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"); } } return $this->handler; } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->getHandler()->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->getHandler()->getFormatter(); } } NullHandler.php 0000777 00000001671 14711055564 0007511 0 ustar 00 <?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; /** * Blackhole * * Any record it can handle will be thrown away. This can be used * to put on top of an existing stack to override it temporarily. * * @author Jordi Boggiano <j.boggiano@seld.be> */ class NullHandler extends AbstractHandler { /** * @param int $level The minimum logging level at which this handler will be triggered */ public function __construct($level = Logger::DEBUG) { parent::__construct($level, false); } /** * {@inheritdoc} */ public function handle(array $record) { if ($record['level'] < $this->level) { return false; } return true; } } InsightOpsHandler.php 0000777 00000003522 14711055564 0010663 0 ustar 00 <?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; /** * Inspired on LogEntriesHandler. * * @author Robert Kaufmann III <rok3@rok3.me> * @author Gabriel Machado <gabriel.ms1@hotmail.com> */ class InsightOpsHandler extends SocketHandler { /** * @var string */ protected $logToken; /** * @param string $token Log token supplied by InsightOps * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. * @param bool $useSSL Whether or not SSL encryption should be used * @param int $level The minimum logging level to trigger this handler * @param bool $bubble Whether or not messages that are handled should bubble up the stack. * * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing */ public function __construct($token, $region = 'us', $useSSL = true, $level = Logger::DEBUG, $bubble = true) { if ($useSSL && !extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler'); } $endpoint = $useSSL ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' : $region . '.data.logs.insight.rapid7.com:80'; parent::__construct($endpoint, $level, $bubble); $this->logToken = $token; } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { return $this->logToken . ' ' . $record['formatted']; } } PushoverHandler.php 0000777 00000014742 14711055564 0010415 0 ustar 00 <?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 bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $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 = (bool) $value; } } MissingExtensionException.php 0000777 00000000702 14711055564 0012460 0 ustar 00 <?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; /** * Exception can be thrown if an extension for an handler is missing * * @author Christian Bergau <cbergau86@gmail.com> */ class MissingExtensionException extends \Exception { } PHPConsoleHandler.php 0000777 00000023453 14711055564 0010553 0 ustar 00 <?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 Monolog\Utils; 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 .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true); } $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%'); } } PsrHandler.php 0000777 00000002631 14711055564 0007340 0 ustar 00 <?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 Psr\Log\LoggerInterface; /** * Proxies log messages to an existing PSR-3 compliant logger. * * @author Michael Moussa <michael.moussa@gmail.com> */ class PsrHandler extends AbstractHandler { /** * PSR-3 compliant logger * * @var LoggerInterface */ protected $logger; /** * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied * @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(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true) { parent::__construct($level, $bubble); $this->logger = $logger; } /** * {@inheritDoc} */ public function handle(array $record) { if (!$this->isHandling($record)) { return false; } $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']); return false === $this->bubble; } } MandrillHandler.php 0000777 00000004403 14711055564 0010335 0 ustar 00 <?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 bool $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); if (version_compare(\Swift::VERSION, '6.0.0', '>=')) { $message->setDate(new \DateTimeImmutable()); } else { $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); } } MailHandler.php 0000777 00000003126 14711055564 0007456 0 ustar 00 <?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; /** * Base class for all mail handlers * * @author Gyula Sallai */ abstract class MailHandler extends AbstractProcessingHandler { /** * {@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)) { $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); } } /** * Send a mail with the given content * * @param string $content formatted email body to be sent * @param array $records the array of log records that formed this content */ abstract protected function send($content, array $records); /** * {@inheritdoc} */ protected function write(array $record) { $this->send((string) $record['formatted'], array($record)); } protected function getHighestRecord(array $records) { $highestRecord = null; foreach ($records as $record) { if ($highestRecord === null || $highestRecord['level'] < $record['level']) { $highestRecord = $record; } } return $highestRecord; } } SlackHandler.php 0000777 00000014517 14711055564 0007637 0 ustar 00 <?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\Utils; 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; } public function getToken() { return $this->token; } /** * {@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'] = Utils::jsonEncode($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; } } SyslogUdpHandler.php 0000777 00000006224 14711055564 0010527 0 ustar 00 <?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> * @author Dominik Kukacka <dominik.kukacka@gmail.com> */ class SyslogUdpHandler extends AbstractSyslogHandler { const RFC3164 = 0; const RFC5424 = 1; private $dateFormats = array( self::RFC3164 => 'M d H:i:s', self::RFC5424 => \DateTime::RFC3339, ); protected $socket; protected $ident; protected $rfc; /** * @param string $host * @param int $port * @param mixed $facility * @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 string $ident Program name or tag for each log message. * @param int $rfc RFC to format the message for. */ public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php', $rfc = self::RFC5424) { parent::__construct($facility, $level, $bubble); $this->ident = $ident; $this->rfc = $rfc; $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 or rfc3164) */ protected function makeCommonSyslogHeader($severity) { $priority = $severity + $this->facility; if (!$pid = getmypid()) { $pid = '-'; } if (!$hostname = gethostname()) { $hostname = '-'; } $date = $this->getDateTime(); if ($this->rfc === self::RFC3164) { return "<$priority>" . $date . " " . $hostname . " " . $this->ident . "[" . $pid . "]: "; } else { return "<$priority>1 " . $date . " " . $hostname . " " . $this->ident . " " . $pid . " - - "; } } protected function getDateTime() { return date($this->dateFormats[$this->rfc]); } /** * Inject your own socket, mainly used for testing */ public function setSocket($socket) { $this->socket = $socket; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка