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; | ||
|  |     } | ||
|  | } |