MediaWiki REL1_39
LegacyHandler.php
Go to the documentation of this file.
1<?php
22
23use LogicException;
25use Monolog\Handler\AbstractProcessingHandler;
26use Monolog\Logger;
27use Socket;
28use UnexpectedValueException;
29
50class LegacyHandler extends AbstractProcessingHandler {
51
56 protected $uri;
57
63
68 protected $sink;
69
73 protected $error;
74
78 protected $host;
79
83 protected $port;
84
88 protected $prefix;
89
96 public function __construct(
97 $stream,
98 $useLegacyFilter = false,
99 $level = Logger::DEBUG,
100 $bubble = true
101 ) {
102 parent::__construct( $level, $bubble );
103 $this->uri = $stream;
104 $this->useLegacyFilter = $useLegacyFilter;
105 }
106
110 protected function openSink() {
111 if ( !$this->uri ) {
112 throw new LogicException(
113 'Missing stream uri, the stream can not be opened.' );
114 }
115 $this->error = null;
116 set_error_handler( [ $this, 'errorTrap' ] );
117
118 if ( substr( $this->uri, 0, 4 ) == 'udp:' ) {
119 $parsed = parse_url( $this->uri );
120 if ( !isset( $parsed['host'] ) ) {
121 throw new UnexpectedValueException( sprintf(
122 'Udp transport "%s" must specify a host', $this->uri
123 ) );
124 }
125 if ( !isset( $parsed['port'] ) ) {
126 throw new UnexpectedValueException( sprintf(
127 'Udp transport "%s" must specify a port', $this->uri
128 ) );
129 }
130
131 $this->host = $parsed['host'];
132 $this->port = $parsed['port'];
133 $this->prefix = '';
134
135 if ( isset( $parsed['path'] ) ) {
136 $this->prefix = ltrim( $parsed['path'], '/' );
137 }
138
139 if ( filter_var( $this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
140 $domain = AF_INET6;
141
142 } else {
143 $domain = AF_INET;
144 }
145
146 $this->sink = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
147
148 } else {
149 $this->sink = fopen( $this->uri, 'a' );
150 }
151 restore_error_handler();
152
153 if ( !$this->sink ) {
154 $this->sink = null;
155 throw new UnexpectedValueException( sprintf(
156 'The stream or file "%s" could not be opened: %s',
157 // @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal Set by error handler
158 $this->uri, $this->error
159 ) );
160 }
161 }
162
168 protected function errorTrap( $code, $msg ) {
169 $this->error = $msg;
170 }
171
176 protected function useUdp() {
177 return $this->host !== null;
178 }
179
180 protected function write( array $record ): void {
181 if ( $this->useLegacyFilter &&
182 !LegacyLogger::shouldEmit(
183 $record['channel'], $record['message'],
184 $record['level'], $record
185 ) ) {
186 // Do not write record if we are enforcing legacy rules and they
187 // do not pass this message. This used to be done in isHandling(),
188 // but Monolog 1.12.0 made a breaking change that removed access
189 // to the needed channel and context information.
190 return;
191 }
192
193 if ( $this->sink === null ) {
194 $this->openSink();
195 }
196
197 $text = (string)$record['formatted'];
198 if ( $this->useUdp() ) {
199 // Clean it up for the multiplexer
200 if ( $this->prefix !== '' ) {
201 $leader = ( $this->prefix === '{channel}' ) ?
202 $record['channel'] : $this->prefix;
203 $text = preg_replace( '/^/m', "{$leader} ", $text );
204
205 // Limit to 64 KiB
206 if ( strlen( $text ) > 65506 ) {
207 $text = substr( $text, 0, 65506 );
208 }
209
210 if ( substr( $text, -1 ) != "\n" ) {
211 $text .= "\n";
212 }
213
214 } elseif ( strlen( $text ) > 65507 ) {
215 $text = substr( $text, 0, 65507 );
216 }
217
218 socket_sendto(
219 $this->sink,
220 $text,
221 strlen( $text ),
222 0,
223 $this->host,
224 $this->port
225 );
226
227 } else {
228 fwrite( $this->sink, $text );
229 }
230 }
231
232 public function close(): void {
233 if ( $this->sink ) {
234 if ( $this->useUdp() ) {
235 socket_close( $this->sink );
236 } else {
237 fclose( $this->sink );
238 }
239 }
240 $this->sink = null;
241 }
242}
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition WebStart.php:82
PSR-3 logger that mimics the historic implementation of MediaWiki's former wfErrorLog logging impleme...
Log handler that replicates the behavior of MediaWiki's former wfErrorLog() logging service.
openSink()
Open the log sink described by our stream URI.
__construct( $stream, $useLegacyFilter=false, $level=Logger::DEBUG, $bubble=true)
bool $useLegacyFilter
Filter log events using legacy rules.
errorTrap( $code, $msg)
Custom error handler.
useUdp()
Should we use UDP to send messages to the sink?
string $uri
Log sink descriptor.
Socket resource null $sink
Log sink.