Vova Lando
Vova Lando

Reputation: 558

SugarCRM: adding custom Helper Class

Recently got into SugarCRM, ATM I have to implement custom Logger, the idea is to get some relevant data from Bean object from after save logic hook and put it into XML (as requested). I think that implementing it as a Sugar module could be wrong approach.

The question is where its right to put my class, in the directory hierarchy, and how should I bootstrap him?

Thanks in advance

Upvotes: 2

Views: 1126

Answers (1)

RobertGonzalez
RobertGonzalez

Reputation: 164

You'll want to put your class in custom/include/SugarLogger and name it something like SugarXMLLogger (this is flexible, but follows convention within SugarCRM). Make sure to name the class the same as the file.

You should implement LoggerTemplate at the very least and, if you want the complete structure of the default logger used by SugarCRM, you would want to extend SugarLogger. However, for a simple XML logger this isn't entirely necessary.

While I know you didn't ask for code to actually do the logging, when testing out the actual building of a custom logger, I decided to make one. This is my attempt at a very simple XML logger using SimpleXML. I tested this against the Ping API to watch it work using both fatal logs to XML and all logs to XML.

<?php
/** 
 * Save to custom/include/SugarLogger/SugarXMLLogger.php
 * 
 * Usage: 
 * To make one particular log level write to the XML log do this:
 * ```php
 * <?php
 * LoggerManager::setLogger('fatal', 'SugarXMLLogger');
 * $GLOBALS['log']->fatal('Testing out the XML logger');
 * ```
 * 
 * To make all levels log to the XML log, do this
 * ```php
 * <?php
 * LoggerManager::setLogger('default', 'SugarXMLLogger');
 * $GLOBALS['log']->warn('Testing out the XML logger');
 * ```
 */

/**
 * Get the interface that his logger should implement
 */
require_once 'include/SugarLogger/LoggerTemplate.php';

/**
 * SugarXMLLogger - A very simple logger that will save log entries into an XML
 * log file
 */
class SugarXMLLogger implements LoggerTemplate
{
    /**
     * The name of the log file
     * 
     * @var string
     */
    protected $logfile = 'sugarcrm.log.xml';

    /**
     * The format for the timestamp entry of the log
     * 
     * @var string
     */
    protected $dateFormat = '%c';

    /**
     * The current SimpleXMLElement logger resource
     * 
     * @var SimpleXMLElement
     */
    protected $currentData;

    /**
     * Logs an entry to the XML log
     * 
     * @param string $level The log level being logged
     * @param array $message The message to log
     * @return boolean True if the log was saved
     */
    public function log($level, $message)
    {
        // Get the current log XML
        $this->setCurrentLog();

        // Append to it
        $this->appendToLog($level, $message);

        // Save it
        return $this->saveLog();
    }

    /**
     * Saves the log file
     * 
     * @return boolean True if the save was successful
     */
    protected function saveLog()
    {
        $write = $this->currentData->asXML();
        return sugar_file_put_contents_atomic($this->logfile, $write);
    }

    /**
     * Sets the SimpleXMLElement log object
     * 
     * If there is an existing log, it will consume it. Otherwise it will create
     * a SimpleXMLElement object from a default construct.
     */
    protected function setCurrentLog()
    {
        if (file_exists($this->logfile)) {
            $this->currentData = simplexml_load_file($this->logfile);
        } else {
            sugar_touch($this->logfile);
            $this->currentData = simplexml_load_string("<?xml version='1.0' standalone='yes'?><entries></entries>");
        }
    }

    /**
     * Adds an entry of level $level to the log, with message $message
     * 
     * @param string $level The log level being logged
     * @param array $message The message to log
     */
    protected function appendToLog($level, $message)
    {
        // Set some basics needed for every entry, starting with the current
        // user id
        $userID = $this->getUserID();

        // Get the process id
        $pid = getmypid();

        // Get the message to log
        $message = $this->getMessage($message);

        // Set the timestamp
        $timestamp = strftime($this->dateFormat);

        // Add it to the data now
        $newEntry = $this->currentData->addChild('entry');
        $newEntry->addChild('timestamp', $timestamp);
        $newEntry->addChild('pid', $pid);
        $newEntry->addChild('userid', $userID);
        $newEntry->addChild('level', $level);
        $newEntry->addChild('message', $message);
    }

    /**
     * Gets the user id for the current user, or '-none-' if the current user ID
     * is not attainable
     * 
     * @return string The ID of the current user
     */
    protected function getUserID()
    {
        if (!empty($GLOBALS['current_user']->id)) {
            return $GLOBALS['current_user']->id;
        } 

        return '-none-';
    }

    /**
     * Gets the message in a loggable format
     * 
     * @param mixed $message The message to log, as a string or an array
     * @return string The message to log, as a string
     */
    protected function getMessage($message)
    {
        if (is_array($message) && count($message) == 1) {
            $message = array_shift($message);
        }

        // change to a human-readable array output if it's any other array
        if (is_array($message)) {
            $message = print_r($message,true);
        }

        return $message;
    }
}

Upvotes: 4

Related Questions