4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * http://www.gnu.org/copyleft/gpl.html
24 * Log handler that replicates the behavior of MediaWiki's wfErrorLog()
25 * logging service. Log output can be directed to a local file, a PHP stream,
26 * or a udp2log server.
28 * For udp2log output, the stream specification must have the form:
29 * "udp://HOST:PORT[/PREFIX]"
31 * - HOST: IPv4, IPv6 or hostname
33 * - PREFIX: optional (but recommended) prefix telling udp2log how to route
36 * When not targeting a udp2log stream this class will act as a drop-in
37 * replacement for Monolog's StreamHandler.
40 * @author Bryan Davis <bd808@wikimedia.org>
41 * @copyright © 2013 Bryan Davis and Wikimedia Foundation.
43 class MWLoggerMonologHandler
extends \Monolog\Handler\AbstractProcessingHandler
{
52 * Filter log events using legacy rules
53 * @var bool $useLegacyFilter
55 protected $useLegacyFilter;
85 * @param string $stream Stream URI
86 * @param bool $useLegacyFilter Filter log events using legacy rules
87 * @param int $level Minimum logging level that will trigger handler
88 * @param bool $bubble Can handled meesages bubble up the handler stack?
90 public function __construct(
92 $useLegacyFilter = false,
93 $level = \Monolog\Logger
::DEBUG
,
96 parent
::__construct( $level, $bubble );
98 $this->useLegacyFilter
= $useLegacyFilter;
101 public function isHandling( array $record ) {
102 $levelOk = parent
::isHandling( $record );
103 if ( $levelOk && $this->useLegacyFilter
) {
104 return MWLoggerLegacyLogger
::shouldEmit(
105 $record['channel'], $record['message'], $record
112 * Open the log sink described by our stream URI.
114 protected function openSink() {
116 throw new LogicException(
117 'Missing stream uri, the stream can not be opened.' );
120 set_error_handler( array( $this, 'errorTrap' ) );
122 if ( substr( $this->uri
, 0, 4 ) == 'udp:' ) {
123 $parsed = parse_url( $this->uri
);
124 if ( !isset( $parsed['host'] ) ) {
125 throw new UnexpectedValueException( sprintf(
126 'Udp transport "%s" must specify a host', $this->uri
129 if ( !isset( $parsed['port'] ) ) {
130 throw new UnexpectedValueException( sprintf(
131 'Udp transport "%s" must specify a port', $this->uri
135 $this->host
= $parsed['host'];
136 $this->port
= $parsed['port'];
139 if ( isset( $parsed['path'] ) ) {
140 $this->prefix
= ltrim( $parsed['path'], '/' );
143 if ( filter_var( $this->host
, FILTER_VALIDATE_IP
, FILTER_FLAG_IPV6
) ) {
150 $this->sink
= socket_create( $domain, SOCK_DGRAM
, SOL_UDP
);
153 $this->sink
= fopen( $this->uri
, 'a' );
155 restore_error_handler();
157 if ( !is_resource( $this->sink
) ) {
159 throw new UnexpectedValueException( sprintf(
160 'The stream or file "%s" could not be opened: %s',
161 $this->uri
, $this->error
168 * Custom error handler.
169 * @param int $code Error number
170 * @param string $msg Error message
172 protected function errorTrap( $code, $msg ) {
178 * Should we use UDP to send messages to the sink?
181 protected function useUdp() {
182 return $this->host
!== null;
186 protected function write( array $record ) {
187 if ( $this->sink
=== null ) {
191 $text = (string)$record['formatted'];
192 if ( $this->useUdp() ) {
194 // Clean it up for the multiplexer
195 if ( $this->prefix
!== '' ) {
196 $text = preg_replace( '/^/m', "{$this->prefix} ", $text );
199 if ( strlen( $text ) > 65506 ) {
200 $text = substr( $text, 0, 65506 );
203 if ( substr( $text, -1 ) != "\n" ) {
207 } elseif ( strlen( $text ) > 65507 ) {
208 $text = substr( $text, 0, 65507 );
212 $this->sink
, $text, strlen( $text ), 0, $this->host
, $this->port
);
215 fwrite( $this->sink
, $text );
220 public function close() {
221 if ( is_resource( $this->sink
) ) {
222 if ( $this->useUdp() ) {
223 socket_close( $this->sink
);
226 fclose( $this->sink
);