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