Файловый менеджер - Редактировать - /var/www/html/logger.zip
Ðазад
PK ! 5s<� � LegacySpi.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger; use Psr\Log\LoggerInterface; /** * The default service provider for MediaWiki\Logger\LoggerFactory, which creates * LegacyLogger objects. * * Usage: * @code * $wgMWLoggerDefaultSpi = [ * 'class' => \MediaWiki\Logger\LegacySpi::class, * ]; * @endcode * * @since 1.25 * @ingroup Debug * @copyright © 2014 Wikimedia Foundation and contributors */ class LegacySpi implements Spi { /** * @var array */ protected $singletons = []; /** * Get a logger instance. * * @param string $channel Logging channel * @return \Psr\Log\LoggerInterface Logger instance */ public function getLogger( $channel ) { if ( !isset( $this->singletons[$channel] ) ) { $this->singletons[$channel] = new LegacyLogger( $channel ); } return $this->singletons[$channel]; } /** * @internal For use by MediaWikiIntegrationTestCase * @param string $channel * @param LoggerInterface|null $logger * @return LoggerInterface|null */ public function setLoggerForTest( $channel, ?LoggerInterface $logger = null ) { $ret = $this->singletons[$channel] ?? null; $this->singletons[$channel] = $logger; return $ret; } } PK ! ?{> Spi.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger; /** * @defgroup Debug Debug logging * * To primary APIs for this feature, and the classes where their documentation * starts, are: * * - MediaWiki\Logger\LoggerFactory, this creates all logger objects at * run time. * * - MediaWiki\Logger\Spi, to develop or configure the service classes * that internally create logger objects. For example, MediaWiki\Logger\LegacyLogger * is the default Spi that backs features like $wgDebugLogFile. * MediaWiki\Logger\MonologSpi is an Spi you can opt-in to via $wgMWLoggerDefaultSpi * to enable structured logging with destinations like Syslog and Logstash, * and "processor" and "formatter" features to augment messages with metadata, * as powered by the Monolog library. * * - MWDebug, the logic behind $wgDebugToolbar. * * @see [Logger documentation](@ref debuglogger) in docs/Logger.md */ /** * Service provider interface to create \Psr\Log\LoggerInterface objects. * * MediaWiki can be configured to use a class implementing this interface * via the $wgMWLoggerDefaultSpi configuration variable. * * This configuration is consumed by MediaWiki\Logger\LoggerFactory, which is * where we create logger objects. * * While not recommended in production code, you can construct and install * an Spi class at runtime via MediaWiki\Logger\LoggerFactory::registerProvider * (e.g. to power debug features in PHPUnit bootstrapping, or Maintenance * scripts). * * @stable to implement * @since 1.25 * @ingroup Debug * @copyright © 2014 Wikimedia Foundation and contributors */ interface Spi { /** * Get a logger instance. * * @param string $channel Logging channel * @return \Psr\Log\LoggerInterface Logger instance */ public function getLogger( $channel ); } PK ! B-8�$ �$ MonologSpi.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger; use DateTimeZone; use MediaWiki\Logger\Monolog\BufferHandler; use Monolog\Formatter\FormatterInterface; use Monolog\Handler\FormattableHandlerInterface; use Monolog\Handler\HandlerInterface; use Monolog\Handler\PsrHandler; use Monolog\Handler\StreamHandler; use Monolog\Logger; use Psr\Log\LoggerInterface; use Wikimedia\ObjectFactory\ObjectFactory; /** * LoggerFactory service provider that creates loggers implemented by * Monolog. * * Configured using an array of configuration data with the keys 'loggers', * 'processors', 'handlers' and 'formatters'. * * The ['loggers']['\@default'] configuration will be used to create loggers * for any channel that isn't explicitly named in the 'loggers' configuration * section. * * Configuration will most typically be provided in the $wgMWLoggerDefaultSpi * global configuration variable used by LoggerFactory to construct its * default SPI provider: * @code * $wgMWLoggerDefaultSpi = [ * 'class' => \MediaWiki\Logger\MonologSpi::class, * 'args' => [ [ * 'loggers' => [ * '@default' => [ * 'processors' => [ 'wiki', 'psr', 'pid', 'uid', 'web' ], * 'handlers' => [ 'stream' ], * ], * 'runJobs' => [ * 'processors' => [ 'wiki', 'psr', 'pid' ], * 'handlers' => [ 'stream' ], * ] * ], * 'processors' => [ * 'wiki' => [ * 'class' => \MediaWiki\Logger\Monolog\WikiProcessor::class, * ], * 'psr' => [ * 'class' => \Monolog\Processor\PsrLogMessageProcessor::class, * ], * 'pid' => [ * 'class' => \Monolog\Processor\ProcessIdProcessor::class, * ], * 'uid' => [ * 'class' => \Monolog\Processor\UidProcessor::class, * ], * 'web' => [ * 'class' => \Monolog\Processor\WebProcessor::class, * ], * ], * 'handlers' => [ * 'stream' => [ * 'class' => \Monolog\Handler\StreamHandler::class, * 'args' => [ 'path/to/your.log' ], * 'formatter' => 'line', * ], * 'redis' => [ * 'class' => \Monolog\Handler\RedisHandler::class, * 'args' => [ function() { * $redis = new Redis(); * $redis->connect( '127.0.0.1', 6379 ); * return $redis; * }, * 'logstash' * ], * 'formatter' => 'logstash', * 'buffer' => true, * ], * 'udp2log' => [ * 'class' => \MediaWiki\Logger\Monolog\LegacyHandler::class, * 'args' => [ * 'udp://127.0.0.1:8420/mediawiki * ], * 'formatter' => 'line', * ], * ], * 'formatters' => [ * 'line' => [ * 'class' => \Monolog\Formatter\LineFormatter::class, * ], * 'logstash' => [ * 'class' => \Monolog\Formatter\LogstashFormatter::class, * 'args' => [ 'mediawiki', php_uname( 'n' ), null, '', 1 ], * ], * ], * ] ], * ]; * @endcode * * @see https://github.com/Seldaek/monolog * @since 1.25 * @ingroup Debug * @copyright © 2014 Wikimedia Foundation and contributors */ class MonologSpi implements Spi { /** * @var array{loggers:LoggerInterface[],handlers:HandlerInterface[],formatters:FormatterInterface[],processors:callable[]} */ protected $singletons; /** * Configuration for creating new loggers. * @var array<string,array<string,array>> */ protected array $config = []; /** * @param array $config Configuration data. */ public function __construct( array $config ) { $this->mergeConfig( $config ); } /** * Merge additional configuration data into the configuration. * * @since 1.26 * @param array $config Configuration data. */ public function mergeConfig( array $config ) { foreach ( $config as $key => $value ) { if ( isset( $this->config[$key] ) ) { $this->config[$key] = array_merge( $this->config[$key], $value ); } else { $this->config[$key] = $value; } } if ( !isset( $this->config['loggers']['@default'] ) ) { $this->config['loggers']['@default'] = [ 'handlers' => [ '@default' ], ]; $this->config['handlers']['@default'] ??= [ 'class' => StreamHandler::class, 'args' => [ 'php://stderr', Logger::ERROR ], ]; } $this->reset(); } /** * Reset internal caches. * * This is public for use in unit tests. Under normal operation there should * be no need to flush the caches. */ public function reset() { $this->singletons = [ 'loggers' => [], 'handlers' => [], 'formatters' => [], 'processors' => [], ]; } /** * Get a logger instance. * * Creates and caches a logger instance based on configuration found in the * $wgMWLoggerMonologSpiConfig global. Subsequent request for the same channel * name will return the cached instance. * * @param string $channel Logging channel * @return LoggerInterface */ public function getLogger( $channel ) { if ( !isset( $this->singletons['loggers'][$channel] ) ) { // Fallback to using the '@default' configuration if an explicit // configuration for the requested channel isn't found. $spec = $this->config['loggers'][$channel] ?? $this->config['loggers']['@default']; $monolog = $this->createLogger( $channel, $spec ); $this->singletons['loggers'][$channel] = $monolog; } return $this->singletons['loggers'][$channel]; } /** * Create a logger. * @param string $channel Logger channel * @param array $spec Configuration * @return LoggerInterface */ protected function createLogger( $channel, $spec ): LoggerInterface { global $wgShowDebug, $wgDebugToolbar; $handlers = []; if ( isset( $spec['handlers'] ) && $spec['handlers'] ) { foreach ( $spec['handlers'] as $handler ) { $handlers[] = $this->getHandler( $handler ); } } $processors = []; if ( isset( $spec['processors'] ) ) { foreach ( $spec['processors'] as $processor ) { $processors[] = $this->getProcessor( $processor ); } } // Use UTC for logs instead of Monolog's default, which asks the // PHP runtime, which MediaWiki sets to $wgLocaltimezone (T99581) $obj = new Logger( $channel, $handlers, $processors, new DateTimeZone( 'UTC' ) ); if ( $wgShowDebug || $wgDebugToolbar ) { $legacyLogger = new LegacyLogger( $channel ); $legacyPsrHandler = new PsrHandler( $legacyLogger ); $obj->pushHandler( $legacyPsrHandler ); } if ( isset( $spec['calls'] ) ) { foreach ( $spec['calls'] as $method => $margs ) { $obj->$method( ...$margs ); } } return $obj; } /** * Create or return cached processor. * @param string $name Processor name * @return callable */ public function getProcessor( $name ) { if ( !isset( $this->singletons['processors'][$name] ) ) { $spec = $this->config['processors'][$name]; /** @var callable $processor */ $processor = ObjectFactory::getObjectFromSpec( $spec ); $this->singletons['processors'][$name] = $processor; } return $this->singletons['processors'][$name]; } /** * Create or return cached handler. * @param string $name Processor name * @return HandlerInterface */ public function getHandler( $name ) { if ( !isset( $this->singletons['handlers'][$name] ) ) { $spec = $this->config['handlers'][$name]; /** @var HandlerInterface $handler */ $handler = ObjectFactory::getObjectFromSpec( $spec ); if ( isset( $spec['formatter'] ) && $handler instanceof FormattableHandlerInterface ) { $handler->setFormatter( $this->getFormatter( $spec['formatter'] ) ); } if ( isset( $spec['buffer'] ) && $spec['buffer'] ) { $handler = new BufferHandler( $handler ); } $this->singletons['handlers'][$name] = $handler; } return $this->singletons['handlers'][$name]; } /** * Create or return cached formatter. * @param string $name Formatter name * @return FormatterInterface */ public function getFormatter( $name ) { if ( !isset( $this->singletons['formatters'][$name] ) ) { $spec = $this->config['formatters'][$name]; /** @var FormatterInterface $formatter */ $formatter = ObjectFactory::getObjectFromSpec( $spec ); $this->singletons['formatters'][$name] = $formatter; } return $this->singletons['formatters'][$name]; } } PK ! ����h h LogCapturingSpi.phpnu �Iw�� <?php namespace MediaWiki\Logger; use Psr\Log\AbstractLogger; use Psr\Log\LoggerInterface; /** * Wrap another Spi and keep a copy of all log messages. * * This is developed for use by PHPUnit bootstrapping, to collect logs * generated during a given unit test, and print them after a failing test. * * @internal For use in MediaWiki core only * @ingroup Debug */ class LogCapturingSpi implements Spi { /** @var LoggerInterface[] */ private $singletons; /** @var Spi */ private $inner; /** @var array */ private $logs = []; public function __construct( Spi $inner ) { $this->inner = $inner; } /** * @return array */ public function getLogs() { return $this->logs; } /** * @param string $channel * @return LoggerInterface */ public function getLogger( $channel ) { if ( !isset( $this->singletons[$channel] ) ) { $this->singletons[$channel] = $this->createLogger( $channel ); } return $this->singletons[$channel]; } /** * @param array $log */ public function capture( $log ) { $this->logs[] = $log; } /** * @param string $channel * @return LoggerInterface */ private function createLogger( $channel ) { $inner = $this->inner->getLogger( $channel ); return new class( $channel, $inner, $this ) extends AbstractLogger { /** @var string */ private $channel; /** @var LoggerInterface */ private $logger; /** @var LogCapturingSpi */ private $parent; public function __construct( $channel, LoggerInterface $logger, LogCapturingSpi $parent ) { $this->channel = $channel; $this->logger = $logger; $this->parent = $parent; } public function log( $level, $message, array $context = [] ) { $this->parent->capture( [ 'channel' => $this->channel, 'level' => $level, 'message' => $message, 'context' => $context ] ); $this->logger->log( $level, $message, $context ); } }; } /** * @internal For use by MediaWikiIntegrationTestCase * @return Spi */ public function getInnerSpi(): Spi { return $this->inner; } /** * @internal For use by MediaWikiIntegrationTestCase * @param string $channel * @param LoggerInterface|null $logger * @return LoggerInterface|null */ public function setLoggerForTest( $channel, ?LoggerInterface $logger = null ) { $ret = $this->singletons[$channel] ?? null; $this->singletons[$channel] = $logger; return $ret; } } PK ! ґH� � ConsoleLogger.phpnu �Iw�� <?php namespace MediaWiki\Logger; use Psr\Log\AbstractLogger; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Wikimedia\Assert\Assert; /** * Write logs to command-line output (STDERR). * * The output is supposed to be human-readable, and should be changed as necessary * to better achieve that goal. * * This is developed for use in maintenance/eval.php. * * @internal For use in MediaWiki core only * @since 1.30 * @ingroup Debug */ class ConsoleLogger extends AbstractLogger { private const LEVELS = [ LogLevel::DEBUG => 0, LogLevel::INFO => 1, LogLevel::NOTICE => 2, LogLevel::WARNING => 3, LogLevel::ERROR => 4, LogLevel::CRITICAL => 5, LogLevel::ALERT => 6, LogLevel::EMERGENCY => 7, ]; private string $channel; private ?string $minLevel; private ?LoggerInterface $forwardTo; /** * @param string $channel log channel name. * @param string|null $minLevel Minimum PSR-3 level below which messages are ignored. * @param LoggerInterface|null $forwardTo Other logger to forward to. */ public function __construct( string $channel, ?string $minLevel = null, ?LoggerInterface $forwardTo = null ) { Assert::parameter( $minLevel === null || isset( self::LEVELS[$minLevel] ), '$minLevel', 'must be a valid, lowercase PSR-3 log level' ); $this->channel = $channel; $this->minLevel = $minLevel; $this->forwardTo = $forwardTo; } /** * @inheritDoc */ public function log( $level, $message, array $context = [] ) { if ( !$this->minLevel || self::LEVELS[$level] >= self::LEVELS[$this->minLevel] ) { fwrite( STDERR, "[$level] " . LegacyLogger::format( $this->channel, $message, $context ) ); } if ( $this->forwardTo ) { $this->forwardTo->log( $level, $message, $context ); } } } PK ! r�N�z z LoggerFactory.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger; use Wikimedia\ObjectFactory\ObjectFactory; /** * Create PSR-3 logger objects. * * Creation of \Psr\Log\LoggerInterface instances is managed via the * LoggerFactory::getInstance() static method which in turn delegates to the * currently registered service provider. * * A service provider is any class implementing the Spi interface. * There are two possible methods of registering a service provider. The * LoggerFactory::registerProvider() static method can be called at any time * to change the service provider. If LoggerFactory::getInstance() is called * before any service provider has been registered, it will attempt to use the * $wgMWLoggerDefaultSpi global to bootstrap Spi registration. * $wgMWLoggerDefaultSpi is expected to be an array usable by * ObjectFactory::getObjectFromSpec() to create a class. * * @since 1.25 * @ingroup Debug * @copyright © 2014 Wikimedia Foundation and contributors */ class LoggerFactory { /** * Service provider. * @var \MediaWiki\Logger\Spi */ private static $spi; /** * Register a service provider to create new \Psr\Log\LoggerInterface * instances. * * @param \MediaWiki\Logger\Spi $provider Provider to register */ public static function registerProvider( Spi $provider ) { self::$spi = $provider; } /** * Get the registered service provider. * * If called before any service provider has been registered, it will * attempt to use the $wgMWLoggerDefaultSpi global to bootstrap * Spi registration. $wgMWLoggerDefaultSpi is expected to be an * array usable by ObjectFactory::getObjectFromSpec() to create a class. * * @return \MediaWiki\Logger\Spi * @see registerProvider() * @see ObjectFactory::getObjectFromSpec() */ public static function getProvider() { if ( self::$spi === null ) { global $wgMWLoggerDefaultSpi; $provider = ObjectFactory::getObjectFromSpec( $wgMWLoggerDefaultSpi ); self::registerProvider( $provider ); } return self::$spi; } /** * Get a named logger instance from the currently configured logger factory. * * @param string $channel Logger channel (name) * @return \Psr\Log\LoggerInterface */ public static function getInstance( $channel ) { return self::getProvider()->getLogger( $channel ); } /** * Construction of utility class is not allowed. */ private function __construct() { // no-op } } PK ! E�'� � ConsoleSpi.phpnu �Iw�� <?php namespace MediaWiki\Logger; use Psr\Log\NullLogger; /** * ConsoleLogger service provider for MediaWiki\Logger\LoggerFactory. * * This is developed for use in maintenance/eval.php. * * @internal For use in MediaWiki core only * @since 1.30 * @ingroup Debug */ class ConsoleSpi implements Spi { /** @var string[]|null Channel allow-list: channel name => minimum level */ private ?array $channels; private ?Spi $forwardTo; /** * @param array $config * - channels: (string[]) List of channels to log: channel name => minimum level. * Omit to log everything. * - forwardTo: (Spi) Forward all log messages to this SPI (regardless of whether * ConsoleSpi logs them). */ public function __construct( array $config = [] ) { $this->channels = $config['channels'] ?? null; $this->forwardTo = $config['forwardTo'] ?? null; } /** @inheritDoc */ public function getLogger( $channel ) { if ( !$this->channels || isset( $this->channels[$channel] ) ) { return new ConsoleLogger( $channel, $this->channels[$channel] ?? null, $this->forwardTo ? $this->forwardTo->getLogger( $channel ) : null ); } else { return $this->forwardTo ? $this->forwardTo->getLogger( $channel ) : new NullLogger(); } } } PK ! ����� � monolog/MwlogHandler.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger\Monolog; use Monolog\Handler\SyslogUdpHandler; use Monolog\Logger; /** * Write logs to syslog with the channel appended to the application name. * * This use case for this handler is to emulate Wikimedia Foundation's * udp2log system by leveraging syslog (and e.g. Rsyslog/Kafka) and * allow an unstructured string to pass through mostly as-is, with the * exception of the channel name, which is encoded in transit as part * of the syslog "application name". It is intended that the syslog * consumer "wildcard" subscribes to all messages with the app prefix, * and then * strips it off at some point before writing the messages * to a log file named after the channel. * * Transition plan (2016): * - https://phabricator.wikimedia.org/T205856#4957430 * - https://phabricator.wikimedia.org/T126989 * * @unstable * @since 1.32 * @ingroup Debug * @copyright © 2019 Wikimedia Foundation and contributors */ class MwlogHandler extends SyslogUdpHandler { /** * @var string */ private $appprefix; /** * @var string */ private $hostname; /** * @param string $appprefix Application prefix to use, channel will be appended. * @param string $host Syslog host * @param int $port Syslog port * @param int $facility Syslog message facility * @param int $level The minimum logging level at which this handler * will be triggered * @param bool $bubble Whether the messages that are handled can bubble up * the stack or not */ public function __construct( $appprefix, $host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true ) { parent::__construct( $host, $port, $facility, $level, $bubble ); $this->appprefix = $appprefix; $this->hostname = php_uname( 'n' ); } protected function syslogHeader( $severity, $app ) { $pri = $severity + $this->facility; // Goofy date format courtesy of RFC 3164 :( // RFC 3164 actually specifies that the day of month should be space // padded rather than unpadded but this seems to work with rsyslog and // Logstash. $timestamp = date( 'M j H:i:s' ); return "<{$pri}>{$timestamp} {$this->hostname} {$app}: "; } private function splitMessageIntoLines( $message ): array { if ( is_array( $message ) ) { $message = implode( "\n", $message ); } return preg_split( '/$\R?^/m', (string)$message, -1, PREG_SPLIT_NO_EMPTY ); } protected function write( array $record ): void { $lines = $this->splitMessageIntoLines( $record['formatted'] ); $header = $this->syslogHeader( $this->logLevels[$record['level']], $this->appprefix . $record['channel'] ); foreach ( $lines as $line ) { $this->socket->write( $line, $header ); } } } PK ! w�~S� � monolog/LegacyHandler.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger\Monolog; use LogicException; use MediaWiki\Logger\LegacyLogger; use Monolog\Handler\AbstractProcessingHandler; use Monolog\Logger; use Socket; use UnexpectedValueException; /** * Monolog imitation of MediaWiki\Logger\LegacyLogger * * This replicates the behavior of LegacyLogger, which in turn replicates * MediaWiki's former wfErrorLog() function. * * The main use case of the LegacyHandler is to enable adoption of Monolog * features (such as alternate formatters, extra processors, and enabling multiple * destinations/handlers at the same time), where one of the handlers (this one) * essentiallly does what the LegacySpi would do if you hadn't enabled * MonologSpi. In particular: writing to a file like $wgDebugLogFile, * and sending messages to a PHP stream or udp2log server. * * For udp2log output, the stream specification must have the form: * "udp://HOST:PORT[/PREFIX]" * where: * * - HOST: IPv4, IPv6 or hostname * - PORT: server port * - PREFIX: optional (but recommended) prefix telling udp2log how to route * the log event. The special prefix "{channel}" will use the log event's * channel as the prefix value. * * When not targeting a udp2log server, this class will act as a drop-in * replacement for \Monolog\Handler\StreamHandler. * * @since 1.25 * @ingroup Debug * @copyright © 2013 Wikimedia Foundation and contributors */ class LegacyHandler extends AbstractProcessingHandler { /** * Log sink descriptor * @var string */ protected $uri; /** * Filter log events using legacy rules * @var bool */ protected $useLegacyFilter; /** * Log sink * @var Socket|resource|null */ protected $sink; /** * @var string|null */ protected $error; /** * @var string */ protected $host; /** * @var int */ protected $port; /** * @var string */ protected $prefix; /** * @param string $stream Stream URI * @param bool $useLegacyFilter Filter log events using legacy rules * @param int $level Minimum logging level that will trigger handler * @param bool $bubble Can handled messages bubble up the handler stack? */ public function __construct( $stream, $useLegacyFilter = false, $level = Logger::DEBUG, $bubble = true ) { parent::__construct( $level, $bubble ); $this->uri = $stream; $this->useLegacyFilter = $useLegacyFilter; } /** * Open the log sink described by our stream URI. */ protected function openSink() { if ( !$this->uri ) { throw new LogicException( 'Missing stream uri, the stream can not be opened.' ); } $this->error = null; set_error_handler( [ $this, 'errorTrap' ] ); if ( str_starts_with( $this->uri, 'udp:' ) ) { $parsed = parse_url( $this->uri ); if ( !isset( $parsed['host'] ) ) { throw new UnexpectedValueException( sprintf( 'Udp transport "%s" must specify a host', $this->uri ) ); } if ( !isset( $parsed['port'] ) ) { throw new UnexpectedValueException( sprintf( 'Udp transport "%s" must specify a port', $this->uri ) ); } $this->host = $parsed['host']; $this->port = $parsed['port']; $this->prefix = ''; if ( isset( $parsed['path'] ) ) { $this->prefix = ltrim( $parsed['path'], '/' ); } if ( filter_var( $this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) { $domain = AF_INET6; } else { $domain = AF_INET; } $this->sink = socket_create( $domain, SOCK_DGRAM, SOL_UDP ); } else { $this->sink = fopen( $this->uri, 'a' ); } restore_error_handler(); if ( !$this->sink ) { $this->sink = null; throw new UnexpectedValueException( sprintf( 'The stream or file "%s" could not be opened: %s', // @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal Set by error handler $this->uri, $this->error ) ); } } /** * Custom error handler. * @param int $code Error number * @param string $msg Error message */ protected function errorTrap( $code, $msg ) { $this->error = $msg; } /** * Should we use UDP to send messages to the sink? * @return bool */ protected function useUdp() { return $this->host !== null; } protected function write( array $record ): void { if ( $this->useLegacyFilter && !LegacyLogger::shouldEmit( $record['channel'], $record['message'], $record['level'], $record ) ) { // Do not write record if we are enforcing legacy rules and they // do not pass this message. This used to be done in isHandling(), // but Monolog 1.12.0 made a breaking change that removed access // to the needed channel and context information. return; } if ( $this->sink === null ) { $this->openSink(); } $text = (string)$record['formatted']; if ( $this->useUdp() ) { // Clean it up for the multiplexer if ( $this->prefix !== '' ) { $leader = ( $this->prefix === '{channel}' ) ? $record['channel'] : $this->prefix; $text = preg_replace( '/^/m', "{$leader} ", $text ); // Limit to 64 KiB if ( strlen( $text ) > 65506 ) { $text = substr( $text, 0, 65506 ); } if ( !str_ends_with( $text, "\n" ) ) { $text .= "\n"; } } elseif ( strlen( $text ) > 65507 ) { $text = substr( $text, 0, 65507 ); } socket_sendto( $this->sink, $text, strlen( $text ), 0, $this->host, $this->port ); } else { fwrite( $this->sink, $text ); } } public function close(): void { if ( $this->sink ) { if ( $this->useUdp() ) { socket_close( $this->sink ); } else { fclose( $this->sink ); } } $this->sink = null; } } PK ! ��M) ) monolog/LegacyFormatter.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger\Monolog; use MediaWiki\Logger\LegacyLogger; use Monolog\Formatter\NormalizerFormatter; /** * Log message formatter that mimics the legacy log message formatting of `wfDebug`, `wfDebugLog`, * `wfLogDBError` and the former `wfErrorLog` global functions by delegating the formatting to * \MediaWiki\Logger\LegacyLogger. * * @deprecated since 1.32 * @since 1.25 * @ingroup Debug * @copyright © 2013 Wikimedia Foundation and contributors */ class LegacyFormatter extends NormalizerFormatter { public function __construct() { parent::__construct( 'c' ); } public function format( array $record ): string { $normalized = parent::format( $record ); return LegacyLogger::format( $normalized['channel'], $normalized['message'], $normalized ); } } PK ! l�_�# # monolog/SyslogHandler.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger\Monolog; use DateTimeInterface; use Monolog\Handler\SyslogUdpHandler; use Monolog\Logger; /** * Write logs to a syslog server, using RFC 3164 formatted UDP packets. * * This builds on Monolog's SyslogUdpHandler, which creates only a partial * RFC 5424 header (PRI and VERSION), with rest intending to come * from a specifically configured LineFormatter. * * This makes use of SyslogUdpHandler it impossible with a formatter like * \Monolog\Formatter\LogstashFormatter. Additionally, the direct syslog * input for Logstash requires and accepts only RFC 3164 formatted packets. * * This is a complete syslog handler and should work with any formatter. The * formatted message will be prepended with a complete RFC 3164 message * header and a partial message body. The resulting packet looks like: * * <PRI>DATETIME HOSTNAME PROGRAM: MESSAGE * * This format works as input to rsyslog and can also be processed by the * default Logstash syslog input handler. * * @since 1.25 * @ingroup Debug * @copyright © 2015 Wikimedia Foundation and contributors */ class SyslogHandler extends SyslogUdpHandler { private string $appname; private string $hostname; /** * @param string $appname Application name to report to syslog * @param string $host Syslog host * @param int $port Syslog port * @param int $facility Syslog message facility * @param int $level The minimum logging level at which this handler * will be triggered * @param bool $bubble Whether the messages that are handled can bubble up * the stack or not */ public function __construct( $appname, $host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true ) { parent::__construct( $host, $port, $facility, $level, $bubble ); $this->appname = $appname; $this->hostname = php_uname( 'n' ); } protected function makeCommonSyslogHeader( int $severity, DateTimeInterface $datetime ): string { $pri = $severity + $this->facility; // Goofy date format courtesy of RFC 3164 :( // RFC 3164 actually specifies that the day of month should be space // padded rather than unpadded but this seems to work with rsyslog and // Logstash. $timestamp = date( 'M j H:i:s' ); return "<{$pri}>{$timestamp} {$this->hostname} {$this->appname}: "; } } PK ! ?�yD D monolog/CeeFormatter.phpnu �Iw�� <?php namespace MediaWiki\Logger\Monolog; /** * Prefixed version of LogstashFormatter that adds a "cee cookie" for Rsyslog. * * The prefix lets Ryslog differentiate between JSON and non-JSON messages. * * See also: https://www.rsyslog.com/doc/v8-stable/configuration/modules/mmjsonparse.html * * @since 1.33 * @ingroup Debug */ class CeeFormatter extends LogstashFormatter { /** * Format records with a cee cookie * @param array $record * @return string */ public function format( array $record ): string { return "@cee: " . parent::format( $record ); } } PK ! ���c c monolog/LineFormatter.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger\Monolog; use Error; use Monolog\Formatter\LineFormatter as MonologLineFormatter; use MWExceptionHandler; use Throwable; /** * Formats incoming records into a one-line string. * * An 'exeception' in the log record's context will be treated specially. * It will be output for an '%exception%' placeholder in the format and * excluded from '%context%' output if the '%exception%' placeholder is * present. * * Throwables that are logged with this formatter will optional have their * stack traces appended. If that is done, MWExceptionHandler::redactedTrace() * will be used to redact the trace information. * * @since 1.26 * @ingroup Debug * @copyright © 2015 Wikimedia Foundation and contributors */ class LineFormatter extends MonologLineFormatter { /** * @param string|null $format The format of the message * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries * @param bool $ignoreEmptyContextAndExtra * @param bool $includeStacktraces */ public function __construct( $format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false, $includeStacktraces = false ) { parent::__construct( $format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra ); $this->includeStacktraces( $includeStacktraces ); } /** * @inheritDoc */ public function format( array $record ): string { // Drop the 'private' flag from the context unset( $record['context']['private'] ); // Handle throwables specially: pretty format and remove from context // Will be output for a '%exception%' placeholder in format $prettyException = ''; if ( isset( $record['context']['exception'] ) && str_contains( $this->format, '%exception%' ) ) { $e = $record['context']['exception']; unset( $record['context']['exception'] ); if ( $e instanceof Throwable ) { $prettyException = $this->normalizeException( $e ); } elseif ( is_array( $e ) ) { $prettyException = $this->normalizeExceptionArray( $e ); } else { $prettyException = $this->stringify( $e ); } } $output = parent::format( $record ); if ( str_contains( $output, '%exception%' ) ) { $output = str_replace( '%exception%', $prettyException, $output ); } return $output; } /** * Convert a Throwable to a string. * * @param Throwable $e * @param int $depth * @return string */ protected function normalizeException( Throwable $e, int $depth = 0 ): string { // Can't use typehint. Must match Monolog\Formatter\LineFormatter::normalizeException($e) return $this->normalizeExceptionArray( $this->exceptionAsArray( $e ) ); } /** * Convert a throwable to an array of structured data. * * @param Throwable $e * @return array */ protected function exceptionAsArray( Throwable $e ) { $out = [ 'class' => get_class( $e ), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => MWExceptionHandler::redactTrace( $e->getTrace() ), ]; $prev = $e->getPrevious(); if ( $prev ) { $out['previous'] = $this->exceptionAsArray( $prev ); } return $out; } /** * Convert an array of Throwable data to a string. * * @param array $e * @return string */ protected function normalizeExceptionArray( array $e ) { $defaults = [ 'class' => 'Unknown', 'file' => 'unknown', 'line' => null, 'message' => 'unknown', 'trace' => [], ]; $e = array_merge( $defaults, $e ); // @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal class is always set $which = is_a( $e['class'], Error::class, true ) ? 'Error' : 'Exception'; $str = "\n[$which {$e['class']}] (" . "{$e['file']}:{$e['line']}) {$e['message']}"; if ( $this->includeStacktraces && $e['trace'] ) { $str .= "\n" . // @phan-suppress-next-line PhanTypeMismatchArgumentNullable trace is always set MWExceptionHandler::prettyPrintTrace( $e['trace'], ' ' ); } if ( isset( $e['previous'] ) ) { $prev = $e['previous']; while ( $prev ) { $prev = array_merge( $defaults, $prev ); $which = is_a( $prev['class'], Error::class, true ) ? 'Error' : 'Exception'; $str .= "\nCaused by: [$which {$prev['class']}] (" . "{$prev['file']}:{$prev['line']}) {$prev['message']}"; if ( $this->includeStacktraces && $prev['trace'] ) { $str .= "\n" . MWExceptionHandler::prettyPrintTrace( $prev['trace'], ' ' ); } $prev = $prev['previous'] ?? null; } } return $str; } } PK ! �Q�� � monolog/BufferHandler.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger\Monolog; use MediaWiki\Deferred\DeferredUpdates; use Monolog\Handler\BufferHandler as BaseBufferHandler; /** * Helper class for the index.php entry point. * * Updates \Monolog\Handler\BufferHandler to use DeferredUpdates rather * than register_shutdown_function. On supported platforms this will * use register_postsend_function or fastcgi_finish_request() to delay * until after the request has shutdown and we are no longer delaying * the web request. * * TODO: shutdown is later than postsend. Is this class still useful? * * @since 1.26 * @ingroup Debug */ class BufferHandler extends BaseBufferHandler { /** * @inheritDoc */ public function handle( array $record ): bool { if ( !$this->initialized ) { DeferredUpdates::addCallableUpdate( [ $this, 'close' ] ); $this->initialized = true; } return parent::handle( $record ); } } PK ! \!cAU U monolog/WikiProcessor.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger\Monolog; use MediaWiki\Http\Telemetry; use MediaWiki\WikiMap\WikiMap; /** * Annotate log records with request-global metadata, such as the hostname, * wiki / request ID, and MediaWiki version. * * @since 1.25 * @ingroup Debug * @copyright © 2013 Wikimedia Foundation and contributors */ class WikiProcessor { /** * @param array $record * @return array */ public function __invoke( array $record ) { $telemetry = Telemetry::getInstance(); $record['extra']['host'] = wfHostname(); $record['extra']['wiki'] = WikiMap::getCurrentWikiId(); $record['extra']['mwversion'] = MW_VERSION; $record['extra']['reqId'] = $telemetry->getRequestId(); if ( wfIsCLI() && isset( $_SERVER['argv'] ) ) { $record['extra']['cli_argv'] = implode( ' ', $_SERVER['argv'] ); } return $record; } } PK ! �S�Z� � monolog/LogstashFormatter.phpnu �Iw�� <?php namespace MediaWiki\Logger\Monolog; /** * Modified version of Monolog\Formatter\LogstashFormatter * * - Squash the base message array, the context and extra subarrays into one. * This can result in unfortunately named context fields overwriting other data (T145133). * - Improve exception JSON-ification, which is done poorly by the standard class. * * @since 1.29 * @ingroup Debug */ class LogstashFormatter extends \Monolog\Formatter\LogstashFormatter { public const V0 = 0; public const V1 = 1; /** @var array Keys which should not be used in log context */ protected $reservedKeys = [ // from LogstashFormatter 'message', 'channel', 'level', 'type', // from WebProcessor 'url', 'ip', 'http_method', 'server', 'referrer', // from WikiProcessor 'host', 'wiki', 'reqId', 'mwversion', // from config magic 'normalized_message', ]; /** * @var int Logstash format version to use */ protected $version; /** * TODO: See T247675 for removing this override. * * @param string $applicationName The application that sends the data, used as the "type" * field of logstash * @param string|null $systemName The system/machine name, used as the "source" field of * logstash, defaults to the hostname of the machine * @param string $extraKey The key for extra keys inside logstash "fields", defaults to '' * @param string $contextKey The key for context keys inside logstash "fields", defaults * @param int $version The logstash format version to use, defaults to V0 * to '' */ public function __construct( string $applicationName, ?string $systemName = null, string $extraKey = '', string $contextKey = 'ctxt_', $version = self::V0 ) { $this->version = $version; parent::__construct( $applicationName, $systemName, $extraKey, $contextKey ); } public function format( array $record ): string { $record = \Monolog\Formatter\NormalizerFormatter::format( $record ); if ( $this->version === self::V1 ) { $message = $this->formatV1( $record ); } elseif ( $this->version === self::V0 ) { $message = $this->formatV0( $record ); } else { $message = __METHOD__ . ' unknown version ' . $this->version; } return $this->toJson( $message ) . "\n"; } /** * Prevent key conflicts * @param array $record * @return array */ protected function formatV0( array $record ) { if ( $this->contextKey !== '' ) { return $this->formatMonologV0( $record ); } $context = !empty( $record['context'] ) ? $record['context'] : []; $record['context'] = []; $formatted = $this->formatMonologV0( $record ); $formatted['@fields'] = $this->fixKeyConflicts( $formatted['@fields'], $context ); return $formatted; } /** * Borrowed from monolog/monolog 1.25.3 * https://github.com/Seldaek/monolog/blob/1.x/src/Monolog/Formatter/LogstashFormatter.php#L87-L128 * * @param array $record * @return array */ protected function formatMonologV0( array $record ) { if ( empty( $record['datetime'] ) ) { $record['datetime'] = gmdate( 'c' ); } $message = [ '@timestamp' => $record['datetime'], '@source' => $this->systemName, '@fields' => [], ]; if ( isset( $record['message'] ) ) { $message['@message'] = $record['message']; } if ( isset( $record['channel'] ) ) { $message['@tags'] = [ $record['channel'] ]; $message['@fields']['channel'] = $record['channel']; } if ( isset( $record['level'] ) ) { $message['@fields']['level'] = $record['level']; } if ( $this->applicationName ) { $message['@type'] = $this->applicationName; } if ( isset( $record['extra']['server'] ) ) { $message['@source_host'] = $record['extra']['server']; } if ( isset( $record['extra']['url'] ) ) { $message['@source_path'] = $record['extra']['url']; } if ( !empty( $record['extra'] ) ) { foreach ( $record['extra'] as $key => $val ) { $message['@fields'][$this->extraKey . $key] = $val; } } if ( !empty( $record['context'] ) ) { foreach ( $record['context'] as $key => $val ) { $message['@fields'][$this->contextKey . $key] = $val; } } return $message; } /** * Prevent key conflicts * @param array $record * @return array */ protected function formatV1( array $record ) { if ( $this->contextKey ) { return $this->formatMonologV1( $record ); } $context = !empty( $record['context'] ) ? $record['context'] : []; $record['context'] = []; $formatted = $this->formatMonologV1( $record ); return $this->fixKeyConflicts( $formatted, $context ); } /** * Borrowed mostly from monolog/monolog 1.25.3 * https://github.com/Seldaek/monolog/blob/1.25.3/src/Monolog/Formatter/LogstashFormatter.php#L130-165 * * @param array $record * @return array */ protected function formatMonologV1( array $record ) { if ( empty( $record['datetime'] ) ) { $record['datetime'] = gmdate( 'c' ); } $message = [ '@timestamp' => $record['datetime'], '@version' => 1, 'host' => $this->systemName, ]; if ( isset( $record['message'] ) ) { $message['message'] = $record['message']; } if ( isset( $record['channel'] ) ) { $message['type'] = $record['channel']; $message['channel'] = $record['channel']; } if ( isset( $record['level_name'] ) ) { $message['level'] = $record['level_name']; } // level -> monolog_level is new in 2.0 // https://github.com/Seldaek/monolog/blob/2.0.2/src/Monolog/Formatter/LogstashFormatter.php#L86-L88 if ( isset( $record['level'] ) ) { $message['monolog_level'] = $record['level']; } if ( $this->applicationName ) { $message['type'] = $this->applicationName; } if ( !empty( $record['extra'] ) ) { foreach ( $record['extra'] as $key => $val ) { $message[$this->extraKey . $key] = $val; } } if ( !empty( $record['context'] ) ) { foreach ( $record['context'] as $key => $val ) { $message[$this->contextKey . $key] = $val; } } return $message; } /** * Rename any context field that would otherwise overwrite a message key. * * @param array $fields Fields to be sent to logstash * @param array $context Copy of the original $record['context'] * @return array Updated version of $fields */ protected function fixKeyConflicts( array $fields, array $context ) { foreach ( $context as $key => $val ) { if ( in_array( $key, $this->reservedKeys, true ) && isset( $fields[$key] ) && $fields[$key] !== $val ) { $fields['logstash_formatter_key_conflict'][] = $key; $key = 'c_' . $key; } $fields[$key] = $val; } return $fields; } /** * Use a more user-friendly trace format than Monolog\Formatter\NormalizerFormatter. * * @param \Throwable $e * @param int $depth * @return array */ protected function normalizeException( \Throwable $e, int $depth = 0 ) { $data = [ 'class' => get_class( $e ), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine(), 'trace' => \MWExceptionHandler::getRedactedTraceAsString( $e ), ]; $previous = $e->getPrevious(); if ( $previous ) { $data['previous'] = $this->normalizeException( $previous ); } return $data; } } PK ! �fw�8= 8= LegacyLogger.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger; use DateTimeZone; use Error; use LogicException; use MediaWiki\Debug\MWDebug; use MediaWiki\WikiMap\WikiMap; use MWExceptionHandler; use Psr\Log\AbstractLogger; use Psr\Log\LogLevel; use Throwable; use UDPTransport; use Wikimedia\AtEase\AtEase; /** * PSR-3 logger that mimics the historic implementation of MediaWiki's former * wfErrorLog logging implementation. * * This logger is configured by the following global configuration variables: * - `$wgDebugLogFile` * - `$wgDebugLogGroups` * - `$wgDBerrorLog` * - `$wgDBerrorLogTZ` * * See docs/Configuration.md for detailed explanations of these settings. * * @see \MediaWiki\Logger\LoggerFactory * @since 1.25 * @ingroup Debug * @copyright © 2014 Wikimedia Foundation and contributors */ class LegacyLogger extends AbstractLogger { /** * @var string */ protected $channel; private const LEVEL_DEBUG = 100; private const LEVEL_INFO = 200; private const LEVEL_NOTICE = 250; private const LEVEL_WARNING = 300; private const LEVEL_ERROR = 400; private const LEVEL_CRITICAL = 500; private const LEVEL_ALERT = 550; private const LEVEL_EMERGENCY = 600; private const LEVEL_INFINITY = 999; /** * Convert \Psr\Log\LogLevel constants into int for sensible comparisons * These are the same values that Monolog uses * * @var array */ protected static $levelMapping = [ LogLevel::DEBUG => self::LEVEL_DEBUG, LogLevel::INFO => self::LEVEL_INFO, LogLevel::NOTICE => self::LEVEL_NOTICE, LogLevel::WARNING => self::LEVEL_WARNING, LogLevel::ERROR => self::LEVEL_ERROR, LogLevel::CRITICAL => self::LEVEL_CRITICAL, LogLevel::ALERT => self::LEVEL_ALERT, LogLevel::EMERGENCY => self::LEVEL_EMERGENCY, ]; /** * Minimum level. This is just to allow faster discard of debugging * messages. Not all messages meeting the level will be logged. * * @var int */ private $minimumLevel; /** * Whether the channel is a DB channel * * @var bool */ private $isDB; /** * @param string $channel */ public function __construct( $channel ) { global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups, $wgDebugToolbar, $wgDebugRawPage, $wgShowDebug; $this->channel = $channel; $this->isDB = ( $channel === 'rdbms' ); // Calculate minimum level, duplicating some of the logic from log() and shouldEmit() if ( !$wgDebugRawPage && wfIsDebugRawPage() ) { $this->minimumLevel = self::LEVEL_WARNING; } elseif ( $wgDebugLogFile != '' || $wgShowDebug || $wgDebugToolbar ) { // Log all messages if there is a debug log file or debug toolbar $this->minimumLevel = self::LEVEL_DEBUG; } elseif ( isset( $wgDebugLogGroups[$channel] ) ) { $logConfig = $wgDebugLogGroups[$channel]; // Log messages if the config is set, according to the configured level if ( is_array( $logConfig ) && isset( $logConfig['level'] ) ) { $this->minimumLevel = self::$levelMapping[$logConfig['level']]; } else { $this->minimumLevel = self::LEVEL_DEBUG; } } else { // No other case hit: discard all messages $this->minimumLevel = self::LEVEL_INFINITY; } if ( $this->isDB && $wgDBerrorLog && $this->minimumLevel > self::LEVEL_ERROR ) { // Log DB errors if there is a DB error log $this->minimumLevel = self::LEVEL_ERROR; } } /** * Change an existing Logger singleton to act like NullLogger. * * @internal For use by MediaWikiIntegrationTestCase::setNullLogger * @param null|int $level * @return int */ public function setMinimumForTest( ?int $level ) { if ( !defined( 'MW_PHPUNIT_TEST' ) ) { throw new LogicException( 'Not allowed outside tests' ); } // Set LEVEL_INFINITY if given null, or restore the original level. $original = $this->minimumLevel; $this->minimumLevel = $level ?? self::LEVEL_INFINITY; return $original; } /** * Logs with an arbitrary level. * * @param string|int $level * @param string $message * @param array $context */ public function log( $level, $message, array $context = [] ) { if ( is_string( $level ) ) { $level = self::$levelMapping[$level]; } if ( $level < $this->minimumLevel ) { return; } if ( $this->isDB && $level === self::LEVEL_DEBUG && isset( $context['sql'] ) ) { // Also give the query information to the MWDebug tools MWDebug::query( $context['sql'], $context['method'], $context['runtime_ms'] / 1000, $context['db_server'] ); } // If this is a DB-related error, and the site has $wgDBerrorLog // configured, rewrite the channel as wfLogDBError instead. // Likewise, if the site does not use $wgDBerrorLog, it should // configurable like any other channel via $wgDebugLogGroups // or $wgMWLoggerDefaultSpi. global $wgDBerrorLog; if ( $this->isDB && $level >= self::LEVEL_ERROR && $wgDBerrorLog ) { // Format and write DB errors to the legacy locations $effectiveChannel = 'wfLogDBError'; } else { $effectiveChannel = $this->channel; } if ( self::shouldEmit( $effectiveChannel, $message, $level, $context ) ) { $text = self::format( $effectiveChannel, $message, $context ); $destination = self::destination( $effectiveChannel, $message, $context ); $this->maybeLogToStderr( $text ); self::emit( $text, $destination ); } if ( !isset( $context['private'] ) || !$context['private'] ) { // Add to debug toolbar if not marked as "private" MWDebug::debugMsg( $message, [ 'channel' => $this->channel ] + $context ); } } /** * Determine if the given message should be emitted or not. * * @param string $channel * @param string $message * @param string|int $level \Psr\Log\LogEvent constant or Monolog level int * @param array $context * @return bool True if message should be sent to disk/network, false * otherwise */ public static function shouldEmit( $channel, $message, $level, $context ) { global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups; if ( is_string( $level ) ) { $level = self::$levelMapping[$level]; } if ( $channel === 'wfLogDBError' ) { // wfLogDBError messages are emitted if a database log location is // specified. $shouldEmit = (bool)$wgDBerrorLog; } elseif ( $channel === 'wfDebug' ) { // wfDebug messages are emitted if a catch all logging file has // been specified. Checked explicitly so that 'private' flagged // messages are not discarded by unset $wgDebugLogGroups channel // handling below. $shouldEmit = $wgDebugLogFile != ''; } elseif ( isset( $wgDebugLogGroups[$channel] ) ) { $logConfig = $wgDebugLogGroups[$channel]; if ( is_array( $logConfig ) ) { $shouldEmit = true; if ( isset( $logConfig['sample'] ) ) { // Emit randomly with a 1 in 'sample' chance for each message. $shouldEmit = mt_rand( 1, $logConfig['sample'] ) === 1; } if ( isset( $logConfig['level'] ) ) { $shouldEmit = $level >= self::$levelMapping[$logConfig['level']]; } } else { // Emit unless the config value is explicitly false. $shouldEmit = $logConfig !== false; } } elseif ( isset( $context['private'] ) && $context['private'] ) { // Don't emit if the message didn't match previous checks based on // the channel and the event is marked as private. This check // discards messages sent via wfDebugLog() with dest == 'private' // and no explicit wgDebugLogGroups configuration. $shouldEmit = false; } else { // Default return value is the same as the historic wfDebug // method: emit if $wgDebugLogFile has been set. $shouldEmit = $wgDebugLogFile != ''; } return $shouldEmit; } /** * Format a message. * * Messages to the 'wfDebug' and 'wfLogDBError' channels receive special formatting to mimic the * historic output of the functions of the same name. All other channel values are formatted * based on the historic output of the `wfDebugLog()` global function. * * @param string $channel * @param string $message * @param array $context * @return string */ public static function format( $channel, $message, $context ) { global $wgDebugLogGroups, $wgLogExceptionBacktrace; if ( $channel === 'wfDebug' ) { $text = self::formatAsWfDebug( $channel, $message, $context ); } elseif ( $channel === 'wfLogDBError' ) { $text = self::formatAsWfLogDBError( $channel, $message, $context ); } elseif ( !isset( $wgDebugLogGroups[$channel] ) ) { $text = self::formatAsWfDebug( $channel, "[{$channel}] {$message}", $context ); } else { // Default formatting is wfDebugLog's historic style $text = self::formatAsWfDebugLog( $channel, $message, $context ); } // Append stacktrace of throwable if available if ( $wgLogExceptionBacktrace && isset( $context['exception'] ) ) { $e = $context['exception']; $backtrace = false; if ( $e instanceof Throwable ) { $backtrace = MWExceptionHandler::getRedactedTrace( $e ); } elseif ( is_array( $e ) && isset( $e['trace'] ) ) { // Throwable has already been unpacked as structured data $backtrace = $e['trace']; } if ( $backtrace ) { $text .= MWExceptionHandler::prettyPrintTrace( $backtrace ) . "\n"; } } return self::interpolate( $text, $context ); } /** * Format a message as `wfDebug()` would have formatted it. * * @param string $channel * @param string $message * @param array $context * @return string */ protected static function formatAsWfDebug( $channel, $message, $context ) { $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $message ); if ( isset( $context['seconds_elapsed'] ) ) { // Prepend elapsed request time and real memory usage with two // trailing spaces. $text = "{$context['seconds_elapsed']} {$context['memory_used']} {$text}"; } if ( isset( $context['prefix'] ) ) { $text = "{$context['prefix']}{$text}"; } return "{$text}\n"; } /** * Format a message as `wfLogDBError()` would have formatted it. * * @param string $channel * @param string $message * @param array $context * @return string */ protected static function formatAsWfLogDBError( $channel, $message, $context ) { global $wgDBerrorLogTZ; static $cachedTimezone = null; if ( !$cachedTimezone ) { $cachedTimezone = new DateTimeZone( $wgDBerrorLogTZ ); } $d = date_create( 'now', $cachedTimezone ); $date = $d->format( 'D M j G:i:s T Y' ); $host = wfHostname(); $wiki = WikiMap::getCurrentWikiId(); $text = "{$date}\t{$host}\t{$wiki}\t{$message}\n"; return $text; } /** * Format a message as `wfDebugLog() would have formatted it. * * @param string $channel * @param string $message * @param array $context * @return string */ protected static function formatAsWfDebugLog( $channel, $message, $context ) { $time = wfTimestamp( TS_DB ); $wiki = WikiMap::getCurrentWikiId(); $host = wfHostname(); $text = "{$time} {$host} {$wiki}: {$message}\n"; return $text; } /** * Interpolate placeholders in logging message. * * @param string $message * @param array $context * @return string Interpolated message */ public static function interpolate( $message, array $context ) { if ( str_contains( $message, '{' ) ) { $replace = []; foreach ( $context as $key => $val ) { $replace['{' . $key . '}'] = self::flatten( $val ); } $message = strtr( $message, $replace ); } return $message; } /** * Convert a logging context element to a string suitable for * interpolation. * * @param mixed $item * @return string */ protected static function flatten( $item ) { if ( $item === null ) { return '[Null]'; } if ( is_bool( $item ) ) { return $item ? 'true' : 'false'; } if ( is_float( $item ) ) { if ( is_infinite( $item ) ) { return ( $item > 0 ? '' : '-' ) . 'INF'; } if ( is_nan( $item ) ) { return 'NaN'; } return (string)$item; } if ( is_scalar( $item ) ) { return (string)$item; } if ( is_array( $item ) ) { return '[Array(' . count( $item ) . ')]'; } if ( $item instanceof \DateTime ) { return $item->format( 'c' ); } if ( $item instanceof Throwable ) { $which = $item instanceof Error ? 'Error' : 'Exception'; return '[' . $which . ' ' . get_class( $item ) . '( ' . $item->getFile() . ':' . $item->getLine() . ') ' . $item->getMessage() . ']'; } if ( is_object( $item ) ) { if ( method_exists( $item, '__toString' ) ) { return (string)$item; } return '[Object ' . get_class( $item ) . ']'; } // phpcs:ignore MediaWiki.Usage.ForbiddenFunctions.is_resource if ( is_resource( $item ) ) { return '[Resource ' . get_resource_type( $item ) . ']'; } return '[Unknown ' . get_debug_type( $item ) . ']'; } /** * Select the appropriate log output destination for the given log event. * * If the event context contains 'destination' * * @param string $channel * @param string $message * @param array $context * @return string */ protected static function destination( $channel, $message, $context ) { global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups; // Default destination is the debug log file as historically used by // the wfDebug function. $destination = $wgDebugLogFile; if ( isset( $context['destination'] ) ) { // Use destination explicitly provided in context $destination = $context['destination']; } elseif ( $channel === 'wfDebug' ) { $destination = $wgDebugLogFile; } elseif ( $channel === 'wfLogDBError' ) { $destination = $wgDBerrorLog; } elseif ( isset( $wgDebugLogGroups[$channel] ) ) { $logConfig = $wgDebugLogGroups[$channel]; if ( is_array( $logConfig ) ) { $destination = $logConfig['destination']; } else { $destination = strval( $logConfig ); } } return $destination; } /** * Log to a file without getting "file size exceeded" signals. * * Can also log to UDP with the syntax udp://host:port/prefix. This will send * lines to the specified port, prefixed by the specified prefix and a space. * * @param string $text * @param string $file Filename */ public static function emit( $text, $file ) { if ( str_starts_with( $file, 'udp:' ) ) { $transport = UDPTransport::newFromString( $file ); $transport->emit( $text ); } else { AtEase::suppressWarnings(); $exists = file_exists( $file ); $size = $exists ? filesize( $file ) : false; if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) { file_put_contents( $file, $text, FILE_APPEND ); } AtEase::restoreWarnings(); } } /** * If MW_LOG_STDERR is set (used currently in `composer serve`) then also * emit to stderr using error_log(). * * @param string $text * @return void */ private function maybeLogToStderr( string $text ): void { if ( getenv( 'MW_LOG_STDERR' ) ) { error_log( trim( $text ) ); } } } PK ! ���{M M NullSpi.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file */ namespace MediaWiki\Logger; use Psr\Log\NullLogger; /** * LoggerFactory service provider that creates \Psr\Log\NullLogger * instances. A NullLogger silently discards all log events sent to it. * * Usage: * * $wgMWLoggerDefaultSpi = [ * 'class' => \MediaWiki\Logger\NullSpi::class, * ]; * * @see \MediaWiki\Logger\LoggerFactory * @since 1.25 * @ingroup Debug * @copyright © 2014 Wikimedia Foundation and contributors */ class NullSpi implements Spi { /** * @var \Psr\Log\NullLogger */ protected $singleton; public function __construct() { $this->singleton = new NullLogger(); } /** * Get a logger instance. * * @param string $channel Logging channel * @return \Psr\Log\NullLogger Logger instance */ public function getLogger( $channel ) { return $this->singleton; } } PK ! 5s<� � LegacySpi.phpnu �Iw�� PK ! ?{> � Spi.phpnu �Iw�� PK ! B-8�$ �$ MonologSpi.phpnu �Iw�� PK ! ����h h 37 LogCapturingSpi.phpnu �Iw�� PK ! ґH� � �@ ConsoleLogger.phpnu �Iw�� PK ! r�N�z z H LoggerFactory.phpnu �Iw�� PK ! E�'� � �T ConsoleSpi.phpnu �Iw�� PK ! ����� � �Y monolog/MwlogHandler.phpnu �Iw�� PK ! w�~S� � �g monolog/LegacyHandler.phpnu �Iw�� PK ! ��M) ) � monolog/LegacyFormatter.phpnu �Iw�� PK ! l�_�# # �� monolog/SyslogHandler.phpnu �Iw�� PK ! ?�yD D � monolog/CeeFormatter.phpnu �Iw�� PK ! ���c c � monolog/LineFormatter.phpnu �Iw�� PK ! �Q�� � +� monolog/BufferHandler.phpnu �Iw�� PK ! \!cAU U � monolog/WikiProcessor.phpnu �Iw�� PK ! �S�Z� � �� monolog/LogstashFormatter.phpnu �Iw�� PK ! �fw�8= 8= �� LegacyLogger.phpnu �Iw�� PK ! ���{M M c NullSpi.phpnu �Iw�� PK � �
| ver. 1.1 | |
.
| PHP 8.4.18 | Ð“ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ñтраницы: 0.01 |
proxy
|
phpinfo
|
ÐаÑтройка