      1: <?php
      2: /**
      3:  * Licensed to the Apache Software Foundation (ASF) under one or more
      4:  * contributor license agreements.  See the NOTICE file distributed with
      5:  * this work for additional information regarding copyright ownership.
      6:  * The ASF licenses this file to You under the Apache License, Version 2.0
      7:  * (the "License"); you may not use this file except in compliance with
      8:  * the License.  You may obtain a copy of the License at
      9:  *
     10:  *     http://www.apache.org/licenses/LICENSE-2.0
     11:  *
     12:  * Unless required by applicable law or agreed to in writing, software
     13:  * distributed under the License is distributed on an "AS IS" BASIS,
     14:  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15:  * See the License for the specific language governing permissions and
     16:  * limitations under the License.
     17:  *
     18:  * @package log4php
     19:  */
     20:
     21: /**
     22:  * The output of the LoggerXmlLayout consists of a series of log4php:event elements.
     23:  *
     24:  * Configurable parameters:
     25:  * - {@link $locationInfo} - If set to true then the file name and line number
     26:  *   of the origin of the log statement will be included in output.
     27:  * - {@link $log4jNamespace} - If set to true then log4j namespace will be used
     28:  *   instead of log4php namespace. This can be usefull when using log viewers
     29:  *   which can only parse the log4j namespace such as Apache Chainsaw.
     30:  *
     31:  * <p>It does not output a complete well-formed XML file.
     32:  * The output is designed to be included as an external entity in a separate file to form
     33:  * a correct XML file.</p>
     34:  *
     35:  * Example:
     36:  *
     37:  * {@example ../../examples/php/layout_xml.php 19}<br>
     38:  *
     39:  * {@example ../../examples/resources/layout_xml.properties 18}<br>
     40:  *
     41:  * The above would print:
     42:  *
     43:  * <pre>
     44:  * <log4php:eventSet xmlns:log4php="http://logging.apache.org/log4php/" version="0.3" includesLocationInfo="true">
     45:  *  <log4php:event logger="root" level="INFO" thread="13802" timestamp="1252456226491">
     46:  *      <log4php:message><![CDATA[Hello World!]]></log4php:message>
     47:  *      <log4php:locationInfo class="main" file="examples/php/layout_xml.php" line="6" method="main" />
     48:  *  </log4php:event>
     49:  * </log4php:eventSet>
     50:  * </pre>
     51:  *
     52:  * @version $Revision: 1213283 $
     53:  * @package log4php
     54:  * @subpackage layouts
     55:  */
     56: class LoggerLayoutXml extends LoggerLayout {
     57:     const LOG4J_NS_PREFIX ='log4j';
     58:     const LOG4J_NS = 'http://jakarta.apache.org/log4j/';
     59:
     60:     const LOG4PHP_NS_PREFIX = 'log4php';
     61:     const LOG4PHP_NS = 'http://logging.apache.org/log4php/';
     62:
     63:     const CDATA_START = '<![CDATA[';
     64:     const CDATA_END = ']]>';
     65:     const CDATA_PSEUDO_END = ']]&gt;';
     66:     const CDATA_EMBEDDED_END = ']]>]]&gt;<![CDATA[';
     67:
     68:     /**
     69:      * If set to true then the file name and line number of the origin of the
     70:      * log statement will be output.
     71:      * @var boolean
     72:      */
     73:     protected $locationInfo = true;
     74:
     75:     /**
     76:      * If set to true, log4j namespace will be used instead of the log4php
     77:      * namespace.
     78:      * @var boolean
     79:      */
     80:     protected $log4jNamespace = false;
     81:
     82:     /** The namespace in use. */
     83:     protected $namespace = self::LOG4PHP_NS;
     84:
     85:     /** The namespace prefix in use */
     86:     protected $namespacePrefix = self::LOG4PHP_NS_PREFIX;
     87:
     88:     public function activateOptions() {
     89:         if ($this->getLog4jNamespace()) {
     90:             $this->namespace        = self::LOG4J_NS;
     91:             $this->namespacePrefix  = self::LOG4J_NS_PREFIX;
     92:         } else {
     93:             $this->namespace        = self::LOG4PHP_NS;
     94:             $this->namespacePrefix  = self::LOG4PHP_NS_PREFIX;
     95:         }
     96:     }
     97:
     98:     /**
     99:      * @return string
    100:      */
    101:     public function getHeader() {
    102:         return "<{$this->namespacePrefix}:eventSet ".
    103:             "xmlns:{$this->namespacePrefix}=\"{$this->namespace}\" ".
    104:             "version=\"0.3\" ".
    105:             "includesLocationInfo=\"".($this->getLocationInfo() ? "true" : "false")."\"".
    106:             ">" . PHP_EOL;
    107:     }
    108:
    109:     /**
    110:      * Formats a {@link LoggerLoggingEvent} in conformance with the log4php.dtd.
    111:      *
    112:      * @param LoggerLoggingEvent $event
    113:      * @return string
    114:      */
    115:     public function format(LoggerLoggingEvent $event) {
    116:         $ns = $this->namespacePrefix;
    117:
    118:         $loggerName = $event->getLoggerName();
    119:         $timeStamp = number_format((float)($event->getTimeStamp() * 1000), 0, '', '');
    120:         $thread = $event->getThreadName();
    121:         $level = $event->getLevel()->toString();
    122:
    123:         $buf  = "<$ns:event logger=\"{$loggerName}\" level=\"{$level}\" thread=\"{$thread}\" timestamp=\"{$timeStamp}\">".PHP_EOL;
    124:         $buf .= "<$ns:message>";
    125:         $buf .= $this->encodeCDATA($event->getRenderedMessage());
    126:         $buf .= "</$ns:message>".PHP_EOL;
    127:
    128:         $ndc = $event->getNDC();
    129:         if(!empty($ndc)) {
    130:             $buf .= "<$ns:NDC><![CDATA[";
    131:             $buf .= $this->encodeCDATA($ndc);
    132:             $buf .= "]]></$ns:NDC>".PHP_EOL;
    133:         }
    134:
    135:         $mdcMap = $event->getMDCMap();
    136:         if (!empty($mdcMap)) {
    137:             $buf .= "<$ns:properties>".PHP_EOL;
    138:             foreach ($mdcMap as $name=>$value) {
    139:                 $buf .= "<$ns:data name=\"$name\" value=\"$value\" />".PHP_EOL;
    140:             }
    141:             $buf .= "</$ns:properties>".PHP_EOL;
    142:         }
    143:
    144:         if ($this->getLocationInfo()) {
    145:             $locationInfo = $event->getLocationInformation();
    146:             $buf .= "<$ns:locationInfo ".
    147:                     "class=\"" . $locationInfo->getClassName() . "\" ".
    148:                     "file=\"" .  htmlentities($locationInfo->getFileName(), ENT_QUOTES) . "\" ".
    149:                     "line=\"" .  $locationInfo->getLineNumber() . "\" ".
    150:                     "method=\"" . $locationInfo->getMethodName() . "\" ";
    151:             $buf .= "/>".PHP_EOL;
    152:         }
    153:
    154:         $buf .= "</$ns:event>".PHP_EOL;
    155:
    156:         return $buf;
    157:     }
    158:
    159:     /**
    160:      * @return string
    161:      */
    162:     public function getFooter() {
    163:         return "</{$this->namespacePrefix}:eventSet>" . PHP_EOL;
    164:     }
    165:
    166:
    167:     /**
    168:      * Whether or not file name and line number will be included in the output.
    169:      * @return boolean
    170:      */
    171:     public function getLocationInfo() {
    172:         return $this->locationInfo;
    173:     }
    174:
    175:     /**
    176:      * The {@link $locationInfo} option takes a boolean value. By default,
    177:      * it is set to false which means there will be no location
    178:      * information output by this layout. If the the option is set to
    179:      * true, then the file name and line number of the statement at the
    180:      * origin of the log statement will be output.
    181:      */
    182:     public function setLocationInfo($flag) {
    183:         $this->setBoolean('locationInfo', $flag);
    184:     }
    185:
    186:     /**
    187:      * @return boolean
    188:      */
    189:      public function getLog4jNamespace() {
    190:         return $this->log4jNamespace;
    191:      }
    192:
    193:     /**
    194:      * @param boolean
    195:      */
    196:     public function setLog4jNamespace($flag) {
    197:         $this->setBoolean('log4jNamespace', $flag);
    198:     }
    199:
    200:     /**
    201:      * Encases a string in CDATA tags, and escapes any existing CDATA end
    202:      * tags already present in the string.
    203:      * @param string $string
    204:      */
    205:     private function encodeCDATA($string) {
    206:         $string = str_replace(self::CDATA_END, self::CDATA_EMBEDDED_END, $string);
    207:         return self::CDATA_START . $string . self::CDATA_END;
    208:     }
    209: }
    210:
    211: