mirror of
https://github.com/Seldaek/monolog.git
synced 2025-08-06 13:16:39 +02:00
@@ -4,6 +4,7 @@
|
||||
|
||||
* Added Monolog\Logger::isHandling() to check if a handler will
|
||||
handle the given log level
|
||||
* Added ChromePhpHandler
|
||||
|
||||
* 1.0.2 (2011-10-24)
|
||||
|
||||
|
@@ -41,6 +41,7 @@ Handlers
|
||||
- _StreamHandler_: Logs records into any php stream, use this for log files.
|
||||
- _RotatingFileHandler_: Logs records to a file and creates one logfile per day. It will also delete files older than $maxFiles. You should use [logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile setups though, this is just meant as a quick and dirty solution.
|
||||
- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing inline `console` messages within [FireBug](http://getfirebug.com/).
|
||||
- _ChromePhpHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing inline `console` messages within Chrome.
|
||||
- _NativeMailHandler_: Sends emails using PHP's mail() function.
|
||||
- _SwiftMailerHandler_: Sends emails using a SwiftMailer instance.
|
||||
- _SyslogHandler_: Logs records to the syslog.
|
||||
@@ -60,6 +61,7 @@ Formatters
|
||||
- _LineFormatter_: Formats a log record into a one-line string.
|
||||
- _JsonFormatter_: Encodes a log record into json.
|
||||
- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler.
|
||||
- _ChromePhpFormatter_: Used to format log records into the ChromePhp format, only useful for the ChromePhpHandler.
|
||||
|
||||
Processors
|
||||
----------
|
||||
|
77
src/Monolog/Formatter/ChromePhpFormatter.php
Normal file
77
src/Monolog/Formatter/ChromePhpFormatter.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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\Formatter;
|
||||
|
||||
use Monolog\Logger;
|
||||
|
||||
/**
|
||||
* Formats a log message according to the ChromePhp array format
|
||||
*
|
||||
* @author Christophe Coevoet <stof@notk.org>
|
||||
*/
|
||||
class ChromePhpFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Translates Monolog log levels to Wildfire levels.
|
||||
*/
|
||||
private $logLevels = array(
|
||||
Logger::DEBUG => 'log',
|
||||
Logger::INFO => 'info',
|
||||
Logger::WARNING => 'warn',
|
||||
Logger::ERROR => 'error',
|
||||
Logger::CRITICAL => 'error',
|
||||
Logger::ALERT => 'error',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function format(array $record)
|
||||
{
|
||||
// Retrieve the line and file if set and remove them from the formatted extra
|
||||
$backtrace = 'unknown';
|
||||
if (isset($record['extra']['file']) && isset($record['extra']['line'])) {
|
||||
$backtrace = $record['extra']['file'].' : '.$record['extra']['line'];
|
||||
unset($record['extra']['file']);
|
||||
unset($record['extra']['line']);
|
||||
}
|
||||
|
||||
$message = array('message' => $record['message']);
|
||||
if ($record['context']) {
|
||||
$message['context'] = $record['context'];
|
||||
}
|
||||
if ($record['extra']) {
|
||||
$message['extra'] = $record['extra'];
|
||||
}
|
||||
if (count($message) === 1) {
|
||||
$message = reset($message);
|
||||
}
|
||||
|
||||
return array(
|
||||
$record['channel'],
|
||||
$message,
|
||||
$backtrace,
|
||||
$this->logLevels[$record['level']],
|
||||
);
|
||||
}
|
||||
|
||||
public function formatBatch(array $records)
|
||||
{
|
||||
$formatted = array();
|
||||
|
||||
foreach ($records as $record) {
|
||||
$formatted[] = $this->format($record);
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
}
|
127
src/Monolog/Handler/ChromePhpHandler.php
Normal file
127
src/Monolog/Handler/ChromePhpHandler.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Monolog package.
|
||||
*
|
||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Monolog\Handler;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Formatter\ChromePhpFormatter;
|
||||
|
||||
/**
|
||||
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
|
||||
*
|
||||
* @author Christophe Coevoet <stof@notk.org>
|
||||
*/
|
||||
class ChromePhpHandler extends AbstractProcessingHandler
|
||||
{
|
||||
/**
|
||||
* Version of the extension
|
||||
*/
|
||||
const VERSION = '3.0';
|
||||
|
||||
/**
|
||||
* Header name
|
||||
*/
|
||||
const HEADER_NAME = 'X-ChromePhp-Data';
|
||||
|
||||
static protected $initialized = false;
|
||||
|
||||
static protected $json = array(
|
||||
'version' => self::VERSION,
|
||||
'columns' => array('label', 'log', 'backtrace', 'type'),
|
||||
'rows' => array(),
|
||||
);
|
||||
|
||||
protected $sendHeaders = true;
|
||||
|
||||
/**
|
||||
* {@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::$initialized) {
|
||||
$this->sendHeaders = $this->headersAccepted();
|
||||
self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
||||
|
||||
self::$initialized = true;
|
||||
}
|
||||
|
||||
$this->sendHeader(self::HEADER_NAME, base64_encode(utf8_encode(json_encode(self::$json))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send header string to the client
|
||||
*
|
||||
* @param string $header
|
||||
* @param string $content
|
||||
*/
|
||||
protected function sendHeader($header, $content)
|
||||
{
|
||||
if (!headers_sent() && $this->sendHeaders) {
|
||||
header(sprintf('%s: %s', $header, $content));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the headers are accepted by the current user agent
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function headersAccepted()
|
||||
{
|
||||
return !isset($_SERVER['HTTP_USER_AGENT'])
|
||||
|| preg_match('{\bChrome/\d+[\.\d+]*\b}', $_SERVER['HTTP_USER_AGENT']);
|
||||
}
|
||||
}
|
158
tests/Monolog/Formatter/ChromePhpFormatterTest.php
Normal file
158
tests/Monolog/Formatter/ChromePhpFormatterTest.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?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\Formatter;
|
||||
|
||||
use Monolog\Logger;
|
||||
|
||||
class ChromePhpFormatterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @covers Monolog\Formatter\ChromePhpFormatter::format
|
||||
*/
|
||||
public function testDefaultFormat()
|
||||
{
|
||||
$formatter = new ChromePhpFormatter();
|
||||
$record = array(
|
||||
'level' => Logger::ERROR,
|
||||
'level_name' => 'ERROR',
|
||||
'channel' => 'meh',
|
||||
'context' => array('from' => 'logger'),
|
||||
'datetime' => new \DateTime("@0"),
|
||||
'extra' => array('ip' => '127.0.0.1'),
|
||||
'message' => 'log',
|
||||
);
|
||||
|
||||
$message = $formatter->format($record);
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'meh',
|
||||
array(
|
||||
'message' => 'log',
|
||||
'context' => array('from' => 'logger'),
|
||||
'extra' => array('ip' => '127.0.0.1'),
|
||||
),
|
||||
'unknown',
|
||||
'error'
|
||||
),
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Monolog\Formatter\ChromePhpFormatter::format
|
||||
*/
|
||||
public function testFormatWithFileAndLine()
|
||||
{
|
||||
$formatter = new ChromePhpFormatter();
|
||||
$record = array(
|
||||
'level' => Logger::CRITICAL,
|
||||
'level_name' => 'CRITICAL',
|
||||
'channel' => 'meh',
|
||||
'context' => array('from' => 'logger'),
|
||||
'datetime' => new \DateTime("@0"),
|
||||
'extra' => array('ip' => '127.0.0.1', 'file' => 'test', 'line' => 14),
|
||||
'message' => 'log',
|
||||
);
|
||||
|
||||
$message = $formatter->format($record);
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'meh',
|
||||
array(
|
||||
'message' => 'log',
|
||||
'context' => array('from' => 'logger'),
|
||||
'extra' => array('ip' => '127.0.0.1'),
|
||||
),
|
||||
'test : 14',
|
||||
'error'
|
||||
),
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Monolog\Formatter\ChromePhpFormatter::format
|
||||
*/
|
||||
public function testFormatWithoutContext()
|
||||
{
|
||||
$formatter = new ChromePhpFormatter();
|
||||
$record = array(
|
||||
'level' => Logger::DEBUG,
|
||||
'level_name' => 'DEBUG',
|
||||
'channel' => 'meh',
|
||||
'context' => array(),
|
||||
'datetime' => new \DateTime("@0"),
|
||||
'extra' => array(),
|
||||
'message' => 'log',
|
||||
);
|
||||
|
||||
$message = $formatter->format($record);
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'meh',
|
||||
'log',
|
||||
'unknown',
|
||||
'log'
|
||||
),
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Monolog\Formatter\ChromePhpFormatter::formatBatch
|
||||
*/
|
||||
public function testBatchFormatThrowException()
|
||||
{
|
||||
$formatter = new ChromePhpFormatter();
|
||||
$records = array(
|
||||
array(
|
||||
'level' => Logger::INFO,
|
||||
'level_name' => 'INFO',
|
||||
'channel' => 'meh',
|
||||
'context' => array(),
|
||||
'datetime' => new \DateTime("@0"),
|
||||
'extra' => array(),
|
||||
'message' => 'log',
|
||||
),
|
||||
array(
|
||||
'level' => Logger::WARNING,
|
||||
'level_name' => 'WARNING',
|
||||
'channel' => 'foo',
|
||||
'context' => array(),
|
||||
'datetime' => new \DateTime("@0"),
|
||||
'extra' => array(),
|
||||
'message' => 'log2',
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
array(
|
||||
'meh',
|
||||
'log',
|
||||
'unknown',
|
||||
'info'
|
||||
),
|
||||
array(
|
||||
'foo',
|
||||
'log2',
|
||||
'unknown',
|
||||
'warn'
|
||||
),
|
||||
),
|
||||
$formatter->formatBatch($records)
|
||||
);
|
||||
}
|
||||
}
|
@@ -20,11 +20,13 @@ spl_autoload_register(function($class)
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\FirePHPHandler;
|
||||
use Monolog\Handler\ChromePhpHandler;
|
||||
|
||||
$logger = new Logger('firephp');
|
||||
$logger->pushHandler(new FirePHPHandler);
|
||||
$logger->pushHandler(new ChromePhpHandler());
|
||||
|
||||
$logger->addDebug('Debug');
|
||||
$logger->addInfo('Info');
|
||||
$logger->addWarning('Warning');
|
||||
$logger->addError('Error');
|
||||
$logger->addError('Error');
|
||||
|
98
tests/Monolog/Handler/ChromePhpHandlerTest.php
Normal file
98
tests/Monolog/Handler/ChromePhpHandlerTest.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?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\TestCase;
|
||||
use Monolog\Logger;
|
||||
|
||||
/**
|
||||
* @covers Monolog\Handler\ChromePhpHandler
|
||||
*/
|
||||
class ChromePhpHandlerTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
TestChromePhpHandler::reset();
|
||||
}
|
||||
|
||||
public function testHeaders()
|
||||
{
|
||||
$handler = new TestChromePhpHandler();
|
||||
$handler->setFormatter($this->getIdentityFormatter());
|
||||
$handler->handle($this->getRecord(Logger::DEBUG));
|
||||
$handler->handle($this->getRecord(Logger::WARNING));
|
||||
|
||||
$expected = array(
|
||||
'X-ChromePhp-Data' => base64_encode(utf8_encode(json_encode(array(
|
||||
'version' => ChromePhpHandler::VERSION,
|
||||
'columns' => array('label', 'log', 'backtrace', 'type'),
|
||||
'rows' => array(
|
||||
'test',
|
||||
'test',
|
||||
),
|
||||
'request_uri' => '',
|
||||
))))
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $handler->getHeaders());
|
||||
}
|
||||
|
||||
public function testConcurrentHandlers()
|
||||
{
|
||||
$handler = new TestChromePhpHandler();
|
||||
$handler->setFormatter($this->getIdentityFormatter());
|
||||
$handler->handle($this->getRecord(Logger::DEBUG));
|
||||
$handler->handle($this->getRecord(Logger::WARNING));
|
||||
|
||||
$handler2 = new TestChromePhpHandler();
|
||||
$handler2->setFormatter($this->getIdentityFormatter());
|
||||
$handler2->handle($this->getRecord(Logger::DEBUG));
|
||||
$handler2->handle($this->getRecord(Logger::WARNING));
|
||||
|
||||
$expected = array(
|
||||
'X-ChromePhp-Data' => base64_encode(utf8_encode(json_encode(array(
|
||||
'version' => ChromePhpHandler::VERSION,
|
||||
'columns' => array('label', 'log', 'backtrace', 'type'),
|
||||
'rows' => array(
|
||||
'test',
|
||||
'test',
|
||||
'test',
|
||||
'test',
|
||||
),
|
||||
'request_uri' => '',
|
||||
))))
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $handler2->getHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
class TestChromePhpHandler extends ChromePhpHandler
|
||||
{
|
||||
protected $headers = array();
|
||||
|
||||
public static function reset()
|
||||
{
|
||||
self::$initialized = false;
|
||||
self::$json['rows'] = array();
|
||||
}
|
||||
|
||||
protected function sendHeader($header, $content)
|
||||
{
|
||||
$this->headers[$header] = $content;
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user