diff --git a/README.mdown b/README.mdown index 64b4cfe36..168b7ecf8 100644 --- a/README.mdown +++ b/README.mdown @@ -178,6 +178,7 @@ Formatters ---------- - _LineFormatter_: Formats a log record into a one-line string. +- _HtmlFormatter_: Used to format log records into a human readable html table, mainly suitable for emails. - _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded. - _ScalarFormatter_: Used to format log records into an associative array of scalar values. - _JsonFormatter_: Encodes a log record into json. @@ -196,6 +197,7 @@ Processors - _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record. - _ProcessIdProcessor_: Adds the process id to a log record. - _UidProcessor_: Adds a unique identifier to a log record. +- _GitProcessor_: Adds the current git branch and commit to a log record. Utilities --------- diff --git a/src/Monolog/Formatter/ElasticaFormatter.php b/src/Monolog/Formatter/ElasticaFormatter.php index f460abe42..b0b0cf066 100644 --- a/src/Monolog/Formatter/ElasticaFormatter.php +++ b/src/Monolog/Formatter/ElasticaFormatter.php @@ -47,6 +47,7 @@ public function __construct($index, $type) public function format(array $record) { $record = parent::format($record); + return $this->getDocument($record); } @@ -71,7 +72,7 @@ public function getType() /** * Convert a log message into an Elastica Document * - * @param array $record Log message + * @param array $record Log message * @return Document */ protected function getDocument($record) @@ -80,6 +81,7 @@ protected function getDocument($record) $document->setData($record); $document->setType($this->type); $document->setIndex($this->index); + return $document; } } diff --git a/src/Monolog/Formatter/HtmlEmailFormatter.php b/src/Monolog/Formatter/HtmlFormatter.php similarity index 71% rename from src/Monolog/Formatter/HtmlEmailFormatter.php rename to src/Monolog/Formatter/HtmlFormatter.php index 056bf13b9..d853547ba 100644 --- a/src/Monolog/Formatter/HtmlEmailFormatter.php +++ b/src/Monolog/Formatter/HtmlFormatter.php @@ -10,14 +10,16 @@ namespace Monolog\Formatter; +use Monolog\Logger; + /** - * Formats incoming records into a HTML table + * Formats incoming records into an HTML table * * This is especially useful for html email logging * * @author Tiago Brito */ -class HtmlEmailFormatter extends NormalizerFormatter +class HtmlFormatter extends NormalizerFormatter { /** * Translates Monolog log levels to html color priorities. @@ -44,14 +46,14 @@ public function __construct($dateFormat = null) /** * Creates an HTML table row * - * @param $th string Row header content - * @param string $td Row standard cell content + * @param string $th Row header content + * @param string $td Row standard cell content * @return string */ private function addRow($th, $td = ' ') { - $th = htmlspecialchars($th); - $td = '
'.htmlspecialchars($td).'
'; + $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); + $td = '
'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
'; return "\n$th:\n".$td."\n"; } @@ -59,14 +61,14 @@ private function addRow($th, $td = ' ') /** * Create a HTML h1 tag * - * @param $title string Text to be in the h1 - * @param $level integer Error level + * @param string $title Text to be in the h1 + * @param integer $level Error level * @return string */ private function addTitle($title, $level) { - $title = htmlspecialchars($title); - + $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); + return '

'.$title.'

'; } /** @@ -77,15 +79,18 @@ private function addTitle($title, $level) */ public function format(array $record) { - $output = $this->addTitle($this->convertToString($record['level_name']), $record['level']); + $output = $this->addTitle($record['level_name'], $record['level']); $output .= ''; - $output .= $this->addRow('Message', $this->convertToString($record['message'])); - $output .= $this->addRow('Generated at', $this->convertToString($record['datetime'])); - $output .= $this->addRow('Level', $this->convertToString($record['level'])); - $output .= $this->addRow('Channel', $this->convertToString($record['channel'])); - $output .= $this->addRow('Context', $this->convertToString($record['context'])); - $output .= $this->addRow('Extra', $this->convertToString($record['extra'])); + $output .= $this->addRow('Message', (string) $record['message']); + $output .= $this->addRow('Time', $record['datetime']->format('Y-m-d\TH:i:s.uO')); + $output .= $this->addRow('Channel', $record['channel']); + if ($record['context']) { + $output .= $this->addRow('Context', $this->convertToString($record['context'])); + } + if ($record['extra']) { + $output .= $this->addRow('Extra', $this->convertToString($record['extra'])); + } return $output.'
'; } diff --git a/src/Monolog/Formatter/ScalarFormatter.php b/src/Monolog/Formatter/ScalarFormatter.php index 600750303..9e4201ab3 100644 --- a/src/Monolog/Formatter/ScalarFormatter.php +++ b/src/Monolog/Formatter/ScalarFormatter.php @@ -34,7 +34,7 @@ public function format(array $record) } /** - * @param mixed $value + * @param mixed $value * @return mixed */ protected function normalizeValue($value) diff --git a/src/Monolog/Handler/DynamoDbHandler.php b/src/Monolog/Handler/DynamoDbHandler.php index 9a7aa7505..ad6622e1e 100644 --- a/src/Monolog/Handler/DynamoDbHandler.php +++ b/src/Monolog/Handler/DynamoDbHandler.php @@ -39,9 +39,9 @@ class DynamoDbHandler extends AbstractProcessingHandler /** * @param DynamoDbClient $client - * @param string $table - * @param integer $level - * @param boolean $bubble + * @param string $table + * @param integer $level + * @param boolean $bubble */ public function __construct(DynamoDbClient $client, $table, $level = Logger::DEBUG, $bubble = true) { @@ -70,7 +70,7 @@ protected function write(array $record) } /** - * @param array $record + * @param array $record * @return array */ protected function filterEmptyFields(array $record) diff --git a/src/Monolog/Handler/ElasticSearchHandler.php b/src/Monolog/Handler/ElasticSearchHandler.php index 1508e77de..96e5d57f2 100644 --- a/src/Monolog/Handler/ElasticSearchHandler.php +++ b/src/Monolog/Handler/ElasticSearchHandler.php @@ -46,10 +46,10 @@ class ElasticSearchHandler extends AbstractProcessingHandler protected $options = array(); /** - * @param Client $client Elastica Client object - * @param array $options Handler configuration - * @param integer $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 Client $client Elastica Client object + * @param array $options Handler configuration + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true) { @@ -112,7 +112,7 @@ public function handleBatch(array $records) /** * Use Elasticsearch bulk API to send list of documents - * @param array $documents + * @param array $documents * @throws \RuntimeException */ protected function bulkSend(array $documents) diff --git a/src/Monolog/Handler/StreamHandler.php b/src/Monolog/Handler/StreamHandler.php index 521b5751f..281752949 100644 --- a/src/Monolog/Handler/StreamHandler.php +++ b/src/Monolog/Handler/StreamHandler.php @@ -73,7 +73,8 @@ protected function write(array $record) fwrite($this->stream, (string) $record['formatted']); } - private function customErrorHandler($code, $msg) { + private function customErrorHandler($code, $msg) + { $this->errorMessage = preg_replace('{^fopen\(.*?\): }', '', $msg); } } diff --git a/src/Monolog/Processor/GitProcessor.php b/src/Monolog/Processor/GitProcessor.php new file mode 100644 index 000000000..96c2abfef --- /dev/null +++ b/src/Monolog/Processor/GitProcessor.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; + +/** + * Injects Git branch and Git commit SHA in all records + * + * @author Nick Otter + * @author Jordi Boggiano + */ +class GitProcessor +{ + private $level; + private static $cache; + + public function __construct($level = Logger::DEBUG) + { + $this->level = $level; + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['git'] = self::getGitInfo(); + + return $record; + } + + private static function getGitInfo() + { + if (self::$cache) { + return self::$cache; + } + + $branches = `git branch -v --no-abbrev`; + if (preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) { + return self::$cache = array( + 'branch' => $matches[1], + 'commit' => $matches[2], + ); + } + + return self::$cache = array(); + } +} diff --git a/src/Monolog/Registry.php b/src/Monolog/Registry.php new file mode 100644 index 000000000..03cd3eaaa --- /dev/null +++ b/src/Monolog/Registry.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use InvalidArgumentException; + +/** + * Monolog log registry + * + * Allows to get `Logger` instances in the global scope + * via static method calls on this class. + * + * + * $application = new Monolog\Logger('application'); + * $api = new Monolog\Logger('api'); + * + * Monolog\Registry::addLogger($application); + * Monolog\Registry::addLogger($api); + * + * function testLogger() + * { + * Monolog\Registry::api()->addError('Sent to $api Logger instance'); + * Monolog\Registry::application()->addError('Sent to $application Logger instance'); + * } + * + * + * @author Tomas Tatarko + */ +class Registry +{ + /** + * List of all loggers in the registry (ba named indexes) + * + * @var Logger[] + */ + private static $loggers = array(); + + /** + * 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 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 + */ + public static function addLogger(Logger $logger, $name = null, $overwrite = false) + { + $name = $name ?: $logger->getName(); + + if (isset(self::$loggers[$name]) && !$overwrite) { + throw new InvalidArgumentException('Logger with the given name already exists'); + } + + self::$loggers[$name] = $logger; + } + + /** + * Removes instance from registry by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function removeLogger($logger) + { + if ($logger instanceof Logger) { + if (false !== ($idx = array_search($logger, self::$loggers, true))) { + unset(self::$loggers[$idx]); + } + } else { + unset(self::$loggers[$logger]); + } + } + + /** + * Clears the registry + */ + public static function clear() + { + self::$loggers = array(); + } + + /** + * Gets Logger instance from the registry + * + * @param string $name Name of the requested Logger instance + * @return Logger Requested instance of Logger + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function getInstance($name) + { + if (!isset(self::$loggers[$name])) { + throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name)); + } + + return self::$loggers[$name]; + } + + /** + * Gets Logger instance from the registry via static method call + * + * @param string $name Name of the requested Logger instance + * @param array $arguments Arguments passed to static method call + * @return Logger Requested instance of Logger + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function __callStatic($name, $arguments) + { + return self::getInstance($name); + } +} diff --git a/tests/Monolog/Processor/GitProcessorTest.php b/tests/Monolog/Processor/GitProcessorTest.php new file mode 100644 index 000000000..5adb505dc --- /dev/null +++ b/tests/Monolog/Processor/GitProcessorTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\TestCase; + +class GitProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\GitProcessor::__invoke + */ + public function testProcessor() + { + $processor = new GitProcessor(); + $record = $processor($this->getRecord()); + + $this->assertArrayHasKey('git', $record['extra']); + $this->assertTrue(!is_array($record['extra']['git']['branch'])); + } +}