MediaWiki master
LegacyHandler.php
Go to the documentation of this file.
1<?php
8
9use LogicException;
11use Monolog\Handler\AbstractProcessingHandler;
12use Monolog\Logger;
13use Socket;
14use UnexpectedValueException;
15
46class LegacyHandler extends AbstractProcessingHandler {
47
52 protected $uri;
53
59
64 protected $sink;
65
69 protected $error;
70
74 protected $host;
75
79 protected $port;
80
84 protected $prefix;
85
92 public function __construct(
93 $stream,
94 $useLegacyFilter = false,
95 $level = Logger::DEBUG,
96 $bubble = true
97 ) {
98 parent::__construct( $level, $bubble );
99 $this->uri = $stream;
100 $this->useLegacyFilter = $useLegacyFilter;
101 }
102
106 protected function openSink() {
107 if ( !$this->uri ) {
108 throw new LogicException(
109 'Missing stream uri, the stream can not be opened.' );
110 }
111 $this->error = null;
112 set_error_handler( $this->errorTrap( ... ) );
113
114 if ( str_starts_with( $this->uri, 'udp:' ) ) {
115 $parsed = parse_url( $this->uri );
116 if ( !isset( $parsed['host'] ) ) {
117 throw new UnexpectedValueException( sprintf(
118 'Udp transport "%s" must specify a host', $this->uri
119 ) );
120 }
121 if ( !isset( $parsed['port'] ) ) {
122 throw new UnexpectedValueException( sprintf(
123 'Udp transport "%s" must specify a port', $this->uri
124 ) );
125 }
126
127 $this->host = $parsed['host'];
128 $this->port = $parsed['port'];
129 $this->prefix = '';
130
131 if ( isset( $parsed['path'] ) ) {
132 $this->prefix = ltrim( $parsed['path'], '/' );
133 }
134
135 if ( filter_var( $this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
136 $domain = AF_INET6;
137
138 } else {
139 $domain = AF_INET;
140 }
141
142 $this->sink = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
143
144 } else {
145 $this->sink = fopen( $this->uri, 'a' );
146 }
147 restore_error_handler();
148
149 if ( !$this->sink ) {
150 $this->sink = null;
151 throw new UnexpectedValueException( sprintf(
152 'The stream or file "%s" could not be opened: %s',
153 // @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal Set by error handler
154 $this->uri, $this->error
155 ) );
156 }
157 }
158
164 protected function errorTrap( $code, $msg ) {
165 $this->error = $msg;
166 }
167
172 protected function useUdp() {
173 return $this->host !== null;
174 }
175
176 protected function write( array $record ): void {
177 if ( $this->useLegacyFilter &&
178 !LegacyLogger::shouldEmit(
179 $record['channel'], $record['message'],
180 $record['level'], $record
181 ) ) {
182 // Do not write record if we are enforcing legacy rules and they
183 // do not pass this message. This used to be done in isHandling(),
184 // but Monolog 1.12.0 made a breaking change that removed access
185 // to the needed channel and context information.
186 return;
187 }
188
189 if ( $this->sink === null ) {
190 $this->openSink();
191 }
192
193 $text = (string)$record['formatted'];
194 if ( $this->useUdp() ) {
195 // Clean it up for the multiplexer
196 if ( $this->prefix !== '' ) {
197 $leader = ( $this->prefix === '{channel}' ) ?
198 $record['channel'] : $this->prefix;
199 $text = preg_replace( '/^/m', "{$leader} ", $text );
200
201 // Limit to 64 KiB
202 if ( strlen( $text ) > 65506 ) {
203 $text = substr( $text, 0, 65506 );
204 }
205
206 if ( !str_ends_with( $text, "\n" ) ) {
207 $text .= "\n";
208 }
209
210 } elseif ( strlen( $text ) > 65507 ) {
211 $text = substr( $text, 0, 65507 );
212 }
213
214 socket_sendto(
215 $this->sink,
216 $text,
217 strlen( $text ),
218 0,
219 $this->host,
220 $this->port
221 );
222
223 } else {
224 fwrite( $this->sink, $text );
225 }
226 }
227
228 public function close(): void {
229 if ( $this->sink ) {
230 if ( $this->useUdp() ) {
231 socket_close( $this->sink );
232 } else {
233 fclose( $this->sink );
234 }
235 }
236 $this->sink = null;
237 }
238}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:68
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.