SyslogUdpHandler.php
4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<?php declare(strict_types=1);
/*
* 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 DateTimeInterface;
use Monolog\Logger;
use Monolog\Handler\SyslogUdp\UdpSocket;
use Monolog\Utils;
/**
* A Handler for logging to a remote syslogd server.
*
* @author Jesper Skovgaard Nielsen <nulpunkt@gmail.com>
* @author Dominik Kukacka <dominik.kukacka@gmail.com>
*/
class SyslogUdpHandler extends AbstractSyslogHandler
{
const RFC3164 = 0;
const RFC5424 = 1;
const RFC5424e = 2;
/** @var array<self::RFC*, string> */
private $dateFormats = array(
self::RFC3164 => 'M d H:i:s',
self::RFC5424 => \DateTime::RFC3339,
self::RFC5424e => \DateTime::RFC3339_EXTENDED,
);
/** @var UdpSocket */
protected $socket;
/** @var string */
protected $ident;
/** @var self::RFC* */
protected $rfc;
/**
* @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then)
* @param int $port Port number, or 0 if $host is a unix socket
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $ident Program name or tag for each log message.
* @param int $rfc RFC to format the message for.
* @throws MissingExtensionException
*
* @phpstan-param self::RFC* $rfc
*/
public function __construct(string $host, int $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424)
{
if (!extension_loaded('sockets')) {
throw new MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler');
}
parent::__construct($facility, $level, $bubble);
$this->ident = $ident;
$this->rfc = $rfc;
$this->socket = new UdpSocket($host, $port);
}
protected function write(array $record): void
{
$lines = $this->splitMessageIntoLines($record['formatted']);
$header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']], $record['datetime']);
foreach ($lines as $line) {
$this->socket->write($line, $header);
}
}
public function close(): void
{
$this->socket->close();
}
/**
* @param string|string[] $message
* @return string[]
*/
private function splitMessageIntoLines($message): array
{
if (is_array($message)) {
$message = implode("\n", $message);
}
$lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY);
if (false === $lines) {
$pcreErrorCode = preg_last_error();
throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
}
return $lines;
}
/**
* Make common syslog header (see rfc5424 or rfc3164)
*/
protected function makeCommonSyslogHeader(int $severity, DateTimeInterface $datetime): string
{
$priority = $severity + $this->facility;
if (!$pid = getmypid()) {
$pid = '-';
}
if (!$hostname = gethostname()) {
$hostname = '-';
}
if ($this->rfc === self::RFC3164) {
// see https://github.com/phpstan/phpstan/issues/5348
// @phpstan-ignore-next-line
$dateNew = $datetime->setTimezone(new \DateTimeZone('UTC'));
$date = $dateNew->format($this->dateFormats[$this->rfc]);
return "<$priority>" .
$date . " " .
$hostname . " " .
$this->ident . "[" . $pid . "]: ";
}
$date = $datetime->format($this->dateFormats[$this->rfc]);
return "<$priority>1 " .
$date . " " .
$hostname . " " .
$this->ident . " " .
$pid . " - - ";
}
/**
* Inject your own socket, mainly used for testing
*/
public function setSocket(UdpSocket $socket): self
{
$this->socket = $socket;
return $this;
}
}