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 = ']]>';
66: const CDATA_EMBEDDED_END = ']]>]]><![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: