comparison tango/tango/util/log/Hierarchy.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) 2004 Kris Bell. All rights reserved
4
5 license: BSD style: $(LICENSE)
6
7 version: Oct 2004: Initial release
8 version: Feb 2007: Switched to lazy expr
9
10 author: Kris
11
12 *******************************************************************************/
13
14 module tango.util.log.Hierarchy;
15
16 private import tango.time.Clock;
17
18 private import tango.core.Exception;
19
20 private import tango.util.log.Logger,
21 tango.util.log.Appender;
22
23 private import tango.text.convert.Layout;
24
25 private import tango.util.log.model.IHierarchy;
26
27 /*******************************************************************************
28
29 Pull in additional functions from the C library
30
31 *******************************************************************************/
32
33 extern (C)
34 {
35 int memcmp (void *, void *, int);
36 }
37
38 /*******************************************************************************
39
40 Hack to sidestep linux linker errors (big thanks to Keinfarbton)
41
42 *******************************************************************************/
43
44 private Layout!(char) format;
45
46 static this()
47 {
48 .format = new Layout!(char);
49 }
50
51 /*******************************************************************************
52
53 Loggers are named entities, sometimes shared, sometimes specific to
54 a particular portion of code. The names are generally hierarchical in
55 nature, using dot notation (with '.') to separate each named section.
56 For example, a typical name might be something like "mail.send.writer"
57 ---
58 import tango.util.log.Log;
59
60 auto log = Log.getLogger ("mail.send.writer");
61
62 log.info ("an informational message");
63 log.error ("an exception message: " ~ exception.toString);
64
65 etc ...
66 ---
67
68 It is considered good form to pass a logger instance as a function or
69 class-ctor argument, or to assign a new logger instance during static
70 class construction. For example: if it were considered appropriate to
71 have one logger instance per class, each might be constructed like so:
72 ---
73 private Logger log;
74
75 static this()
76 {
77 log = Log.getLogger (nameOfThisClassOrStructOrModule);
78 }
79 ---
80
81 Messages passed to a Logger are assumed to be pre-formatted. You
82 may find that the format() methos is handy for collating various
83 components of the message:
84 ---
85 char tmp[128] = void;
86 ...
87 log.warn (log.format (tmp, "temperature is {} degrees!", 101));
88 ---
89
90 Note that a provided workspace is used to format the message, which
91 should generally be located on the stack so as to support multiple
92 threads of execution. In the example above we indicate assignment as
93 "tmp = void", although this is an optional attribute (see the language
94 manual for more information).
95
96 To avoid overhead when constructing formatted messages, the logging
97 system employs lazy expressions such that the message is not constructed
98 unless the logger is actually active. You can also explicitly check to
99 see whether a logger is active or not:
100 ---
101 if (log.isEnabled (log.Level.Warn))
102 log.warn (log.format (tmp, "temperature is {} degrees!", 101));
103 ---
104
105 You might optionally configure various layout & appender implementations
106 to support specific rendering needs.
107
108 tango.log closely follows both the API and the behaviour as documented
109 at the official Log4J site, where you'll find a good tutorial. Those
110 pages are hosted over
111 <A HREF="http://logging.apache.org/log4j/docs/documentation.html">here</A>.
112
113 *******************************************************************************/
114
115 private class LoggerInstance : Logger
116 {
117 private LoggerInstance next,
118 parent;
119
120 private char[] name_;
121 private Level level_;
122 private Appender appender;
123 private Hierarchy hierarchy;
124 private bool additive,
125 breakpoint;
126
127
128 /***********************************************************************
129
130 Construct a LoggerInstance with the specified name for the
131 given hierarchy. By default, logger instances are additive
132 and are set to emit all events.
133
134 ***********************************************************************/
135
136 protected this (Hierarchy hierarchy, char[] name)
137 {
138 this.hierarchy = hierarchy;
139 this.level_ = Level.Trace;
140 this.additive = true;
141 this.name_ = name;
142 }
143
144 /***********************************************************************
145
146 No, you should not delete or 'scope' these entites
147
148 ***********************************************************************/
149
150 private ~this()
151 {
152 }
153
154 /***********************************************************************
155
156 Is this logger enabed for the specified Level?
157
158 ***********************************************************************/
159
160 final bool isEnabled (Level level = Level.Fatal)
161 {
162 return hierarchy.context.isEnabled (level_, level);
163 }
164
165 /***********************************************************************
166
167 Is this a breakpoint Logger?
168
169 ***********************************************************************/
170
171 final bool isBreakpoint ()
172 {
173 return breakpoint;
174 }
175
176 /***********************************************************************
177
178 Is this logger additive? That is, should we walk ancestors
179 looking for more appenders?
180
181 ***********************************************************************/
182
183 final bool isAdditive ()
184 {
185 return additive;
186 }
187
188 /***********************************************************************
189
190 Append a trace message
191
192 ***********************************************************************/
193
194 final Logger trace (lazy char[] msg)
195 {
196 return append (Level.Trace, msg);
197 }
198
199 /***********************************************************************
200
201 Append an info message
202
203 ***********************************************************************/
204
205 final Logger info (lazy char[] msg)
206 {
207 return append (Level.Info, msg);
208 }
209
210 /***********************************************************************
211
212 Append a warning message
213
214 ***********************************************************************/
215
216 final Logger warn (lazy char[] msg)
217 {
218 return append (Level.Warn, msg);
219 }
220
221 /***********************************************************************
222
223 Append an error message
224
225 ***********************************************************************/
226
227 final Logger error (lazy char[] msg)
228 {
229 return append (Level.Error, msg);
230 }
231
232 /***********************************************************************
233
234 Append a fatal message
235
236 ***********************************************************************/
237
238 final Logger fatal (lazy char[] msg)
239 {
240 return append (Level.Fatal, msg);
241 }
242
243 /***********************************************************************
244
245 Return the name of this Logger (sans the appended dot).
246
247 ***********************************************************************/
248
249 final char[] name ()
250 {
251 int i = name_.length;
252 if (i > 0)
253 --i;
254 return name_[0 .. i];
255 }
256
257 /***********************************************************************
258
259 Return the Level this logger is set to
260
261 ***********************************************************************/
262
263 final Level level ()
264 {
265 return level_;
266 }
267
268 /***********************************************************************
269
270 Set the current level for this logger (and only this logger).
271
272 ***********************************************************************/
273
274 final Logger setLevel (Level level = Level.Trace)
275 {
276 return setLevel (level, false);
277 }
278
279 /***********************************************************************
280
281 Set the current level for this logger, and (optionally) all
282 of its descendents.
283
284 ***********************************************************************/
285
286 final Logger setLevel (Level level, bool propagate)
287 {
288 this.level_ = level;
289 hierarchy.updateLoggers (this, propagate);
290 return this;
291 }
292
293 /***********************************************************************
294
295 Set the breakpoint status of this logger.
296
297 ***********************************************************************/
298
299 final Logger setBreakpoint (bool enabled)
300 {
301 breakpoint = enabled;
302 hierarchy.updateLoggers (this, false);
303 return this;
304 }
305
306 /***********************************************************************
307
308 Set the additive status of this logger. See isAdditive().
309
310 ***********************************************************************/
311
312 final Logger setAdditive (bool enabled)
313 {
314 additive = enabled;
315 return this;
316 }
317
318 /***********************************************************************
319
320 Add (another) appender to this logger. Appenders are each
321 invoked for log events as they are produced. At most, one
322 instance of each appender will be invoked.
323
324 ***********************************************************************/
325
326 final Logger addAppender (Appender next)
327 {
328 if (appender)
329 next.setNext (appender);
330 appender = next;
331 return this;
332 }
333
334 /***********************************************************************
335
336 Remove all appenders from this Logger
337
338 ***********************************************************************/
339
340 final Logger clearAppenders ()
341 {
342 appender = null;
343 return this;
344 }
345
346
347 /***********************************************************************
348
349 Get time since this application started
350
351 ***********************************************************************/
352
353 final TimeSpan runtime ()
354 {
355 return Clock.now - Event.startedAt;
356 }
357
358 /***********************************************************************
359
360 Append a message to this logger via its appender list.
361
362 ***********************************************************************/
363
364 final Logger append (Level level, lazy char[] exp)
365 {
366 if (hierarchy.context.isEnabled (level_, level))
367 {
368 auto event = Event.allocate;
369 scope (exit)
370 Event.deallocate (event);
371
372 // set the event attributes
373 event.set (hierarchy, level, exp, name.length ? name_[0..$-1] : "root");
374
375 // combine appenders from all ancestors
376 auto links = this;
377 Appender.Mask masks = 0;
378 do {
379 auto appender = links.appender;
380
381 // this level have an appender?
382 while (appender)
383 {
384 auto mask = appender.getMask;
385
386 // have we used this appender already?
387 if ((masks & mask) is 0)
388 {
389 // no - append message and update mask
390 event.scratch.length = 0;
391 appender.append (event);
392 masks |= mask;
393 }
394 // process all appenders for this node
395 appender = appender.getNext;
396 }
397 // process all ancestors
398 } while (links.additive && ((links = links.parent) !is null));
399 }
400 return this;
401 }
402
403 /***********************************************************************
404
405 Format text using the formatter configured in the associated
406 hierarchy (see Hierarchy.setFormat)
407
408 ***********************************************************************/
409
410 final char[] format (char[] buffer, char[] formatStr, ...)
411 {
412 return .format.vprint (buffer, formatStr, _arguments, _argptr);
413 }
414
415 /***********************************************************************
416
417 See if the provided Logger is a good match as a parent of
418 this one. Note that each Logger name has a '.' appended to
419 the end, such that name segments will not partially match.
420
421 ***********************************************************************/
422
423 private final bool isCloserAncestor (LoggerInstance other)
424 {
425 auto length = other.name_.length;
426
427 // possible parent if length is shorter
428 if (length < name_.length)
429 // does the prefix match? Note we append a "." to each
430 if (length is 0 ||
431 memcmp (&other.name_[0], &name_[0], length) is 0)
432 // is this a better (longer) match than prior parent?
433 if ((parent is null) || (length >= parent.name_.length))
434 return true;
435 return false;
436 }
437 }
438
439
440 /*******************************************************************************
441
442 The Logger hierarchy implementation. We keep a reference to each
443 logger in a hash-table for convenient lookup purposes, plus keep
444 each logger linked to the others in an ordered chain. Ordering
445 places shortest names at the head and longest ones at the tail,
446 making the job of identifying ancestors easier in an orderly
447 fashion. For example, when propagating levels across descendents
448 it would be a mistake to propagate to a child before all of its
449 ancestors were taken care of.
450
451 *******************************************************************************/
452
453 class Hierarchy : IHierarchy, IHierarchy.Context
454 {
455 private char[] name,
456 address;
457 private LoggerInstance root;
458 private LoggerInstance[char[]] loggers;
459 private Context context_;
460
461 /***********************************************************************
462
463 Construct a hierarchy with the given name.
464
465 ***********************************************************************/
466
467 this (char[] name)
468 {
469 this.name = name;
470 this.address = "network";
471
472 // insert a root node; the root has an empty name
473 root = new LoggerInstance (this, "");
474 context = this;
475 }
476
477 /**********************************************************************
478
479 **********************************************************************/
480
481 final char[] label ()
482 {
483 return "";
484 }
485
486 /**********************************************************************
487
488
489 **********************************************************************/
490
491 final bool isEnabled (ILevel.Level level, ILevel.Level test)
492 {
493 return test >= level;
494 }
495
496 /**********************************************************************
497
498 Return the name of this Hierarchy
499
500 **********************************************************************/
501
502 final char[] getName ()
503 {
504 return name;
505 }
506
507 /**********************************************************************
508
509 Return the address of this Hierarchy. This is typically
510 attached when sending events to remote monitors.
511
512 **********************************************************************/
513
514 final char[] getAddress ()
515 {
516 return address;
517 }
518
519 /**********************************************************************
520
521 Set the name of this Hierarchy
522
523 **********************************************************************/
524
525 final void setName (char[] name)
526 {
527 this.name = name;
528 }
529
530 /**********************************************************************
531
532 Set the address of this Hierarchy. The address is attached
533 used when sending events to remote monitors.
534
535 **********************************************************************/
536
537 final void setAddress (char[] address)
538 {
539 this.address = address;
540 }
541
542 /**********************************************************************
543
544 Set the diagnostic context. Not usually necessary, as a
545 default was created. Useful when you need to provide a
546 different implementation, such as a ThreadLocal variant.
547
548 **********************************************************************/
549
550 final void context (Context context)
551 {
552 this.context_ = context;
553 }
554
555 /**********************************************************************
556
557 Return the diagnostic context. Useful for setting an
558 override logging level.
559
560 **********************************************************************/
561
562 final Context context ()
563 {
564 return context_;
565 }
566
567 /***********************************************************************
568
569 Return the root node.
570
571 ***********************************************************************/
572
573 final LoggerInstance getRootLogger ()
574 {
575 return root;
576 }
577
578 /***********************************************************************
579
580 Return the instance of a Logger with the provided label. If
581 the instance does not exist, it is created at this time.
582
583 ***********************************************************************/
584
585 final synchronized LoggerInstance getLogger (char[] label)
586 {
587 auto name = label ~ ".";
588 auto l = name in loggers;
589
590 if (l is null)
591 {
592 // create a new logger
593 auto li = new LoggerInstance (this, name);
594 l = &li;
595
596 // insert into linked list
597 insertLogger (li);
598
599 // look for and adjust children
600 updateLoggers (li, true);
601
602 // insert into map
603 loggers [name] = li;
604 }
605
606 return *l;
607 }
608
609 /**********************************************************************
610
611 Iterate over all Loggers in list
612
613 **********************************************************************/
614
615 final int opApply (int delegate(inout Logger) dg)
616 {
617 int result = 0;
618 LoggerInstance curr = root;
619
620 while (curr)
621 {
622 // BUG: this uncovers a cast() issue in the 'inout' delegation
623 Logger logger = curr;
624 if ((result = dg (logger)) != 0)
625 break;
626 curr = curr.next;
627 }
628 return result;
629 }
630
631 /***********************************************************************
632
633 Loggers are maintained in a sorted linked-list. The order
634 is maintained such that the shortest name is at the root,
635 and the longest at the tail.
636
637 This is done so that updateLoggers() will always have a
638 known environment to manipulate, making it much faster.
639
640 ***********************************************************************/
641
642 private void insertLogger (LoggerInstance l)
643 {
644 LoggerInstance prev,
645 curr = root;
646
647 while (curr)
648 {
649 // insert here if the new name is shorter
650 if (l.name.length < curr.name.length)
651 if (prev is null)
652 throw new IllegalElementException ("invalid hierarchy");
653 else
654 {
655 l.next = prev.next;
656 prev.next = l;
657 return;
658 }
659 else
660 // find best match for parent of new entry
661 propagate (l, curr, true);
662
663 // remember where insertion point should be
664 prev = curr;
665 curr = curr.next;
666 }
667
668 // add to tail
669 prev.next = l;
670 }
671
672 /***********************************************************************
673
674 Propagate hierarchical changes across known loggers.
675 This includes changes in the hierarchy itself, and to
676 the various settings of child loggers with respect to
677 their parent(s).
678
679 ***********************************************************************/
680
681 private void updateLoggers (LoggerInstance changed, bool force)
682 {
683 LoggerInstance logger = root;
684
685 // scan all loggers
686 while (logger)
687 {
688 propagate (logger, changed, force);
689
690 // try next entry
691 logger = logger.next;
692 }
693 }
694
695 /***********************************************************************
696
697 Propagate changes in the hierarchy downward to child Loggers.
698 Note that while 'parent' and 'breakpoint' are always forced
699 to update, the update of 'level' is selectable.
700
701 ***********************************************************************/
702
703 private void propagate (LoggerInstance logger, LoggerInstance changed, bool force)
704 {
705 // is the changed instance a better match for our parent?
706 if (logger.isCloserAncestor (changed))
707 {
708 // update parent (might actually be current parent)
709 logger.parent = changed;
710
711 // if we don't have an explicit level set, inherit it
712 if ((logger.level is Logger.Level.None) || force)
713 logger.setLevel (changed.level);
714
715 // always force breakpoints to follow parent settings
716 logger.breakpoint = changed.breakpoint;
717 }
718 }
719 }
720
721