MediaWiki master
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
60class LegacyHandler extends AbstractProcessingHandler {
61
66 protected $uri;
67
73
78 protected $sink;
79
83 protected $error;
84
88 protected $host;
89
93 protected $port;
94
98 protected $prefix;
99
106 public function __construct(
107 $stream,
108 $useLegacyFilter = false,
109 $level = Logger::DEBUG,
110 $bubble = true
111 ) {
112 parent::__construct( $level, $bubble );
113 $this->uri = $stream;
114 $this->useLegacyFilter = $useLegacyFilter;
115 }
116
120 protected function openSink() {
121 if ( !$this->uri ) {
122 throw new LogicException(
123 'Missing stream uri, the stream can not be opened.' );
124 }
125 $this->error = null;
126 set_error_handler( [ $this, 'errorTrap' ] );
127
128 if ( str_starts_with( $this->uri, 'udp:' ) ) {
129 $parsed = parse_url( $this->uri );
130 if ( !isset( $parsed['host'] ) ) {
131 throw new UnexpectedValueException( sprintf(
132 'Udp transport "%s" must specify a host', $this->uri
133 ) );
134 }
135 if ( !isset( $parsed['port'] ) ) {
136 throw new UnexpectedValueException( sprintf(
137 'Udp transport "%s" must specify a port', $this->uri
138 ) );
139 }
140
141 $this->host = $parsed['host'];
142 $this->port = $parsed['port'];
143 $this->prefix = '';
144
145 if ( isset( $parsed['path'] ) ) {
146 $this->prefix = ltrim( $parsed['path'], '/' );
147 }
148
149 if ( filter_var( $this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
150 $domain = AF_INET6;
151
152 } else {
153 $domain = AF_INET;
154 }
155
156 $this->sink = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
157
158 } else {
159 $this->sink = fopen( $this->uri, 'a' );
160 }
161 restore_error_handler();
162
163 if ( !$this->sink ) {
164 $this->sink = null;
165 throw new UnexpectedValueException( sprintf(
166 'The stream or file "%s" could not be opened: %s',
167 // @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal Set by error handler
168 $this->uri, $this->error
169 ) );
170 }
171 }
172
178 protected function errorTrap( $code, $msg ) {
179 $this->error = $msg;
180 }
181
186 protected function useUdp() {
187 return $this->host !== null;
188 }
189
190 protected function write( array $record ): void {
191 if ( $this->useLegacyFilter &&
192 !LegacyLogger::shouldEmit(
193 $record['channel'], $record['message'],
194 $record['level'], $record
195 ) ) {
196 // Do not write record if we are enforcing legacy rules and they
197 // do not pass this message. This used to be done in isHandling(),
198 // but Monolog 1.12.0 made a breaking change that removed access
199 // to the needed channel and context information.
200 return;
201 }
202
203 if ( $this->sink === null ) {
204 $this->openSink();
205 }
206
207 $text = (string)$record['formatted'];
208 if ( $this->useUdp() ) {
209 // Clean it up for the multiplexer
210 if ( $this->prefix !== '' ) {
211 $leader = ( $this->prefix === '{channel}' ) ?
212 $record['channel'] : $this->prefix;
213 $text = preg_replace( '/^/m', "{$leader} ", $text );
214
215 // Limit to 64 KiB
216 if ( strlen( $text ) > 65506 ) {
217 $text = substr( $text, 0, 65506 );
218 }
219
220 if ( !str_ends_with( $text, "\n" ) ) {
221 $text .= "\n";
222 }
223
224 } elseif ( strlen( $text ) > 65507 ) {
225 $text = substr( $text, 0, 65507 );
226 }
227
228 socket_sendto(
229 $this->sink,
230 $text,
231 strlen( $text ),
232 0,
233 $this->host,
234 $this->port
235 );
236
237 } else {
238 fwrite( $this->sink, $text );
239 }
240 }
241
242 public function close(): void {
243 if ( $this->sink ) {
244 if ( $this->useUdp() ) {
245 socket_close( $this->sink );
246 } else {
247 fclose( $this->sink );
248 }
249 }
250 $this->sink = null;
251 }
252}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
PSR-3 logger that mimics the historic implementation of MediaWiki's former wfErrorLog logging impleme...
Monolog imitation of MediaWiki\Logger\LegacyLogger.
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.