177 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
		
		
			
		
	
	
			177 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * This file is part of the Monolog package.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * (c) Jordi Boggiano <j.boggiano@seld.be>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * For the full copyright and license information, please view the LICENSE
							 | 
						||
| 
								 | 
							
								 * file that was distributed with this source code.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Monolog\Handler;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Monolog\Logger;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Stores to any stream resource
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Can be used to store into php://stderr, remote and local files, etc.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @author Jordi Boggiano <j.boggiano@seld.be>
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class StreamHandler extends AbstractProcessingHandler
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    protected $stream;
							 | 
						||
| 
								 | 
							
								    protected $url;
							 | 
						||
| 
								 | 
							
								    private $errorMessage;
							 | 
						||
| 
								 | 
							
								    protected $filePermission;
							 | 
						||
| 
								 | 
							
								    protected $useLocking;
							 | 
						||
| 
								 | 
							
								    private $dirCreated;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @param resource|string $stream
							 | 
						||
| 
								 | 
							
								     * @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
							 | 
						||
| 
								 | 
							
								     * @param int|null        $filePermission Optional file permissions (default (0644) are only for owner read/write)
							 | 
						||
| 
								 | 
							
								     * @param bool            $useLocking     Try to lock log file before doing any writes
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @throws \Exception                If a missing directory is not buildable
							 | 
						||
| 
								 | 
							
								     * @throws \InvalidArgumentException If stream is not a resource or string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function __construct($stream, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        parent::__construct($level, $bubble);
							 | 
						||
| 
								 | 
							
								        if (is_resource($stream)) {
							 | 
						||
| 
								 | 
							
								            $this->stream = $stream;
							 | 
						||
| 
								 | 
							
								        } elseif (is_string($stream)) {
							 | 
						||
| 
								 | 
							
								            $this->url = $stream;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            throw new \InvalidArgumentException('A stream must either be a resource or a string.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $this->filePermission = $filePermission;
							 | 
						||
| 
								 | 
							
								        $this->useLocking = $useLocking;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function close()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if ($this->url && is_resource($this->stream)) {
							 | 
						||
| 
								 | 
							
								            fclose($this->stream);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $this->stream = null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Return the currently active stream if it is open
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return resource|null
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getStream()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->stream;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Return the stream URL if it was configured with a URL and not an active resource
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return string|null
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getUrl()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->url;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function write(array $record)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!is_resource($this->stream)) {
							 | 
						||
| 
								 | 
							
								            if (null === $this->url || '' === $this->url) {
							 | 
						||
| 
								 | 
							
								                throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            $this->createDir();
							 | 
						||
| 
								 | 
							
								            $this->errorMessage = null;
							 | 
						||
| 
								 | 
							
								            set_error_handler(array($this, 'customErrorHandler'));
							 | 
						||
| 
								 | 
							
								            $this->stream = fopen($this->url, 'a');
							 | 
						||
| 
								 | 
							
								            if ($this->filePermission !== null) {
							 | 
						||
| 
								 | 
							
								                @chmod($this->url, $this->filePermission);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            restore_error_handler();
							 | 
						||
| 
								 | 
							
								            if (!is_resource($this->stream)) {
							 | 
						||
| 
								 | 
							
								                $this->stream = null;
							 | 
						||
| 
								 | 
							
								                throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ($this->useLocking) {
							 | 
						||
| 
								 | 
							
								            // ignoring errors here, there's not much we can do about them
							 | 
						||
| 
								 | 
							
								            flock($this->stream, LOCK_EX);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $this->streamWrite($this->stream, $record);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ($this->useLocking) {
							 | 
						||
| 
								 | 
							
								            flock($this->stream, LOCK_UN);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Write to stream
							 | 
						||
| 
								 | 
							
								     * @param resource $stream
							 | 
						||
| 
								 | 
							
								     * @param array $record
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function streamWrite($stream, array $record)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        fwrite($stream, (string) $record['formatted']);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private function customErrorHandler($code, $msg)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @param string $stream
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return null|string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private function getDirFromStream($stream)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $pos = strpos($stream, '://');
							 | 
						||
| 
								 | 
							
								        if ($pos === false) {
							 | 
						||
| 
								 | 
							
								            return dirname($stream);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ('file://' === substr($stream, 0, 7)) {
							 | 
						||
| 
								 | 
							
								            return dirname(substr($stream, 7));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    private function createDir()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // Do not try to create dir if it has already been tried.
							 | 
						||
| 
								 | 
							
								        if ($this->dirCreated) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $dir = $this->getDirFromStream($this->url);
							 | 
						||
| 
								 | 
							
								        if (null !== $dir && !is_dir($dir)) {
							 | 
						||
| 
								 | 
							
								            $this->errorMessage = null;
							 | 
						||
| 
								 | 
							
								            set_error_handler(array($this, 'customErrorHandler'));
							 | 
						||
| 
								 | 
							
								            $status = mkdir($dir, 0777, true);
							 | 
						||
| 
								 | 
							
								            restore_error_handler();
							 | 
						||
| 
								 | 
							
								            if (false === $status && !is_dir($dir)) {
							 | 
						||
| 
								 | 
							
								                throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $this->dirCreated = true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |