1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the php-gelf package. |
5
|
|
|
* |
6
|
|
|
* (c) Benjamin Zikarsky <http://benjamin-zikarsky.de> |
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 Gelf; |
13
|
|
|
|
14
|
|
|
use Gelf\Transport\UdpTransport; |
15
|
|
|
use Psr\Log\LoggerInterface; |
16
|
|
|
use Psr\Log\AbstractLogger; |
17
|
|
|
use Exception; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* A basic PSR-3 compliant logger |
21
|
|
|
* |
22
|
|
|
* @author Benjamin Zikarsky <[email protected]> |
23
|
|
|
*/ |
24
|
|
|
class Logger extends AbstractLogger implements LoggerInterface |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* @var string|null |
28
|
|
|
*/ |
29
|
|
|
protected $facility; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var array |
33
|
|
|
*/ |
34
|
|
|
protected $defaultContext; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var PublisherInterface |
38
|
|
|
*/ |
39
|
|
|
protected $publisher; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Creates a PSR-3 Logger for GELF/Graylog2 |
43
|
|
|
* |
44
|
|
|
* @param PublisherInterface|null $publisher |
45
|
|
|
* @param string|null $facility |
46
|
|
|
* @param array $defaultContext |
47
|
|
|
*/ |
48
|
16 |
|
public function __construct( |
49
|
|
|
PublisherInterface $publisher = null, |
50
|
|
|
$facility = null, |
51
|
|
|
array $defaultContext = array() |
52
|
|
|
) { |
53
|
|
|
// if no publisher is provided build a "default" publisher |
54
|
|
|
// which is logging via Gelf over UDP to localhost on the default port |
55
|
16 |
|
$this->publisher = $publisher ?: new Publisher(new UdpTransport()); |
56
|
|
|
|
57
|
16 |
|
$this->setFacility($facility); |
58
|
16 |
|
$this->setDefaultContext($defaultContext); |
59
|
16 |
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Publishes a given message and context with given level |
63
|
|
|
* |
64
|
|
|
* @param mixed $level |
65
|
|
|
* @param mixed $rawMessage |
66
|
|
|
* @param array $context |
67
|
|
|
*/ |
68
|
14 |
|
public function log($level, $rawMessage, array $context = array()) |
69
|
|
|
{ |
70
|
14 |
|
$message = $this->initMessage($level, $rawMessage, $context); |
71
|
|
|
|
72
|
|
|
// add exception data if present |
73
|
14 |
|
if (isset($context['exception']) |
74
|
14 |
|
&& $context['exception'] instanceof Exception |
75
|
14 |
|
) { |
76
|
1 |
|
$this->initExceptionData($message, $context['exception']); |
77
|
1 |
|
} |
78
|
|
|
|
79
|
14 |
|
$this->publisher->publish($message); |
80
|
14 |
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Returns the currently used publisher |
84
|
|
|
* |
85
|
|
|
* @return PublisherInterface |
86
|
|
|
*/ |
87
|
1 |
|
public function getPublisher() |
88
|
|
|
{ |
89
|
1 |
|
return $this->publisher; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Sets a new publisher |
94
|
|
|
* |
95
|
|
|
* @param PublisherInterface $publisher |
96
|
|
|
*/ |
97
|
1 |
|
public function setPublisher(PublisherInterface $publisher) |
98
|
|
|
{ |
99
|
1 |
|
$this->publisher = $publisher; |
100
|
1 |
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Returns the faciilty-name used in GELF |
104
|
|
|
* |
105
|
|
|
* @return string|null |
106
|
|
|
*/ |
107
|
1 |
|
public function getFacility() |
108
|
|
|
{ |
109
|
1 |
|
return $this->facility; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Sets the facility for GELF messages |
114
|
|
|
* |
115
|
|
|
* @param string|null $facility |
116
|
|
|
*/ |
117
|
16 |
|
public function setFacility($facility = null) |
118
|
|
|
{ |
119
|
16 |
|
$this->facility = $facility; |
120
|
16 |
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* @return array |
124
|
|
|
*/ |
125
|
14 |
|
public function getDefaultContext() |
126
|
|
|
{ |
127
|
14 |
|
return $this->defaultContext; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param array $defaultContext |
132
|
|
|
*/ |
133
|
16 |
|
public function setDefaultContext($defaultContext) |
134
|
|
|
{ |
135
|
16 |
|
$this->defaultContext = $defaultContext; |
136
|
16 |
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Initializes message-object |
140
|
|
|
* |
141
|
|
|
* @param mixed $level |
142
|
|
|
* @param mixed $message |
143
|
|
|
* @param array $context |
144
|
|
|
* @return Message |
145
|
|
|
*/ |
146
|
14 |
|
protected function initMessage($level, $message, array $context) |
147
|
|
|
{ |
148
|
|
|
// assert that message is a string, and interpolate placeholders |
149
|
14 |
|
$message = (string) $message; |
150
|
14 |
|
$context = $this->initContext($context); |
151
|
14 |
|
$message = self::interpolate($message, $context); |
152
|
|
|
|
153
|
|
|
// create message object |
154
|
14 |
|
$messageObj = new Message(); |
155
|
14 |
|
$messageObj->setLevel($level); |
156
|
14 |
|
$messageObj->setShortMessage($message); |
157
|
14 |
|
$messageObj->setFacility($this->facility); |
158
|
|
|
|
159
|
14 |
|
foreach ($this->getDefaultContext() as $key => $value) { |
160
|
|
|
$messageObj->setAdditional($key, $value); |
161
|
14 |
|
} |
162
|
14 |
|
foreach ($context as $key => $value) { |
163
|
11 |
|
$messageObj->setAdditional($key, $value); |
164
|
14 |
|
} |
165
|
|
|
|
166
|
14 |
|
return $messageObj; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Initializes context array, ensuring all values are string-safe |
171
|
|
|
* |
172
|
|
|
* @param array $context |
173
|
|
|
* @return array |
174
|
|
|
*/ |
175
|
14 |
|
protected function initContext($context) |
176
|
|
|
{ |
177
|
14 |
|
foreach ($context as $key => &$value) { |
178
|
11 |
|
switch (gettype($value)) { |
179
|
11 |
|
case 'string': |
180
|
11 |
|
case 'integer': |
181
|
11 |
|
case 'double': |
182
|
|
|
// These types require no conversion |
183
|
3 |
|
break; |
184
|
8 |
|
case 'array': |
185
|
8 |
|
case 'boolean': |
186
|
3 |
|
$value = json_encode($value); |
187
|
3 |
|
break; |
188
|
5 |
|
case 'object': |
189
|
3 |
|
if (method_exists($value, '__toString')) { |
190
|
2 |
|
$value = (string)$value; |
191
|
2 |
|
} else { |
192
|
1 |
|
$value = '[object (' . get_class($value) . ')]'; |
193
|
|
|
} |
194
|
3 |
|
break; |
195
|
2 |
|
case 'NULL': |
196
|
1 |
|
$value = 'NULL'; |
197
|
1 |
|
break; |
198
|
1 |
|
default: |
199
|
1 |
|
$value = '[' . gettype($value) . ']'; |
200
|
1 |
|
break; |
201
|
11 |
|
} |
202
|
14 |
|
} |
203
|
|
|
|
204
|
14 |
|
return $context; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Initializes Exceptiondata with given message |
209
|
|
|
* |
210
|
|
|
* @param Message $message |
211
|
|
|
* @param Exception $exception |
212
|
|
|
*/ |
213
|
1 |
|
protected function initExceptionData(Message $message, Exception $exception) |
214
|
|
|
{ |
215
|
1 |
|
$message->setLine($exception->getLine()); |
216
|
1 |
|
$message->setFile($exception->getFile()); |
217
|
|
|
|
218
|
1 |
|
$longText = ""; |
|
|
|
|
219
|
|
|
|
220
|
|
|
do { |
221
|
1 |
|
$longText .= sprintf( |
222
|
1 |
|
"%s: %s (%d)\n\n%s\n", |
223
|
1 |
|
get_class($exception), |
224
|
1 |
|
$exception->getMessage(), |
225
|
1 |
|
$exception->getCode(), |
226
|
1 |
|
$exception->getTraceAsString() |
227
|
1 |
|
); |
228
|
|
|
|
229
|
1 |
|
$exception = $exception->getPrevious(); |
230
|
1 |
|
} while ($exception && $longText .= "\n--\n\n"); |
231
|
|
|
|
232
|
1 |
|
$message->setFullMessage($longText); |
233
|
1 |
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Interpolates context values into the message placeholders. |
237
|
|
|
* |
238
|
|
|
* Reference implementation |
239
|
|
|
* @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#12-message |
|
|
|
|
240
|
|
|
* |
241
|
|
|
* @param mixed $message |
242
|
|
|
* @param array $context |
243
|
|
|
* @return string |
244
|
|
|
*/ |
245
|
14 |
|
private static function interpolate($message, array $context) |
246
|
|
|
{ |
247
|
|
|
// build a replacement array with braces around the context keys |
248
|
14 |
|
$replace = array(); |
249
|
14 |
|
foreach ($context as $key => $val) { |
250
|
11 |
|
$replace['{' . $key . '}'] = $val; |
251
|
14 |
|
} |
252
|
|
|
|
253
|
|
|
// interpolate replacement values into the message and return |
254
|
14 |
|
return strtr($message, $replace); |
255
|
|
|
} |
256
|
|
|
} |
|
|
|
|
257
|
|
|
|
PHP provides two ways to mark string literals. Either with single quotes
'literal'
or with double quotes"literal"
. The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (
\'
) and the backslash (\\
). Every other character is displayed as is.Double quoted string literals may contain other variables or more complex escape sequences.
will print an indented:
Single is Value
If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.
For more information on PHP string literals and available escape sequences see the PHP core documentation.