Project

General

Profile

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 NDC class implements <i>nested diagnostic contexts</i>.
23
 * 
24
 * NDC was defined by Neil Harrison in the article "Patterns for Logging
25
 * Diagnostic Messages" part of the book <i>"Pattern Languages of
26
 * Program Design 3"</i> edited by Martin et al.
27
 *
28
 * A Nested Diagnostic Context, or NDC in short, is an instrument
29
 * to distinguish interleaved log output from different sources. Log
30
 * output is typically interleaved when a server handles multiple
31
 * clients near-simultaneously.
32
 *
33
 * This class is similar to the {@link LoggerMDC} class except that it is
34
 * based on a stack instead of a map.
35
 *
36
 * Interleaved log output can still be meaningful if each log entry
37
 * from different contexts had a distinctive stamp. This is where NDCs
38
 * come into play.
39
 *
40
 * <b>Note that NDCs are managed on a per thread basis</b>. 
41
 * 
42
 * NDC operations such as {@link push()}, {@link pop()}, 
43
 * {@link clear()}, {@link getDepth()} and {@link setMaxDepth()}
44
 * affect the NDC of the <i>current</i> thread only. NDCs of other
45
 * threads remain unaffected.
46
 *
47
 * For example, a servlet can build a per client request NDC
48
 * consisting the clients host name and other information contained in
49
 * the the request. <i>Cookies</i> are another source of distinctive
50
 * information. To build an NDC one uses the {@link push()}
51
 * operation.
52
 * 
53
 * Simply put,
54
 *
55
 * - Contexts can be nested.
56
 * - When entering a context, call <kbd>LoggerNDC::push()</kbd>
57
 *	 As a side effect, if there is no nested diagnostic context for the
58
 *	 current thread, this method will create it.
59
 * - When leaving a context, call <kbd>LoggerNDC::pop()</kbd>
60
 * - <b>When exiting a thread make sure to call {@link remove()}</b>
61
 *	 
62
 * There is no penalty for forgetting to match each
63
 * <kbd>push</kbd> operation with a corresponding <kbd>pop</kbd>,
64
 * except the obvious mismatch between the real application context
65
 * and the context set in the NDC.
66
 *
67
 * If configured to do so, {@link LoggerPatternLayout} and {@link LoggerLayoutTTCC} 
68
 * instances automatically retrieve the nested diagnostic
69
 * context for the current thread without any user intervention.
70
 * Hence, even if a servlet is serving multiple clients
71
 * simultaneously, the logs emanating from the same code (belonging to
72
 * the same category) can still be distinguished because each client
73
 * request will have a different NDC tag.
74
 *
75
 * Example:
76
 *	
77
 * {@example ../../examples/php/ndc.php 19}<br>
78
 *
79
 * With the properties file:
80
 * 
81
 * {@example ../../examples/resources/ndc.properties 18}<br>
82
 * 
83
 * Will result in the following (notice the conn and client ids):
84
 * 
85
 * <pre>
86
 * 2009-09-13 19:04:27 DEBUG root conn=1234: just received a new connection in src/examples/php/ndc.php at 23
87
 * 2009-09-13 19:04:27 DEBUG root conn=1234 client=ab23: some more messages that can in src/examples/php/ndc.php at 25
88
 * 2009-09-13 19:04:27 DEBUG root conn=1234 client=ab23: now related to a client in src/examples/php/ndc.php at 26
89
 * 2009-09-13 19:04:27 DEBUG root : back and waiting for new connections in src/examples/php/ndc.php at 29
90
 * </pre>
91
 *	
92
 * @version $Revision: 1166187 $
93
 * @package log4php 
94
 * @since 0.3
95
 */
96
class LoggerNDC {
97
	
98
	/** This is the repository of NDC stack */
99
	private static $stack = array();
100
	
101
	/**
102
	 * Clear any nested diagnostic information if any. This method is
103
	 * useful in cases where the same thread can be potentially used
104
	 * over and over in different unrelated contexts.
105
	 *
106
	 * <p>This method is equivalent to calling the {@link setMaxDepth()}
107
	 * method with a zero <var>maxDepth</var> argument.
108
	 */
109
	public static function clear() {
110
		self::$stack = array();
111
	}
112

    
113
	/**
114
	 * Never use this method directly, use the {@link LoggerLoggingEvent::getNDC()} method instead.
115
	 * @return array
116
	 */
117
	public static function get() {
118
		return implode(' ', self::$stack);
119
	}
120
  
121
	/**
122
	 * Get the current nesting depth of this diagnostic context.
123
	 *
124
	 * @see setMaxDepth()
125
	 * @return integer
126
	 */
127
	public static function getDepth() {
128
		return count(self::$stack);
129
	}
130

    
131
	/**
132
	 * Clients should call this method before leaving a diagnostic
133
	 * context.
134
	 *
135
	 * <p>The returned value is the value that was pushed last. If no
136
	 * context is available, then the empty string "" is returned.</p>
137
	 *
138
	 * @return string The innermost diagnostic context.
139
	 */
140
	public static function pop() {
141
		if(count(self::$stack) > 0) {
142
			return array_pop(self::$stack);
143
		} else {
144
			return '';
145
		}
146
	}
147

    
148
	/**
149
	 * Looks at the last diagnostic context at the top of this NDC
150
	 * without removing it.
151
	 *
152
	 * <p>The returned value is the value that was pushed last. If no
153
	 * context is available, then the empty string "" is returned.</p>
154
	 * @return string The innermost diagnostic context.
155
	 */
156
	public static function peek(){
157
		if(count(self::$stack) > 0) {
158
			return end(self::$stack);
159
		} else {
160
			return '';
161
		}
162
	}
163
  
164
	/**
165
	 * Push new diagnostic context information for the current thread.
166
	 *
167
	 * <p>The contents of the <var>message</var> parameter is
168
	 * determined solely by the client.
169
	 *	
170
	 * @param string $message The new diagnostic context information.
171
	 */
172
	public static function push($message) {
173
		array_push(self::$stack, (string)$message);
174
	}
175

    
176
	/**
177
	 * Remove the diagnostic context for this thread.
178
	 */
179
	public static function remove() {
180
		LoggerNDC::clear();
181
	}
182

    
183
	/**
184
	 * Set maximum depth of this diagnostic context. If the current
185
	 * depth is smaller or equal to <var>maxDepth</var>, then no
186
	 * action is taken.
187
	 *
188
	 * <p>This method is a convenient alternative to multiple 
189
	 * {@link pop()} calls. Moreover, it is often the case that at 
190
	 * the end of complex call sequences, the depth of the NDC is
191
	 * unpredictable. The {@link setMaxDepth()} method circumvents
192
	 * this problem.
193
	 *
194
	 * @param integer $maxDepth
195
	 * @see getDepth()
196
	 */
197
	public static function setMaxDepth($maxDepth) {
198
		$maxDepth = (int)$maxDepth;
199
		if(LoggerNDC::getDepth() > $maxDepth) {
200
			self::$stack = array_slice(self::$stack, 0, $maxDepth);
201
		}
202
	}
203
}
(15-15/19)