phossa2/logger

View on GitHub
src/Logger/Handler/LogfileHandler.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa2\Logger
 * @copyright Copyright (c) 2016 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa2\Logger\Handler;

use Phossa2\Logger\Message\Message;
use Phossa2\Logger\Exception\LogicException;
use Phossa2\Logger\Formatter\FormatterInterface;

/**
 * LogfileHandler
 *
 * Log to a file with file rotation support
 *
 * @package Phossa2\Logger
 * @author  Hong Zhang <phossa@126.com>
 * @see     StreamHandler
 * @version 2.0.0
 * @since   2.0.0 added
 * @since   2.0.1 updated constructor
 */
class LogfileHandler extends StreamHandler
{
    /**
     * rotation type
     * @var    int
     */
    const ROTATE_NONE =  0; // do not rotate
    const ROTATE_DATE = -1; // rotate by date

    /**
     * Constructor
     *
     * @param  string $path full path
     * @param  int $rotate rotate type or filesize in MB
     * @param  FormatterInterface $formatter
     * @param  bool $stopPropagation
     * @throws LogicException if path not writable
     * @access public
     * @since  2.0.1 removed level param
     */
    public function __construct(
        /*# string */ $path,
        /*# int */ $rotate = self::ROTATE_NONE,
        FormatterInterface $formatter = null,
        /*# bool */ $stopPropagation = false
    ) {
        // remove prefix 'file://' if any
        if ('file://' === substr($path, 0, 7)) {
            $path = substr($path, 7);
        }

        // check file path
        $this->checkPath($path);

        // rotate file ?
        if (file_exists($path)) {
            $this->doRotation($path, $rotate);
        }

        parent::__construct($path, $formatter, $stopPropagation);
    }

    /**
     * Check file path
     *
     * @param  string $path
     * @throws LogicException if directory failure etc.
     * @access protected
     */
    protected function checkPath(/*# string */$path)
    {
        // get the directory
        $dir = dirname(realpath($path));

        if (!is_dir($dir)) {
            mkdir($dir, 0777, true);
        }

        if (!is_dir($dir) || !is_writable($dir)) {
            throw new LogicException(
                Message::get(Message::MSG_PATH_NONWRITABLE, $dir),
                Message::MSG_PATH_NONWRITABLE
            );
        }
    }

    /**
     * Rotate file on start
     *
     * @param  string $path
     * @param  int $type
     * @return bool rotation status
     * @access protected
     */
    protected function doRotation(
        /*# string */ $path,
        /*# int */ $type
    )/*# : bool */ {
        switch ($type) {
            // no rotation
            case self::ROTATE_NONE:
                return true;

            // rotate by date
            case self::ROTATE_DATE:
                return $this->rotateByDate($path);

            // rotate by size
            default:
                return $this->rotateBySize($path, $type);
        }
    }

    /**
     * Rotate $path to $path_20160616
     *
     * @param  string $path
     * @param  string $format date format
     * @return bool rotation status
     * @access protected
     */
    protected function rotateByDate(
        /*# string */ $path,
        /*# string */ $format = 'Ymd'
    )/*# : bool */ {
        $time = filectime($path);
        return rename($path, $path . '_' . date($format, $time));
    }

    /**
     * Rotate $path if filesize > the specified in MB
     *
     * Rotate to $path.201606141310 (hour & minute)
     *
     * @param  string $path
     * @param  int $size size in MB
     * @return bool rotation status
     * @access protected
     */
    protected function rotateBySize(
        /*# string */ $path,
        /*# int */ $size
    )/*# : bool */ {
        if (round(filesize($path) / (1024 * 1024), 2) > $size) {
            return $this->rotateByDate($path, 'YmdHi');
        }
        return true;
    }
}