1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Monolog package. |
5
|
|
|
* |
6
|
|
|
* (c) Jordi Boggiano <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Monolog\Formatter; |
13
|
|
|
|
14
|
|
|
use Monolog\Logger; |
15
|
|
|
use Gelf\Message; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Serializes a log message to GELF |
19
|
|
|
* @see http://www.graylog2.org/about/gelf |
20
|
|
|
* |
21
|
|
|
* @author Matt Lehner <[email protected]> |
22
|
|
|
*/ |
23
|
|
|
class GelfMessageFormatter extends NormalizerFormatter |
24
|
|
|
{ |
25
|
|
|
const DEFAULT_MAX_LENGTH = 32766; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var string the name of the system for the Gelf log message |
29
|
|
|
*/ |
30
|
|
|
protected $systemName; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var string a prefix for 'extra' fields from the Monolog record (optional) |
34
|
|
|
*/ |
35
|
|
|
protected $extraPrefix; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var string a prefix for 'context' fields from the Monolog record (optional) |
39
|
|
|
*/ |
40
|
|
|
protected $contextPrefix; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var int max length per field |
44
|
|
|
*/ |
45
|
|
|
protected $maxLength; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Translates Monolog log levels to Graylog2 log priorities. |
49
|
|
|
*/ |
50
|
|
|
private $logLevels = array( |
51
|
|
|
Logger::DEBUG => 7, |
52
|
|
|
Logger::INFO => 6, |
53
|
|
|
Logger::NOTICE => 5, |
54
|
|
|
Logger::WARNING => 4, |
55
|
|
|
Logger::ERROR => 3, |
56
|
|
|
Logger::CRITICAL => 2, |
57
|
|
|
Logger::ALERT => 1, |
58
|
|
|
Logger::EMERGENCY => 0, |
59
|
|
|
); |
60
|
|
|
|
61
|
|
|
public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $maxLength = null) |
62
|
|
|
{ |
63
|
|
|
parent::__construct('U.u'); |
64
|
|
|
|
65
|
|
|
$this->systemName = $systemName ?: gethostname(); |
66
|
|
|
|
67
|
|
|
$this->extraPrefix = $extraPrefix; |
68
|
|
|
$this->contextPrefix = $contextPrefix; |
69
|
|
|
$this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* {@inheritdoc} |
74
|
|
|
*/ |
75
|
|
|
public function format(array $record) |
76
|
|
|
{ |
77
|
|
|
$record = parent::format($record); |
78
|
|
|
|
79
|
|
|
if (!isset($record['datetime'], $record['message'], $record['level'])) { |
80
|
|
|
throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given'); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
$message = new Message(); |
84
|
|
|
$message |
85
|
|
|
->setTimestamp($record['datetime']) |
86
|
|
|
->setShortMessage((string) $record['message']) |
87
|
|
|
->setHost($this->systemName) |
88
|
|
|
->setLevel($this->logLevels[$record['level']]); |
89
|
|
|
|
90
|
|
|
// message length + system name length + 200 for padding / metadata |
91
|
|
|
$len = 200 + strlen((string) $record['message']) + strlen($this->systemName); |
92
|
|
|
|
93
|
|
|
if ($len > $this->maxLength) { |
94
|
|
|
$message->setShortMessage(substr($record['message'], 0, $this->maxLength)); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
if (isset($record['channel'])) { |
98
|
|
|
$message->setFacility($record['channel']); |
99
|
|
|
} |
100
|
|
|
if (isset($record['extra']['line'])) { |
101
|
|
|
$message->setLine($record['extra']['line']); |
102
|
|
|
unset($record['extra']['line']); |
103
|
|
|
} |
104
|
|
View Code Duplication |
if (isset($record['extra']['file'])) { |
|
|
|
|
105
|
|
|
$message->setFile($record['extra']['file']); |
106
|
|
|
unset($record['extra']['file']); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
View Code Duplication |
foreach ($record['extra'] as $key => $val) { |
|
|
|
|
110
|
|
|
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val); |
111
|
|
|
$len = strlen($this->extraPrefix . $key . $val); |
112
|
|
|
if ($len > $this->maxLength) { |
113
|
|
|
$message->setAdditional($this->extraPrefix . $key, substr($val, 0, $this->maxLength)); |
114
|
|
|
break; |
115
|
|
|
} |
116
|
|
|
$message->setAdditional($this->extraPrefix . $key, $val); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
View Code Duplication |
foreach ($record['context'] as $key => $val) { |
|
|
|
|
120
|
|
|
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val); |
121
|
|
|
$len = strlen($this->contextPrefix . $key . $val); |
122
|
|
|
if ($len > $this->maxLength) { |
123
|
|
|
$message->setAdditional($this->contextPrefix . $key, substr($val, 0, $this->maxLength)); |
124
|
|
|
break; |
125
|
|
|
} |
126
|
|
|
$message->setAdditional($this->contextPrefix . $key, $val); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
if (null === $message->getFile() && isset($record['context']['exception']['file'])) { |
130
|
|
|
if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) { |
131
|
|
|
$message->setFile($matches[1]); |
132
|
|
|
$message->setLine($matches[2]); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return $message; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.