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 internal representation of logging event.
23 *
24 * @version $Revision: 832662 $
25 * @package log4php
26 */
27class LoggerLoggingEvent {
28
29	private static $startTime;
30
31	/**
32	* @var string Fully Qualified Class Name of the calling category class.
33	*/
34	private $fqcn;
35
36	/**
37	* @var Logger reference
38	*/
39	private $logger = null;
40
41	/**
42	* The category (logger) name.
43	* This field will be marked as private in future
44	* releases. Please do not access it directly.
45	* Use the {@link getLoggerName()} method instead.
46	* @deprecated
47	*/
48	private $categoryName;
49
50	/**
51	* Level of logging event.
52	* <p> This field should not be accessed directly. You shoud use the
53	* {@link getLevel()} method instead.
54	*
55	* @deprecated
56	* @var LoggerLevel
57	*/
58	protected $level;
59
60	/**
61	 * @var string The nested diagnostic context (NDC) of logging event.
62	 */
63	private $ndc;
64
65	/**
66	 * Have we tried to do an NDC lookup? If we did, there is no need
67	 * to do it again.	Note that its value is always false when
68	 * serialized. Thus, a receiving SocketNode will never use it's own
69	 * (incorrect) NDC. See also writeObject method.
70	 * @var boolean
71	 */
72	private $ndcLookupRequired = true;
73
74	/**
75	 * Have we tried to do an MDC lookup? If we did, there is no need
76	 * to do it again.	Note that its value is always false when
77	 * serialized. See also the getMDC and getMDCCopy methods.
78	 * @var boolean
79	 */
80	private $mdcCopyLookupRequired = true;
81
82	/**
83	 * @var mixed The application supplied message of logging event.
84	 */
85	private $message;
86
87	/**
88	 * The application supplied message rendered through the log4php
89	 * objet rendering mechanism. At present renderedMessage == message.
90	 * @var string
91	 */
92	private $renderedMessage = null;
93
94	/**
95	 * The name of thread in which this logging event was generated.
96	 * log4php saves here the process id via {@link PHP_MANUAL#getmypid getmypid()}
97	 * @var mixed
98	 */
99	private $threadName = null;
100
101	/**
102	* The number of seconds elapsed from 1/1/1970 until logging event
103	* was created plus microseconds if available.
104	* @var float
105	*/
106	public $timeStamp;
107
108	/**
109	* @var LoggerLocationInfo Location information for the caller.
110	*/
111	private $locationInfo = null;
112
113	/**
114	* Instantiate a LoggingEvent from the supplied parameters.
115	*
116	* <p>Except {@link $timeStamp} all the other fields of
117	* LoggerLoggingEvent are filled when actually needed.
118	*
119	* @param string $fqcn name of the caller class.
120	* @param mixed $logger The {@link Logger} category of this event or the logger name.
121	* @param LoggerLevel $priority The level of this event.
122	* @param mixed $message The message of this event.
123	* @param integer $timeStamp the timestamp of this logging event.
124	*/
125	public function __construct($fqcn, $logger, $priority, $message, $timeStamp = null) {
126		$this->fqcn = $fqcn;
127		if($logger instanceof Logger) {
128			$this->logger = $logger;
129			$this->categoryName = $logger->getName();
130		} else {
131			$this->categoryName = strval($logger);
132		}
133		$this->level = $priority;
134		$this->message = $message;
135		if($timeStamp !== null && is_float($timeStamp)) {
136			$this->timeStamp = $timeStamp;
137		} else {
138			if(function_exists('microtime')) {
139				// get microtime as float
140				$this->timeStamp = microtime(true);
141			} else {
142				$this->timeStamp = floatval(time());
143			}
144		}
145	}
146
147	/**
148	 * Set the location information for this logging event. The collected
149	 * information is cached for future use.
150	 *
151	 * <p>This method uses {@link PHP_MANUAL#debug_backtrace debug_backtrace()} function (if exists)
152	 * to collect informations about caller.</p>
153	 * <p>It only recognize informations generated by {@link Logger} and its subclasses.</p>
154	 * @return LoggerLocationInfo
155	 */
156	public function getLocationInformation() {
157		if($this->locationInfo === null) {
158
159			$locationInfo = array();
160
161			if(function_exists('debug_backtrace')) {
162				$trace = debug_backtrace();
163				$prevHop = null;
164				// make a downsearch to identify the caller
165				$hop = array_pop($trace);
166				while($hop !== null) {
167					if(isset($hop['class'])) {
168						// we are sometimes in functions = no class available: avoid php warning here
169						$className = strtolower($hop['class']);
170						if(!empty($className) and ($className == 'logger' or $className == 'loggercategory' or
171							strtolower(get_parent_class($className)) == 'logger' or
172							strtolower(get_parent_class($className)) == 'loggercategory')) {
173							$locationInfo['line'] = $hop['line'];
174							$locationInfo['file'] = $hop['file'];
175							break;
176						}
177					}
178					$prevHop = $hop;
179					$hop = array_pop($trace);
180				}
181				$locationInfo['class'] = isset($prevHop['class']) ? $prevHop['class'] : 'main';
182				if(isset($prevHop['function']) and
183					$prevHop['function'] !== 'include' and
184					$prevHop['function'] !== 'include_once' and
185					$prevHop['function'] !== 'require' and
186					$prevHop['function'] !== 'require_once') {
187
188					$locationInfo['function'] = $prevHop['function'];
189				} else {
190					$locationInfo['function'] = 'main';
191				}
192			}
193
194			$this->locationInfo = new LoggerLocationInfo($locationInfo, $this->fqcn);
195		}
196		return $this->locationInfo;
197	}
198
199	/**
200	 * Return the level of this event. Use this form instead of directly
201	 * accessing the {@link $level} field.
202	 * @return LoggerLevel
203	 */
204	public function getLevel() {
205		return $this->level;
206	}
207
208	/**
209	 * Return the name of the logger. Use this form instead of directly
210	 * accessing the {@link $categoryName} field.
211	 * @return string
212	 */
213	public function getLoggerName() {
214		return $this->categoryName;
215	}
216
217	/**
218	 * Return the message for this logging event.
219	 *
220	 * <p>Before serialization, the returned object is the message
221	 * passed by the user to generate the logging event. After
222	 * serialization, the returned value equals the String form of the
223	 * message possibly after object rendering.
224	 * @return mixed
225	 */
226	public function getMessage() {
227		if($this->message !== null) {
228			return $this->message;
229		} else {
230			return $this->getRenderedMessage();
231		}
232	}
233
234	/**
235	 * This method returns the NDC for this event. It will return the
236	 * correct content even if the event was generated in a different
237	 * thread or even on a different machine. The {@link LoggerNDC::get()} method
238	 * should <b>never</b> be called directly.
239	 * @return string
240	 */
241	public function getNDC() {
242		if($this->ndcLookupRequired) {
243			$this->ndcLookupRequired = false;
244			$this->ndc = implode(' ', LoggerNDC::get());
245		}
246		return $this->ndc;
247	}
248
249	/**
250	 * Returns the the context corresponding to the <code>key</code>
251	 * parameter.
252	 * @return string
253	 */
254	public function getMDC($key) {
255		return LoggerMDC::get($key);
256	}
257
258	/**
259	 * Render message.
260	 * @return string
261	 */
262	public function getRenderedMessage() {
263		if($this->renderedMessage === null and $this->message !== null) {
264			if(is_string($this->message)) {
265					$this->renderedMessage = $this->message;
266			} else {
267			    // $this->logger might be null or an instance of Logger or RootLogger
268			    // But in contrast to log4j, in log4php there is only have one LoggerHierarchy so there is
269			    // no need figure out which one is $this->logger part of.
270			    // TODO: Logger::getHierarchy() is marked @deprecated!
271				$repository = Logger::getHierarchy();
272				$rendererMap = $repository->getRendererMap();
273				$this->renderedMessage= $rendererMap->findAndRender($this->message);
274			}
275		}
276		return $this->renderedMessage;
277	}
278
279	/**
280	 * Returns the time when the application started, in seconds
281	 * elapsed since 01.01.1970 plus microseconds if available.
282	 *
283	 * @return float
284	 * @static
285	 */
286	public static function getStartTime() {
287		if(!isset(self::$startTime)) {
288			if (function_exists('microtime')) {
289				// microtime as float
290				self::$startTime = microtime(true);
291			} else {
292				self::$startTime = floatval(time());
293			}
294		}
295		return self::$startTime;
296	}
297
298	/**
299	 * @return float
300	 */
301	public function getTimeStamp() {
302		return $this->timeStamp;
303	}
304
305	/**
306	 * Calculates the time of this event.
307	 * @return the time after event starttime when this event has occured
308	 */
309	public function getTime() {
310        $eventTime = (float)$this->getTimeStamp();
311        $eventStartTime = (float)LoggerLoggingEvent::getStartTime();
312        return number_format(($eventTime - $eventStartTime) * 1000, 0, '', '');
313    }
314
315	/**
316	 * @return mixed
317	 */
318	public function getThreadName() {
319		if ($this->threadName === null) {
320			$this->threadName = (string)getmypid();
321		}
322		return $this->threadName;
323	}
324
325	/**
326	 * @return mixed null
327	 */
328	public function getThrowableInformation() {
329		return null;
330	}
331
332	/**
333	 * Serialize this object
334	 * @return string
335	 */
336	public function toString() {
337		serialize($this);
338	}
339
340	/**
341	 * Avoid serialization of the {@link $logger} object
342	 */
343	public function __sleep() {
344		return array(
345			'fqcn',
346			'categoryName',
347			'level',
348			'ndc',
349			'ndcLookupRequired',
350			'message',
351			'renderedMessage',
352			'threadName',
353			'timeStamp',
354			'locationInfo',
355		);
356	}
357
358}
359
360LoggerLoggingEvent::getStartTime();
361