mirror of
https://github.com/Seldaek/monolog.git
synced 2025-10-25 10:36:33 +02:00
Merge branch 'master' into feature/elasticsearch
# Conflicts: # composer.json # src/Monolog/Handler/ElasticSearchHandler.php
This commit is contained in:
@@ -62,6 +62,7 @@ class FluentdFormatter implements FormatterInterface
|
||||
|
||||
$message = [
|
||||
'message' => $record['message'],
|
||||
'context' => $record['context'],
|
||||
'extra' => $record['extra'],
|
||||
];
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class HtmlFormatter extends NormalizerFormatter
|
||||
$td = '<pre>'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'</pre>';
|
||||
}
|
||||
|
||||
return "<tr style=\"padding: 4px;spacing: 0;text-align: left;\">\n<th style=\"background: #cccccc\" width=\"100px\">$th:</th>\n<td style=\"padding: 4px;spacing: 0;text-align: left;background: #eeeeee\">".$td."</td>\n</tr>";
|
||||
return "<tr style=\"padding: 4px;text-align: left;\">\n<th style=\"vertical-align: top;background: #ccc;color: #000\" width=\"100\">$th:</th>\n<td style=\"padding: 4px;text-align: left;vertical-align: top;background: #eee;color: #000\">".$td."</td>\n</tr>";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,7 +64,15 @@ class JsonFormatter extends NormalizerFormatter
|
||||
*/
|
||||
public function format(array $record): string
|
||||
{
|
||||
return $this->toJson($this->normalize($record), true) . ($this->appendNewline ? "\n" : '');
|
||||
$normalized = $this->normalize($record);
|
||||
if (isset($normalized['context']) && $normalized['context'] === []) {
|
||||
$normalized['context'] = new \stdClass;
|
||||
}
|
||||
if (isset($normalized['extra']) && $normalized['extra'] === []) {
|
||||
$normalized['extra'] = new \stdClass;
|
||||
}
|
||||
|
||||
return $this->toJson($normalized, true) . ($this->appendNewline ? "\n" : '');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,8 +130,8 @@ class JsonFormatter extends NormalizerFormatter
|
||||
*/
|
||||
protected function normalize($data, int $depth = 0)
|
||||
{
|
||||
if ($depth > 9) {
|
||||
return 'Over 9 levels deep, aborting normalization';
|
||||
if ($depth > $this->maxNormalizeDepth) {
|
||||
return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization';
|
||||
}
|
||||
|
||||
if (is_array($data) || $data instanceof \Traversable) {
|
||||
@@ -131,10 +139,11 @@ class JsonFormatter extends NormalizerFormatter
|
||||
|
||||
$count = 1;
|
||||
foreach ($data as $key => $value) {
|
||||
if ($count++ >= 1000) {
|
||||
$normalized['...'] = 'Over 1000 items, aborting normalization';
|
||||
if ($count++ > $this->maxNormalizeItemCount) {
|
||||
$normalized['...'] = 'Over '.$this->maxNormalizeItemCount.' items ('.count($data).' total), aborting normalization';
|
||||
break;
|
||||
}
|
||||
|
||||
$normalized[$key] = $this->normalize($value, $depth + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,18 +126,14 @@ class LineFormatter extends NormalizerFormatter
|
||||
|
||||
protected function normalizeException(\Throwable $e, int $depth = 0): string
|
||||
{
|
||||
$previousText = '';
|
||||
$str = $this->formatException($e);
|
||||
|
||||
if ($previous = $e->getPrevious()) {
|
||||
do {
|
||||
$previousText .= ', '.get_class($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
|
||||
$str .= "\n[previous exception] " . $this->formatException($previous);
|
||||
} while ($previous = $previous->getPrevious());
|
||||
}
|
||||
|
||||
$str = '[object] ('.get_class($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
|
||||
if ($this->includeStacktraces) {
|
||||
$str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n";
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
@@ -166,4 +162,14 @@ class LineFormatter extends NormalizerFormatter
|
||||
|
||||
return str_replace(["\r\n", "\r", "\n"], ' ', $str);
|
||||
}
|
||||
|
||||
private function formatException(\Throwable $e): string
|
||||
{
|
||||
$str = '[object] (' . get_class($e) . '(code: ' . $e->getCode() . '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')';
|
||||
if ($this->includeStacktraces) {
|
||||
$str .= "\n[stacktrace]\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace Monolog\Formatter;
|
||||
/**
|
||||
* Serializes a log message to Logstash Event Format
|
||||
*
|
||||
* @see http://logstash.net/
|
||||
* @see https://github.com/elastic/logstash/blob/master/logstash-core-event/lib/logstash/event.rb
|
||||
* @see https://www.elastic.co/products/logstash
|
||||
* @see https://github.com/elastic/logstash/blob/master/logstash-core/src/main/java/org/logstash/Event.java
|
||||
*
|
||||
* @author Tim Mower <timothy.mower@gmail.com>
|
||||
*/
|
||||
@@ -32,30 +32,30 @@ class LogstashFormatter extends NormalizerFormatter
|
||||
protected $applicationName;
|
||||
|
||||
/**
|
||||
* @var string a prefix for 'extra' fields from the Monolog record (optional)
|
||||
* @var string the key for 'extra' fields from the Monolog record
|
||||
*/
|
||||
protected $extraPrefix;
|
||||
protected $extraKey;
|
||||
|
||||
/**
|
||||
* @var string a prefix for 'context' fields from the Monolog record (optional)
|
||||
* @var string the key for 'context' fields from the Monolog record
|
||||
*/
|
||||
protected $contextPrefix;
|
||||
protected $contextKey;
|
||||
|
||||
/**
|
||||
* @param string $applicationName the application that sends the data, used as the "type" field of logstash
|
||||
* @param string $systemName the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine
|
||||
* @param string $extraPrefix prefix for extra keys inside logstash "fields"
|
||||
* @param string $contextPrefix prefix for context keys inside logstash "fields", defaults to ctxt_
|
||||
* @param string $extraKey the key for extra keys inside logstash "fields", defaults to extra
|
||||
* @param string $contextKey the key for context keys inside logstash "fields", defaults to context
|
||||
*/
|
||||
public function __construct(string $applicationName, string $systemName = null, string $extraPrefix = null, string $contextPrefix = 'ctxt_')
|
||||
public function __construct(string $applicationName, string $systemName = null, string $extraKey = 'extra', string $contextKey = 'context')
|
||||
{
|
||||
// logstash requires a ISO 8601 format date with optional millisecond precision.
|
||||
parent::__construct('Y-m-d\TH:i:s.uP');
|
||||
|
||||
$this->systemName = $systemName ?: gethostname();
|
||||
$this->applicationName = $applicationName;
|
||||
$this->extraPrefix = $extraPrefix;
|
||||
$this->contextPrefix = $contextPrefix;
|
||||
$this->extraKey = $extraKey;
|
||||
$this->contextKey = $contextKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,10 +90,10 @@ class LogstashFormatter extends NormalizerFormatter
|
||||
$message['type'] = $this->applicationName;
|
||||
}
|
||||
if (!empty($record['extra'])) {
|
||||
$message[$this->extraPrefix.'extra'] = $record['extra'];
|
||||
$message[$this->extraKey] = $record['extra'];
|
||||
}
|
||||
if (!empty($record['context'])) {
|
||||
$message[$this->contextPrefix.'context'] = $record['context'];
|
||||
$message[$this->contextKey] = $record['context'];
|
||||
}
|
||||
|
||||
return $this->toJson($message) . "\n";
|
||||
|
||||
@@ -24,6 +24,8 @@ class NormalizerFormatter implements FormatterInterface
|
||||
const SIMPLE_DATE = "Y-m-d\TH:i:sP";
|
||||
|
||||
protected $dateFormat;
|
||||
protected $maxNormalizeDepth = 9;
|
||||
protected $maxNormalizeItemCount = 1000;
|
||||
|
||||
/**
|
||||
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format
|
||||
@@ -56,14 +58,40 @@ class NormalizerFormatter implements FormatterInterface
|
||||
return $records;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum number of normalization levels to go through
|
||||
*/
|
||||
public function getMaxNormalizeDepth(): int
|
||||
{
|
||||
return $this->maxNormalizeDepth;
|
||||
}
|
||||
|
||||
public function setMaxNormalizeDepth(int $maxNormalizeDepth): void
|
||||
{
|
||||
$this->maxNormalizeDepth = $maxNormalizeDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum number of items to normalize per level
|
||||
*/
|
||||
public function getMaxNormalizeItemCount(): int
|
||||
{
|
||||
return $this->maxNormalizeItemCount;
|
||||
}
|
||||
|
||||
public function setMaxNormalizeItemCount(int $maxNormalizeItemCount): void
|
||||
{
|
||||
$this->maxNormalizeItemCount = $maxNormalizeItemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @return int|bool|string|null|array
|
||||
*/
|
||||
protected function normalize($data, int $depth = 0)
|
||||
{
|
||||
if ($depth > 9) {
|
||||
return 'Over 9 levels deep, aborting normalization';
|
||||
if ($depth > $this->maxNormalizeDepth) {
|
||||
return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
|
||||
}
|
||||
|
||||
if (null === $data || is_scalar($data)) {
|
||||
@@ -84,10 +112,11 @@ class NormalizerFormatter implements FormatterInterface
|
||||
|
||||
$count = 1;
|
||||
foreach ($data as $key => $value) {
|
||||
if ($count++ >= 1000) {
|
||||
$normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
|
||||
if ($count++ > $this->maxNormalizeItemCount) {
|
||||
$normalized['...'] = 'Over ' . $this->maxNormalizeItemCount . ' items ('.count($data).' total), aborting normalization';
|
||||
break;
|
||||
}
|
||||
|
||||
$normalized[$key] = $this->normalize($value, $depth + 1);
|
||||
}
|
||||
|
||||
@@ -158,9 +187,20 @@ class NormalizerFormatter implements FormatterInterface
|
||||
if (isset($frame['file'])) {
|
||||
$data['trace'][] = $frame['file'].':'.$frame['line'];
|
||||
} elseif (isset($frame['function']) && $frame['function'] === '{closure}') {
|
||||
// We should again normalize the frames, because it might contain invalid items
|
||||
// Simplify closures handling
|
||||
$data['trace'][] = $frame['function'];
|
||||
} else {
|
||||
if (isset($frame['args'])) {
|
||||
// Make sure that objects present as arguments are not serialized nicely but rather only
|
||||
// as a class name to avoid any unexpected leak of sensitive information
|
||||
$frame['args'] = array_map(function ($arg) {
|
||||
if (is_object($arg) && !$arg instanceof \DateTimeInterface) {
|
||||
return sprintf("[object] (%s)", get_class($arg));
|
||||
}
|
||||
|
||||
return $arg;
|
||||
}, $frame['args']);
|
||||
}
|
||||
// We should again normalize the frames, because it might contain invalid items
|
||||
$data['trace'][] = $this->toJson($this->normalize($frame, $depth + 1), true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user