1
0
mirror of https://github.com/Seldaek/monolog.git synced 2025-08-06 13:16:39 +02:00

Merge pull request #2 from Seldaek/master

Update from fork
This commit is contained in:
Tiago Brito
2014-02-19 20:49:41 +00:00
28 changed files with 249 additions and 136 deletions

View File

@@ -5,8 +5,11 @@ php:
- 5.3
- 5.4
- 5.5
- hhvm
before_script:
- composer install --dev --prefer-source
- if [ "`phpenv version-name`" != "hhvm" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
- if [ "`phpenv version-name`" != "hhvm" ]; then echo "extension = amqp.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
- composer install --prefer-source
script: ./vendor/bin/phpunit

View File

@@ -235,6 +235,7 @@ Frameworks Integration
- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin.
- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer.
- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog.
- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog.
Author
------

View File

@@ -19,7 +19,7 @@
"require-dev": {
"phpunit/phpunit": "~3.7.0",
"mlehner/gelf-php": "1.0.*",
"raven/raven": "0.5.*",
"raven/raven": "~0.5",
"ruflin/elastica": "0.90.*",
"doctrine/couchdb": "dev-master",
"aws/aws-sdk-php": "~2.4.8"
@@ -34,7 +34,7 @@
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB"
},
"autoload": {
"psr-0": {"Monolog": "src/"}
"psr-4": {"Monolog\\": "src/Monolog"}
},
"extra": {
"branch-alias": {

View File

@@ -65,7 +65,7 @@ You can now use this handler in your logger:
```php
<?php
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite'));
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')));
// You can now use your logger
$logger->addInfo('My logger is now ready');

View File

@@ -8,7 +8,7 @@ Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packag
and as such installable via [Composer](http://getcomposer.org/).
```bash
php composer.phar require monolog/monolog '~1.4'
php composer.phar require monolog/monolog '~1.7'
```
If you do not use Composer, you can grab the code from GitHub, and use any

View File

@@ -11,6 +11,8 @@
namespace Monolog\Formatter;
use Exception;
/**
* Formats incoming records into a one-line string
*
@@ -24,14 +26,17 @@ class LineFormatter extends NormalizerFormatter
const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
protected $format;
protected $allowInlineLineBreaks;
/**
* @param string $format The format of the message
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format
* @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
*/
public function __construct($format = null, $dateFormat = null)
public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false)
{
$this->format = $format ?: static::SIMPLE_FORMAT;
$this->allowInlineLineBreaks = $allowInlineLineBreaks;
parent::__construct($dateFormat);
}
@@ -45,13 +50,13 @@ class LineFormatter extends NormalizerFormatter
$output = $this->format;
foreach ($vars['extra'] as $var => $val) {
if (false !== strpos($output, '%extra.'.$var.'%')) {
$output = str_replace('%extra.'.$var.'%', $this->convertToString($val), $output);
$output = str_replace('%extra.'.$var.'%', $this->replaceNewlines($this->convertToString($val)), $output);
unset($vars['extra'][$var]);
}
}
foreach ($vars as $var => $val) {
if (false !== strpos($output, '%'.$var.'%')) {
$output = str_replace('%'.$var.'%', $this->convertToString($val), $output);
$output = str_replace('%'.$var.'%', $this->replaceNewlines($this->convertToString($val)), $output);
}
}
@@ -68,37 +73,41 @@ class LineFormatter extends NormalizerFormatter
return $message;
}
protected function normalize($data)
protected function normalizeException(Exception $e)
{
if (is_bool($data) || is_null($data)) {
return var_export($data, true);
}
if ($data instanceof \Exception) {
$previousText = '';
if ($previous = $data->getPrevious()) {
if ($previous = $e->getPrevious()) {
do {
$previousText .= ', '.get_class($previous).': '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
} while ($previous = $previous->getPrevious());
}
return '[object] ('.get_class($data).': '.$data->getMessage().' at '.$data->getFile().':'.$data->getLine().$previousText.')';
}
return parent::normalize($data);
return '[object] ('.get_class($e).': '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
}
protected function convertToString($data)
{
if (null === $data || is_scalar($data)) {
if (null === $data || is_bool($data)) {
return var_export($data, true);
}
if (is_scalar($data)) {
return (string) $data;
}
$data = $this->normalize($data);
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return $this->toJson($data, true);
}
return str_replace('\\/', '/', @json_encode($data));
}
protected function replaceNewlines($str)
{
if ($this->allowInlineLineBreaks) {
return $str;
}
return preg_replace('{[\r\n]+}', ' ', $str);
}
}

View File

@@ -101,7 +101,6 @@ class NormalizerFormatter implements FormatterInterface
);
$trace = $e->getTrace();
array_shift($trace);
foreach ($trace as $frame) {
if (isset($frame['file'])) {
$data['trace'][] = $frame['file'].':'.$frame['line'];

View File

@@ -11,8 +11,6 @@
namespace Monolog\Formatter;
use Monolog\Formatter\NormalizerFormatter;
/**
* Formats data into an associative array of scalar values.
* Objects and arrays will be JSON encoded.

View File

@@ -27,6 +27,16 @@ use Monolog\Logger;
*/
class HipChatHandler extends SocketHandler
{
/**
* 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
*/
@@ -47,6 +57,11 @@ class HipChatHandler extends SocketHandler
*/
private $notify;
/**
* @var string
*/
private $format;
/**
* @param string $token HipChat API Token
* @param string $room The room that should be alerted of the message (Id or Name)
@@ -55,9 +70,14 @@ class HipChatHandler extends SocketHandler
* @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $useSSL Whether to connect via SSL.
* @param string $format The format of the messages (default to text, can be set to html if you have html in the messages)
*/
public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true)
public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text')
{
if (!$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://api.hipchat.com:443' : 'api.hipchat.com:80';
parent::__construct($connectionString, $level, $bubble);
@@ -65,6 +85,7 @@ class HipChatHandler extends SocketHandler
$this->name = $name;
$this->notify = $notify;
$this->room = $room;
$this->format = $format;
}
/**
@@ -93,7 +114,7 @@ class HipChatHandler extends SocketHandler
'room_id' => $this->room,
'notify' => $this->notify,
'message' => $record['formatted'],
'message_format' => 'text',
'message_format' => $this->format,
'color' => $this->getAlertColor($record['level']),
);
@@ -159,13 +180,19 @@ class HipChatHandler extends SocketHandler
return true;
}
$batchRecord = $this->combineRecords($records);
$batchRecords = $this->combineRecords($records);
if (!$this->isHandling($batchRecord)) {
return false;
$handled = false;
foreach ($batchRecords as $batchRecord) {
if ($this->isHandling($batchRecord)) {
$this->write($batchRecord);
$handled = true;
}
}
$this->write($batchRecord);
if (!$handled) {
return false;
}
return false === $this->bubble;
}
@@ -180,6 +207,9 @@ class HipChatHandler extends SocketHandler
*/
private function combineRecords($records)
{
$batchRecord = null;
$batchRecords = array();
$batchedMessages = array();
$messages = array();
$formattedMessages = array();
$level = 0;
@@ -187,12 +217,7 @@ class HipChatHandler extends SocketHandler
$datetime = null;
foreach ($records as $record) {
$record = $this->processRecord($record);
$record['formatted'] = $this->getFormatter()->format($record);
$messages[] = $record['message'];
$formattedMessages[] = $record['formatted'];
if ($record['level'] > $level) {
$level = $record['level'];
@@ -202,18 +227,74 @@ class HipChatHandler extends SocketHandler
if (null === $datetime) {
$datetime = $record['datetime'];
}
}
$messages[] = $record['message'];
$messgeStr = implode(PHP_EOL, $messages);
$formattedMessages[] = $this->getFormatter()->format($record);
$formattedMessageStr = implode('', $formattedMessages);
$batchRecord = array(
'message' => implode(PHP_EOL, $messages),
'formatted' => implode('', $formattedMessages),
'level' => $level,
'level_name' => $levelName,
'datetime' => $datetime,
'message' => $messgeStr,
'formatted' => $formattedMessageStr,
'context' => array(),
'extra' => array(),
);
return $batchRecord;
if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) {
// Pop the last message and implode the remainging 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);
}
}

View File

@@ -107,12 +107,15 @@ class Logger implements LoggerInterface
*/
protected static $timezone;
/**
* @var string
*/
protected $name;
/**
* The handler stack
*
* @var array of Monolog\Handler\HandlerInterface
* @var HandlerInterface[]
*/
protected $handlers;
@@ -121,14 +124,14 @@ class Logger implements LoggerInterface
*
* To process records of a single handler instead, add the processor on that specific handler
*
* @var array of callables
* @var callable[]
*/
protected $processors;
/**
* @param string $name The logging channel
* @param array $handlers Optional stack of handlers, the first one in the array is called first, etc.
* @param array $processors Optional array of processors
* @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc.
* @param callable[] $processors Optional array of processors
*/
public function __construct($name, array $handlers = array(), array $processors = array())
{

View File

@@ -28,9 +28,12 @@ class IntrospectionProcessor
{
private $level;
public function __construct($level = Logger::DEBUG)
private $skipClassesPartials;
public function __construct($level = Logger::DEBUG, array $skipClassesPartials = array('Monolog\\'))
{
$this->level = $level;
$this->skipClassesPartials = $skipClassesPartials;
}
/**
@@ -52,8 +55,15 @@ class IntrospectionProcessor
array_shift($trace);
$i = 0;
while (isset($trace[$i]['class']) && false !== strpos($trace[$i]['class'], 'Monolog\\')) {
while (isset($trace[$i]['class'])) {
foreach ($this->skipClassesPartials as $part) {
if (strpos($trace[$i]['class'], $part) !== false) {
$i++;
continue 2;
}
}
break;
}
// we should have the call source now

View File

@@ -48,7 +48,7 @@ class Registry
* Adds new logging channel to the registry
*
* @param Logger $logger Instance of the logging channel
* @param string $name Name of the logging channel ($logger->getName() by default)
* @param string|null $name Name of the logging channel ($logger->getName() by default)
* @param boolean $overwrite Overwrite instance in the registry if the given name already exists?
* @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists
*/

View File

@@ -42,9 +42,11 @@ class LineFormatterTest extends \PHPUnit_Framework_TestCase
'context' => array(
'foo' => 'bar',
'baz' => 'qux',
'bool' => false,
'null' => null,
)
));
$this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foo {"foo":"bar","baz":"qux"} []'."\n", $message);
$this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foo {"foo":"bar","baz":"qux","bool":false,"null":null} []'."\n", $message);
}
public function testDefFormatExtras()
@@ -148,6 +150,34 @@ class LineFormatterTest extends \PHPUnit_Framework_TestCase
));
$this->assertEquals('['.date('Y-m-d').'] test.CRITICAL: bar [] []'."\n".'['.date('Y-m-d').'] log.WARNING: foo [] []'."\n", $message);
}
public function testFormatShouldStripInlineLineBreaks()
{
$formatter = new LineFormatter(null, 'Y-m-d');
$message = $formatter->format(
array(
'message' => "foo\nbar",
'context' => array(),
'extra' => array(),
)
);
$this->assertRegExp('/foo bar/', $message);
}
public function testFormatShouldNotStripInlineLineBreaksWhenFlagIsSet()
{
$formatter = new LineFormatter(null, 'Y-m-d', true);
$message = $formatter->format(
array(
'message' => "foo\nbar",
'context' => array(),
'extra' => array(),
)
);
$this->assertRegExp('/foo\nbar/', $message);
}
}
class TestFoo

View File

@@ -12,7 +12,6 @@ class ScalarFormatterTest extends \PHPUnit_Framework_TestCase
{
$data = array();
$trace = $e->getTrace();
array_shift($trace);
foreach ($trace as $frame) {
if (isset($frame['file'])) {
$data[] = $frame['file'].':'.$frame['line'];

View File

@@ -1,38 +0,0 @@
<?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;
class AmqpExchangeMock extends \AMQPExchange
{
protected $messages = array();
public function __construct()
{
}
public function publish($message, $routing_key, $params = 0, $attributes = array())
{
$this->messages[] = array($message, $routing_key, $params, $attributes);
return true;
}
public function getMessages()
{
return $this->messages;
}
public function setName($name)
{
return true;
}
}

View File

@@ -32,7 +32,19 @@ class AmqpHandlerTest extends TestCase
public function testHandle()
{
$exchange = $this->getExchange();
$messages = array();
$exchange = $this->getMock('AMQPExchange', array('publish', 'setName'), array(), '', false);
$exchange->expects($this->once())
->method('setName')
->with('log')
;
$exchange->expects($this->any())
->method('publish')
->will($this->returnCallback(function ($message, $routing_key, $flags = 0, $attributes = array()) use (&$messages) {
$messages[] = array($message, $routing_key, $flags, $attributes);
}))
;
$handler = new AmqpHandler($exchange, 'log');
@@ -60,15 +72,9 @@ class AmqpHandlerTest extends TestCase
$handler->handle($record);
$messages = $exchange->getMessages();
$this->assertCount(1, $messages);
$messages[0][0] = json_decode($messages[0][0], true);
unset($messages[0][0]['datetime']);
$this->assertEquals($expected, $messages[0]);
}
protected function getExchange()
{
return new AmqpExchangeMock();
}
}

View File

@@ -21,7 +21,9 @@ class DynamoDbHandlerTest extends TestCase
$this->markTestSkipped('aws/aws-sdk-php not installed');
}
$this->client = $this->getMockBuilder('Aws\DynamoDb\DynamoDbClient')->disableOriginalConstructor()->getMock();
$this->client = $this->getMockBuilder('Aws\DynamoDb\DynamoDbClient')
->setMethods(array('formatAttributes', '__call'))
->disableOriginalConstructor()->getMock();
}
public function testConstruct()

View File

@@ -156,4 +156,12 @@ class HipChatHandlerTest extends TestCase
$this->handler->setFormatter($this->getIdentityFormatter());
}
/**
* @expectedException InvalidArgumentException
*/
public function testCreateWithTooLongName()
{
$hipChatHandler = new \Monolog\Handler\HipChatHandler('token', 'room', 'SixteenCharsHere');
}
}

View File

@@ -26,7 +26,7 @@ class MongoDBHandlerTest extends TestCase
public function testHandle()
{
$mongo = $this->getMock('Mongo', array('selectCollection'));
$mongo = $this->getMock('Mongo', array('selectCollection'), array(), '', false);
$collection = $this->getMock('stdClass', array('save'));
$mongo->expects($this->once())

View File

@@ -16,16 +16,16 @@ class SyslogUdpHandlerTest extends \PHPUnit_Framework_TestCase
public function testWeSplitIntoLines()
{
$handler = new SyslogUdpHandler("127.0.0.1", 514, "local5");
$handler = new SyslogUdpHandler("127.0.0.1", 514, "authpriv");
$handler->setFormatter(new \Monolog\Formatter\ChromePHPFormatter());
$socket = $this->getMock('\Monolog\Handler\SyslogUdp\UdpSocket', array('write'), array('lol', 'lol'));
$socket->expects($this->at(0))
->method('write')
->with("lol", "<172>: ");
->with("lol", "<".(LOG_AUTHPRIV + LOG_WARNING).">: ");
$socket->expects($this->at(1))
->method('write')
->with("hej", "<172>: ");
->with("hej", "<".(LOG_AUTHPRIV + LOG_WARNING).">: ");
$handler->setSocket($socket);

View File

@@ -10,4 +10,6 @@
*/
$loader = require __DIR__ . "/../vendor/autoload.php";
$loader->add('Monolog\\', __DIR__);
$loader->addPsr4('Monolog\\', __DIR__.'/Monolog');
date_default_timezone_set('UTC');