phpxmlloggingzend-framework2zend-log

Logging custom elements Zend Log Formatter XML


I'm trying to some information about a request and the result should be put in an XML file. I'm using the following elements:

Should be:

<logEntry><timestamp>2013-03-29T15:47:41+01:00</timestamp><priority>6</priority><priorityName>INFO</priorityName><message>Called</message><uniqueId>564fg56d4g5d4fg5f4g56fg465dfg</uniqueId><method>\Namespace\Controller::methodName</method><args>aa5abc8d6efeabcd7f67cb6a7df6bac5ba7a5fd7a5d6bac67a5bf6abcbb408f098=</args></logEntry>

The documentation I've read: http://zf2.readthedocs.org/en/latest/modules/zend.log.formatters.html

The not-working code:

$writer    = new \Zend\Log\Writer\Stream(getenv('LOG_FILE_LOCATION'));
$formatter = new \Zend\Log\Formatter\Xml('logEntry', array(
    'timestamp'    => 'timestamp',
    'priority'     => 'priority',
    'priorityName' => 'priorityName',
    'message'      => 'message',
    'uniqueId'     => 'uniqueId',
    'method'       => 'method',
    'args'         => 'args',
));
$writer->setFormatter($formatter);
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);

$logger->info('Started', array(
    'uniqueId' => $this->createGuid(),
    'method'   => __METHOD__,
    'args'     => serialize(array(get_class($e))), 
));

Error: Notice: Undefined index: uniqueId in /var/www/library/Zend/Log/Formatter/Xml.php on line 168

Code for Xml.php https://github.com/zendframework/zf2/blob/master/library/Zend/Log/Formatter/Xml.php#L168

$event[$fieldKey] is the problem. $fieldKey only exists in $event['extra'][$fieldKey]. $this->elementMap is flat(no extra array for extra is added).

Array
(
    [timestamp] => timestamp
    [priority] => priority
    [priorityName] => priorityName
    [message] => message
    [uniqueId] => uniqueId
    [method] => method
    [args] => args
)

What am I doing wrong?

I'm thinking this is a ZF2 bug... When reading the code on Github, I can't find and detectection for extra fields and keys... What do you guys think?


Solution

  • The problem is the Xml formatter ignores the extra data being passed to the logger (logger expects it to be an array, formatter implicitly ignores anything that isn't an empty string, object with __toString() method or scalar. The solution appears to be to write your own processor to merge the extra data with the regular event data being passed from the logger.

    Here's a very simple example, although you'd probably want to do some checking on permitted key values being passed in the array of extra data in your own function, but this will work for demonstration purposes. (You can probably come up with a better name for the class too :D )

    <?php
    namespace Application\Log\Processor;
    
    use Zend\Log\Processor\ProcessorInterface;
    
    class Merger implements ProcessorInterface
    {
    
        /**
         * Merge extra data into the event data
         * @param array $event event data
         * @return array event data
         */
        public function process(array $event)
        {
            if (!isset($event['extra'])) {
                $event['extra'] = array();
            }
            return array_merge($event, $event['extra']);
        }
    }
    

    To implement, do as you did in your first example of non-working code, but just pass in the root element name you want to use

    $writer    = new \Zend\Log\Writer\Stream(getenv('LOG_FILE_LOCATION'));
    $formatter = new \Zend\Log\Formatter\Xml('logEntry');
    $writer->setFormatter($formatter);
    $logger = new \Zend\Log\Logger();
    $logger->addWriter($writer);
    
    // add the processor into the mix
    $processor = new \Application\Log\Processor\Merger;
    $logger->addProcessor($processor);
    
    $data = array(
        'uniqueId' => $this->createGuid(),
        'method'   => __METHOD__,
        'args'     => serialize(func_get_args()), 
    );
    $logger->info('Testing', $data);