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

Merge pull request #58 from stof/chromephp

Added a ChromePhpHandler.
This commit is contained in:
Jordi Boggiano
2012-02-20 08:21:54 -08:00
7 changed files with 466 additions and 1 deletions

View File

@@ -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)

View File

@@ -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
----------

View 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;
}
}

View 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']);
}
}

View 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)
);
}
}

View File

@@ -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');

View 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;
}
}