comparison tango/example/logging/context.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children
comparison
equal deleted inserted replaced
131:5825d48b27d1 132:1700239cab2e
1 /*******************************************************************************
2
3 copyright: Copyright (c) 2007 Stonecobra. All rights reserved
4
5 license: BSD style: $(LICENSE)
6
7 version: Initial release: December 2007
8
9 author: stonecobra
10
11 *******************************************************************************/
12
13 module context;
14
15 import tango.core.Thread,
16 tango.util.log.Log,
17 tango.util.log.Event,
18 tango.util.log.EventLayout,
19 tango.util.log.ConsoleAppender;
20
21 import tango.util.log.model.IHierarchy;
22
23 /*******************************************************************************
24
25 Allows the dynamic setting of log levels on a per-thread basis.
26 Imagine that a user request comes into your production threaded
27 server. You can't afford to turn logging up to trace for the sake
28 of debugging this one users problem, but you also can't afford to
29 find the problem and fix it. So now you just set the override log
30 level to TRACE for the thread the user is on, and you get full trace
31 output for only that user.
32
33 *******************************************************************************/
34
35 class ThreadLocalDiagnosticContext : IHierarchy.Context
36 {
37 private ThreadLocal!(DCData) dcData;
38 private char[128] tmp;
39
40 /***********************************************************************
41
42 ***********************************************************************/
43
44 public this()
45 {
46 dcData = new ThreadLocal!(DCData);
47 }
48
49 /***********************************************************************
50
51 set the 'diagnostic' Level for logging. This overrides
52 the Level in the current Logger. The default level starts
53 at NONE, so as not to modify the behavior of existing clients
54 of tango.util.log
55
56 ***********************************************************************/
57
58 void setLevel (ILevel.Level level)
59 {
60 auto data = dcData.val;
61 data.level = level;
62 dcData.val = data;
63 }
64
65 /***********************************************************************
66
67 All log appends will be checked against this to see if a
68 log level needs to be temporarily adjusted.
69
70 ***********************************************************************/
71
72 bool isEnabled (ILevel.Level setting, ILevel.Level level = ILevel.Level.Trace)
73 {
74 return level >= setting || level >= dcData.val.level;
75 }
76
77 /***********************************************************************
78
79 Return the label to use for the current log message. Usually
80 called by the Layout. This implementation returns "{}".
81
82 ***********************************************************************/
83
84 char[] label ()
85 {
86 return dcData.val.getLabel;
87 }
88
89 /***********************************************************************
90
91 Push another string into the 'stack'. This strings will be
92 appened together when getLabel is called.
93
94 ***********************************************************************/
95
96 void push (char[] label)
97 {
98 auto data = dcData.val;
99 data.push(label);
100 dcData.val = data;
101 }
102
103 /***********************************************************************
104
105 pop the current label off the stack.
106
107 ***********************************************************************/
108
109 void pop ()
110 {
111 auto data = dcData.val;
112 data.pop;
113 dcData.val = data;
114 }
115
116 /***********************************************************************
117
118 Clear the label stack.
119
120 ***********************************************************************/
121
122 void clear()
123 {
124 auto data = dcData.val;
125 data.clear;
126 dcData.val = data;
127 }
128 }
129
130
131 /*******************************************************************************
132
133 The thread locally stored struct to hold the logging level and
134 the label stack.
135
136 *******************************************************************************/
137
138 private struct DCData {
139
140 ILevel.Level level = ILevel.Level.None;
141 char[][8] stack;
142 bool shouldUpdate = true;
143 int stackIndex = 0;
144 uint labelLength;
145 char[256] labelContent;
146
147
148 char[] getLabel() {
149 if (shouldUpdate) {
150 labelLength = 0;
151 append(" {");
152 for (int i = 0; i < stackIndex; i++) {
153 append(stack[i]);
154 if (i < stackIndex - 1) {
155 append(" ");
156 }
157 }
158 append("}");
159 shouldUpdate = false;
160 }
161 return labelContent[0..labelLength];
162 }
163
164 void append(char[] x) {
165 uint addition = x.length;
166 uint newLength = labelLength + x.length;
167
168 if (newLength < labelContent.length)
169 {
170 labelContent [labelLength..newLength] = x[0..addition];
171 labelLength = newLength;
172 }
173 }
174
175 void push(char[] label) {
176 shouldUpdate = true;
177 stack[stackIndex] = label.dup;
178 stackIndex++;
179 }
180
181 void pop() {
182 shouldUpdate = true;
183 if (stackIndex > 0) {
184 stack[stackIndex] = null;
185 stackIndex--;
186 }
187 }
188
189 void clear() {
190 shouldUpdate = true;
191 for (int i = 0; i < stack.length; i++) {
192 stack[i] = null;
193 }
194 }
195 }
196
197
198 /*******************************************************************************
199
200 Simple console appender that counts the number of log lines it
201 has written.
202
203 *******************************************************************************/
204
205 class TestingConsoleAppender : ConsoleAppender {
206
207 int events = 0;
208
209 this (EventLayout layout = null)
210 {
211 super(layout);
212 }
213
214 override void append (Event event)
215 {
216 events++;
217 super.append(event);
218 }
219 }
220
221
222 /*******************************************************************************
223
224 Testing harness for the DiagnosticContext functionality.
225
226 *******************************************************************************/
227
228 void main(char[][] args)
229 {
230 //set up our appender that counts the log output. This is the configuration
231 //equivalent of importing tango.util.log.Configurator.
232 auto appender = new TestingConsoleAppender(new SimpleTimerLayout);
233 Log.getRootLogger.addAppender(appender);
234
235 char[128] tmp = 0;
236 auto log = Log.getLogger("context");
237 log.setLevel(log.Level.Info);
238
239 //first test, use all defaults, validating it is working. None of the trace()
240 //calls should count in the test.
241 for (int i=0;i < 10; i++) {
242 log.info(log.format(tmp, "test1 {}", i));
243 log.trace(log.format(tmp, "test1 {}", i));
244 }
245 if (appender.events !is 10) {
246 log.error(log.format(tmp, "events:{}", appender.events));
247 throw new Exception("Incorrect Number of events in normal mode");
248 }
249
250 appender.events = 0;
251
252 //test the thread local implementation without any threads, as a baseline.
253 //should be same result as test1
254 auto context = new ThreadLocalDiagnosticContext;
255 Log.getHierarchy.context(context);
256 for (int i=0;i < 10; i++) {
257 log.info(log.format(tmp, "test2 {}", i));
258 log.trace(log.format(tmp, "test2 {}", i));
259 }
260 if (appender.events !is 10) {
261 log.error(log.format(tmp, "events:{}", appender.events));
262 throw new Exception("Incorrect Number of events in TLS single thread mode");
263 }
264
265 appender.events = 0;
266
267 //test the thread local implementation without any threads, as a baseline.
268 //This should count all logging requests, because the DiagnosticContext has
269 //'overridden' the logging level on ALL loggers up to TRACE.
270 context.setLevel(log.Level.Trace);
271 for (int i=0;i < 10; i++) {
272 log.info(log.format(tmp, "test3 {}", i));
273 log.trace(log.format(tmp, "test3 {}", i));
274 }
275 if (appender.events !is 20) {
276 log.error(log.format(tmp, "events:{}", appender.events));
277 throw new Exception("Incorrect Number of events in TLS single thread mode with level set");
278 }
279
280 appender.events = 0;
281
282 //test the thread local implementation without any threads, as a baseline.
283 context.setLevel(log.Level.None);
284 for (int i=0;i < 10; i++) {
285 log.info(log.format(tmp, "test4 {}", i));
286 log.trace(log.format(tmp, "test4 {}", i));
287 }
288 if (appender.events !is 10) {
289 log.error(log.format(tmp, "events:{}", appender.events));
290 throw new Exception("Incorrect Number of events in TLS single thread mode after level reset");
291 }
292
293 //Now test threading. set up a trace context in one thread, with a label, while
294 //keeping the second thread at the normal configuration.
295 appender.events = 0;
296 ThreadGroup tg = new ThreadGroup();
297 tg.create({
298 char[128] tmp = 0;
299 context.setLevel(log.Level.Trace);
300 context.push("specialthread");
301 context.push("2ndlevel");
302 for (int i=0;i < 10; i++) {
303 log.info(log.format(tmp, "test5 {}", i));
304 log.trace(log.format(tmp, "test5 {}", i));
305 }
306 });
307 tg.create({
308 char[128] tmp = 0;
309 context.setLevel(log.Level.None);
310 for (int i=0;i < 10; i++) {
311 log.info(log.format(tmp, "test6 {}", i));
312 log.trace(log.format(tmp, "test6 {}", i));
313 }
314 });
315 tg.joinAll();
316
317 if (appender.events !is 30) {
318 log.error(log.format(tmp, "events:{}", appender.events));
319 throw new Exception("Incorrect Number of events in TLS multi thread mode");
320 }
321 }
322