joomla-platform / libraries / joomla / log / loggers / formattedtext.php

<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Log
 *
 * @copyright   Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

jimport('joomla.log.log');
jimport('joomla.log.logger');
jimport('joomla.filesystem.folder');

/**
 * Joomla! Formatted Text File Log class
 *
 * This class is designed to use as a base for building formatted text files for output. By
 * default it emulates the SysLog style format output. This is a disk based output format.
 *
 * @package     Joomla.Platform
 * @subpackage  Log
 * @since       11.1
 */
class JLoggerFormattedText extends JLogger
{
    /**
     * @var    resource  The file pointer for the log file.
     * @since  11.1
     */
    protected $file;

    /**
     * @var    string  The format for which each entry follows in the log file.  All fields must be named
     *                 in all caps and be within curly brackets eg. {FOOBAR}.
     * @since  11.1
     */
    protected $format = '{DATETIME} {PRIORITY}  {CATEGORY}  {MESSAGE}';

    /**
     * @var    array  The parsed fields from the format string.
     * @since  11.1
     */
    protected $fields = array();

    /**
     * @var    string  The full filesystem path for the log file.
     * @since  11.1
     */
    protected $path;

    /**
     * @var    array  Translation array for JLogEntry priorities to text strings.
     * @since  11.1
     */
    protected $priorities = array(
        JLog::EMERGENCY => 'EMERGENCY',
        JLog::ALERT     => 'ALERT',
        JLog::CRITICAL  => 'CRITICAL',
        JLog::ERROR     => 'ERROR',
        JLog::WARNING   => 'WARNING',
        JLog::NOTICE    => 'NOTICE',
        JLog::INFO      => 'INFO',
        JLog::DEBUG     => 'DEBUG'
    );

    /**
     * Constructor.
     *
     * @param   array  $options  Log object options.
     *
     * @return  void
     *
     * @since   11.1
     */
    public function __construct(array & $options)
    {
        // Call the parent constructor.
        parent::__construct($options);

        // The name of the text file defaults to 'error.php' if not explicitly given.
        if (empty($this->options['text_file'])) {
            $this->options['text_file'] = 'error.php';
        }

        // The name of the text file path defaults to that which is set in configuration if not explicitly given.
        if (empty($this->options['text_file_path'])) {
            $this->options['text_file_path'] = JFactory::getConfig()->get('log_path');
        }

        // False to treat the log file as a php file.
        if (empty($this->options['text_file_no_php'])) {
            $this->options['text_file_no_php'] = false;
        }

        // Build the full path to the log file.
        $this->path = $this->options['text_file_path'].'/'.$this->options['text_file'];

        // Use the default entry format unless explicitly set otherwise.
        if (!empty($this->options['text_entry_format'])) {
            $this->format = (string) $this->options['text_entry_format'];
        }

        // Build the fields array based on the format string.
        $this->parseFields();
    }

    /**
     * Destructor.
     *
     * @return  void
     *
     * @since   11.1
     */
    public function __destruct()
    {
        if (is_resource($this->file)) {
            fclose($this->file);
        }
    }

    /**
     * Method to add an entry to the log.
     *
     * @param   JLogEntry  The log entry object to add to the log.
     *
     * @return  boolean  True on success.
     *
     * @since   11.1
     * @throws  LogException
     */
    public function addEntry(JLogEntry $entry)
    {
        // Initialise the file if not already done.
        if (!is_resource($this->file)) {
            $this->initFile();
        }

        // Set some default field values if not already set.
        if (!isset ($entry->clientIP)) {

            // Check for proxies as well.
            if (isset($_SERVER['REMOTE_ADDR'])) {
                $entry->clientIP = $_SERVER['REMOTE_ADDR'];
            }
            elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $entry->clientIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
            }
            elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
                $entry->clientIP = $_SERVER['HTTP_CLIENT_IP'];
            }
        }

        // If the time field is missing or the date field isn't only the date we need to rework it.
        if ((strlen($entry->date) != 10) || !isset($entry->time)) {

            // Get the date and time strings in GMT.
            $entry->datetime = $entry->date->toISO8601();
            $entry->time = $entry->date->format('H:i:s', false);
            $entry->date = $entry->date->format('Y-m-d', false);
        }

        // Decode the entry priority into an English string.
        $entry->priority = $this->priorities[$entry->priority];

        // Get a list of all the entry keys and make sure they are upper case.
        $tmp = array_change_key_case(get_object_vars($entry), CASE_UPPER);

        // Fill in field data for the line.
        $line = $this->format;
        foreach ($this->fields as $field)
        {
            $line = str_replace('{'.$field.'}', (isset($tmp[$field])) ? $tmp[$field] : '-', $line);
        }

        // Write the new entry to the file.
        if (!fputs($this->file, $line."\n")) {
            throw new LogException();
        }
    }

    /**
     * Method to generate the log file header.
     *
     * @return  void
     *
     * @since   11.1
     */
    protected function generateFileHeader()
    {
        // Initialize variables.
        $head = array();

        // Build the log file header.

        // If the no php flag is not set add the php die statement.
        if (empty($this->options['text_file_no_php'])) {
            $head[] = '#<?php die(\'Forbidden.\'); ?>';
        }
        $head[] = '#Date: '.gmdate('Y-m-d H:i:s').' UTC';
        $head[] = '#Software: '.JVersion::getLongVersion();
        $head[] = '';

        // Prepare the fields string
        $head[] = '#Fields: '.strtolower(str_replace('}', '', str_replace('{', '', $this->format)));
        $head[] = '';

        return implode("\n", $head);
    }

    /**
     * Method to initialise the log file.  This will create the folder path to the file if it doesn't already
     * exist and also get a new file header if the file doesn't already exist.  If the file already exists it
     * will simply open it for writing.
     *
     * @return  void
     *
     * @since   11.1
     */
    protected function initFile()
    {
        // If the file doesn't already exist we need to create it and generate the file header.
        if (!is_file($this->path)) {

            // Make sure the folder exists in which to create the log file.
            JFolder::create(dirname($this->path));

            // Build the log file header.
            $head = $this->generateFileHeader();
        }
        else {
            $head = false;
        }

        // Open the file for writing (append mode).
        if (!$this->file = fopen($this->path, 'a')) {
            // Throw exception.
        }
        if ($head) {
            if (!fputs($this->file, $head)) {
                throw new LogException();
            }
        }
    }

    /**
     * Method to parse the format string into an array of fields.
     *
     * @return  void
     *
     * @since   11.1
     */
    protected function parseFields()
    {
        // Initialise variables.
        $this->fields = array();
        $matches = array();

        // Get all of the available fields in the format string.
        preg_match_all("/{(.*?)}/i", $this->format, $matches);

        // Build the parsed fields list based on the found fields.
        foreach ($matches[1] as $match)
        {
            $this->fields[] = strtoupper($match);
        }
    }
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.