1 /******************************************************************************* 2 3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: May 2004 : Initial release 8 version: Oct 2004: Hierarchy moved due to circular dependencies 9 version: Apr 2008: Lazy delegates removed due to awkward usage 10 author: Kris 11 12 13 Simplified, pedestrian usage: 14 --- 15 import tango.util.log.Config; 16 17 Log ("hello world"); 18 Log ("temperature is {} degrees", 75); 19 --- 20 21 Generic usage: 22 23 Loggers are named entities, sometimes shared, sometimes specific to 24 a particular portion of code. The names are generally hierarchical in 25 nature, using dot notation (with '.') to separate each named section. 26 For example, a typical name might be something like "mail.send.writer" 27 --- 28 import tango.util.log.Log; 29 30 auto log = Log.lookup ("mail.send.writer"); 31 32 log.info ("an informational message"); 33 log.error ("an exception message: {}", exception); 34 35 etc ... 36 --- 37 38 It is considered good form to pass a logger instance as a function or 39 class-ctor argument, or to assign a new logger instance during static 40 class construction. For example: if it were considered appropriate to 41 have one logger instance per class, each might be constructed like so: 42 --- 43 private Logger log; 44 45 static this() 46 { 47 log = Log.lookup (nameOfThisClassOrStructOrModule); 48 } 49 --- 50 51 Messages passed to a Logger are assumed to be either self-contained 52 or configured with "{}" notation a la Layout & Stdout: 53 --- 54 log.warn ("temperature is {} degrees!", 101); 55 --- 56 57 Note that an internal workspace is used to format the message, which 58 is limited to 2000 bytes. Use "{.256}" truncation notation to limit 59 the size of individual message components, or use explicit formatting: 60 --- 61 char[4096] buf = void; 62 63 log.warn (log.format (buf, "a very long message: {}", someLongMessage)); 64 --- 65 66 To avoid overhead when constructing arguments passed to formatted 67 messages, you should check to see whether a logger is active or not: 68 --- 69 if (log.warn) 70 log.warn ("temperature is {} degrees!", complexFunction()); 71 --- 72 73 tango.log closely follows both the API and the behaviour as documented 74 at the official Log4J site, where you'll find a good tutorial. Those 75 pages are hosted over 76 <A HREF="http://logging.apache.org/log4j/docs/documentation.html">here</A>. 77 78 *******************************************************************************/ 79 80 module tango.util.log.Log; 81 82 private import tango.sys.Common; 83 84 private import tango.time.Clock; 85 86 private import tango.core.Exception; 87 88 private import tango.io.model.IConduit; 89 90 private import tango.text.convert.Format; 91 92 private import tango.util.log.model.ILogger; 93 94 /******************************************************************************* 95 96 Platform issues ... 97 98 *******************************************************************************/ 99 100 version (GNU) 101 { 102 private import tango.core.Vararg; 103 alias void* Arg; 104 alias va_list ArgList; 105 } 106 else version(LDC) 107 { 108 private import tango.core.Vararg; 109 alias void* Arg; 110 alias va_list ArgList; 111 } 112 else version (DigitalMars) 113 { 114 private import tango.core.Vararg; 115 alias void* Arg; 116 alias va_list ArgList; 117 118 version(X86_64) version = DigitalMarsX64; 119 120 } 121 else 122 { 123 alias void* Arg; 124 alias void* ArgList; 125 } 126 127 /******************************************************************************* 128 129 Pull in additional functions from the C library 130 131 *******************************************************************************/ 132 133 extern (C) 134 { 135 private int memcmp (const(void) *, const(void) *, size_t); 136 } 137 138 version (Win32) 139 { 140 private extern(Windows) int QueryPerformanceCounter(ulong *count); 141 private extern(Windows) int QueryPerformanceFrequency(ulong *frequency); 142 } 143 144 /******************************************************************************* 145 146 These represent the standard LOG4J event levels. Note that 147 Debug is called Trace here, because debug is a reserved word 148 in D 149 150 *******************************************************************************/ 151 152 alias ILogger.Level Level; 153 154 155 /******************************************************************************* 156 157 Manager for routing Logger calls to the default hierarchy. Note 158 that you may have multiple hierarchies per application, but must 159 access the hierarchy directly for root() and lookup() methods within 160 each additional instance. 161 162 *******************************************************************************/ 163 164 public struct Log 165 { 166 // support for old API 167 public alias lookup getLogger; 168 169 // trivial usage via opCall 170 public alias formatln opCall; 171 172 // internal use only 173 private static __gshared Hierarchy base; 174 private static __gshared Time beginTime; 175 176 version (Win32) 177 { 178 private static __gshared double multiplier; 179 private static __gshared ulong timerStart; 180 } 181 182 private struct Pair {const(char)[] name; Level value;} 183 184 private static __gshared Level [immutable(char)[]] map; 185 186 private static __gshared Pair[] Pairs = 187 [ 188 {"TRACE", Level.Trace}, 189 {"Trace", Level.Trace}, 190 {"trace", Level.Trace}, 191 {"INFO", Level.Info}, 192 {"Info", Level.Info}, 193 {"info", Level.Info}, 194 {"WARN", Level.Warn}, 195 {"Warn", Level.Warn}, 196 {"warn", Level.Warn}, 197 {"ERROR", Level.Error}, 198 {"Error", Level.Error}, 199 {"error", Level.Error}, 200 {"Fatal", Level.Fatal}, 201 {"FATAL", Level.Fatal}, 202 {"fatal", Level.Fatal}, 203 {"NONE", Level.None}, 204 {"None", Level.None}, 205 {"none", Level.None}, 206 ]; 207 208 // logging-level names 209 private __gshared immutable char[][] LevelNames = 210 [ 211 "Trace", "Info", "Warn", "Error", "Fatal", "None" 212 ]; 213 214 /*********************************************************************** 215 216 Initialize the base hierarchy 217 218 ***********************************************************************/ 219 220 shared static this () 221 { 222 base = new Hierarchy ("tango"); 223 224 foreach (p; Pairs) 225 map[p.name] = p.value; 226 227 version (Posix) 228 { 229 beginTime = Clock.now; 230 } 231 232 version (Win32) 233 { 234 ulong freq; 235 236 if (! QueryPerformanceFrequency (&freq)) 237 throw new PlatformException ("high-resolution timer is not available"); 238 239 QueryPerformanceCounter (&timerStart); 240 multiplier = cast(double) TimeSpan.TicksPerSecond / freq; 241 beginTime = Clock.now; 242 } 243 } 244 245 /*********************************************************************** 246 247 Return the level of a given name 248 249 ***********************************************************************/ 250 251 static Level convert (const(char[]) name, Level def=Level.Trace) 252 { 253 auto p = name in map; 254 if (p) 255 return *p; 256 return def; 257 } 258 259 /*********************************************************************** 260 261 Return the current time 262 263 ***********************************************************************/ 264 265 @property static Time time () 266 { 267 version (Posix) 268 { 269 return Clock.now; 270 } 271 272 version (Win32) 273 { 274 ulong now; 275 276 QueryPerformanceCounter (&now); 277 return beginTime + TimeSpan(cast(long)((now - timerStart) * multiplier)); 278 } 279 } 280 281 /*********************************************************************** 282 283 Return the root Logger instance. This is the ancestor of 284 all loggers and, as such, can be used to manipulate the 285 entire hierarchy. For instance, setting the root 'level' 286 attribute will affect all other loggers in the tree. 287 288 ***********************************************************************/ 289 290 @property static Logger root () 291 { 292 return base.root; 293 } 294 295 /*********************************************************************** 296 297 Return an instance of the named logger. Names should be 298 hierarchical in nature, using dot notation (with '.') to 299 separate each name section. For example, a typical name 300 might be something like "tango.io.Stdout". 301 302 If the logger does not currently exist, it is created and 303 inserted into the hierarchy. A parent will be attached to 304 it, which will be either the root logger or the closest 305 ancestor in terms of the hierarchical name space. 306 307 ***********************************************************************/ 308 309 static Logger lookup (const(char[]) name) 310 { 311 return base.lookup (name); 312 } 313 314 /*********************************************************************** 315 316 Return text name for a log level 317 318 ***********************************************************************/ 319 320 static const(char)[] convert (int level) 321 { 322 assert (level >= Level.Trace && level <= Level.None); 323 return LevelNames[level]; 324 } 325 326 /*********************************************************************** 327 328 Return the singleton hierarchy. 329 330 ***********************************************************************/ 331 332 static Hierarchy hierarchy () 333 { 334 return base; 335 } 336 337 /*********************************************************************** 338 339 Pedestrian usage support, as an alias for Log.root.info() 340 341 ***********************************************************************/ 342 343 static void formatln (const(char[]) fmt, ...) 344 { 345 root.format (Level.Info, fmt, _arguments, _argptr); 346 } 347 348 /*********************************************************************** 349 350 Initialize the behaviour of a basic logging hierarchy. 351 352 Adds a StreamAppender to the root node, and sets 353 the activity level to be everything enabled. 354 355 ***********************************************************************/ 356 357 static void config (OutputStream stream, bool flush = true) 358 { 359 root.add (new AppendStream (stream, flush)); 360 } 361 } 362 363 364 /******************************************************************************* 365 366 Loggers are named entities, sometimes shared, sometimes specific to 367 a particular portion of code. The names are generally hierarchical in 368 nature, using dot notation (with '.') to separate each named section. 369 For example, a typical name might be something like "mail.send.writer" 370 --- 371 import tango.util.log.Log;format 372 373 auto log = Log.lookup ("mail.send.writer"); 374 375 log.info ("an informational message"); 376 log.error ("an exception message: {}", exception.toString); 377 378 etc ... 379 --- 380 381 It is considered good form to pass a logger instance as a function or 382 class-ctor argument, or to assign a new logger instance during static 383 class construction. For example: if it were considered appropriate to 384 have one logger instance per class, each might be constructed like so: 385 --- 386 private Logger log; 387 388 static this() 389 { 390 log = Log.lookup (nameOfThisClassOrStructOrModule); 391 } 392 --- 393 394 Messages passed to a Logger are assumed to be either self-contained 395 or configured with "{}" notation a la Layout & Stdout: 396 --- 397 log.warn ("temperature is {} degrees!", 101); 398 --- 399 400 Note that an internal workspace is used to format the message, which 401 is limited to 2048 bytes. Use "{.256}" truncation notation to limit 402 the size of individual message components. You can also use your own 403 formatting buffer: 404 --- 405 log.buffer (new char[](4096)); 406 407 log.warn ("a very long warning: {}", someLongWarning); 408 --- 409 410 Or you can use explicit formatting: 411 --- 412 char[4096] buf = void; 413 414 log.warn (log.format (buf, "a very long warning: {}", someLongWarning)); 415 --- 416 417 To avoid overhead when constructing argument passed to formatted 418 messages, you should check to see whether a logger is active or not: 419 --- 420 if (log.enabled (log.Warn)) 421 log.warn ("temperature is {} degrees!", complexFunction()); 422 --- 423 424 The above will be handled implicitly by the logging system when 425 macros are added to the language (used to be handled implicitly 426 via lazy delegates, but usage of those turned out to be awkward). 427 428 tango.log closely follows both the API and the behaviour as documented 429 at the official Log4J site, where you'll find a good tutorial. Those 430 pages are hosted over 431 <A HREF="http://logging.apache.org/log4j/docs/documentation.html">here</A>. 432 433 *******************************************************************************/ 434 435 public class Logger : ILogger 436 { 437 438 alias Level.Trace Trace; // shortcut to Level values 439 alias Level.Info Info; // ... 440 alias Level.Warn Warn; // ... 441 alias Level.Error Error; // ... 442 alias Level.Fatal Fatal; // ... 443 444 alias append opCall; // shortcut to append 445 446 /*********************************************************************** 447 448 Context for a hierarchy, used for customizing behaviour 449 of log hierarchies. You can use this to implement dynamic 450 log-levels, based upon filtering or some other mechanism 451 452 ***********************************************************************/ 453 454 interface Context 455 { 456 /// return a label for this context 457 @property const const(char)[] label (); 458 459 /// first arg is the setting of the logger itself, and 460 /// the second arg is what kind of message we're being 461 /// asked to produce 462 const bool enabled (Level setting, Level target); 463 } 464 465 /*********************************************************************** 466 467 ***********************************************************************/ 468 469 private Logger next, 470 parent; 471 472 private Hierarchy host_; 473 private const(char)[] name_; 474 private Level level_; 475 private bool additive_; 476 private Appender appender_; 477 private char[] buffer_; 478 private size_t buffer_size_; 479 480 /*********************************************************************** 481 482 Construct a LoggerInstance with the specified name for the 483 given hierarchy. By default, logger instances are additive 484 and are set to emit all events. 485 486 ***********************************************************************/ 487 488 private this (Hierarchy host, const(char[]) name) 489 { 490 host_ = host; 491 level_ = Level.Trace; 492 additive_ = true; 493 name_ = name; 494 } 495 496 /*********************************************************************** 497 498 Is this logger enabed for the specified Level? 499 500 ***********************************************************************/ 501 502 final bool enabled (Level level = Level.Fatal) 503 { 504 return host_.context.enabled (level_, level); 505 } 506 507 /*********************************************************************** 508 509 Is trace enabled? 510 511 ***********************************************************************/ 512 513 final bool trace () 514 { 515 return enabled (Level.Trace); 516 } 517 518 /*********************************************************************** 519 520 Append a trace message 521 522 ***********************************************************************/ 523 524 final void trace (const(char[]) fmt, ...) 525 { 526 version (DigitalMarsX64) 527 { 528 va_list ap; 529 530 va_start(ap, fmt); 531 532 scope(exit) va_end(ap); 533 534 format (Level.Trace, fmt, _arguments, ap); 535 } 536 else 537 format (Level.Trace, fmt, _arguments, _argptr); 538 } 539 540 /*********************************************************************** 541 542 Is info enabled? 543 544 ***********************************************************************/ 545 546 final bool info () 547 { 548 return enabled (Level.Info); 549 } 550 551 /*********************************************************************** 552 553 Append an info message 554 555 ***********************************************************************/ 556 557 final void info (const(char[]) fmt, ...) 558 { 559 version (DigitalMarsX64) 560 { 561 va_list ap; 562 563 va_start(ap, fmt); 564 565 scope(exit) va_end(ap); 566 567 format (Level.Info, fmt, _arguments, ap); 568 } 569 else 570 format (Level.Info, fmt, _arguments, _argptr); 571 } 572 573 /*********************************************************************** 574 575 Is warn enabled? 576 577 ***********************************************************************/ 578 579 final bool warn () 580 { 581 return enabled (Level.Warn); 582 } 583 584 /*********************************************************************** 585 586 Append a warning message 587 588 ***********************************************************************/ 589 590 final void warn (const(char[]) fmt, ...) 591 { 592 version (DigitalMarsX64) 593 { 594 va_list ap; 595 596 va_start(ap, fmt); 597 598 scope(exit) va_end(ap); 599 600 format (Level.Warn, fmt, _arguments, ap); 601 } 602 else 603 format (Level.Warn, fmt, _arguments, _argptr); 604 } 605 606 /*********************************************************************** 607 608 Is error enabled? 609 610 ***********************************************************************/ 611 612 final bool error () 613 { 614 return enabled (Level.Error); 615 } 616 617 /*********************************************************************** 618 619 Append an error message 620 621 ***********************************************************************/ 622 623 final void error (const(char[]) fmt, ...) 624 { 625 version (DigitalMarsX64) 626 { 627 va_list ap; 628 629 va_start(ap, fmt); 630 631 scope(exit) va_end(ap); 632 633 format (Level.Error, fmt, _arguments, ap); 634 } 635 else 636 format (Level.Error, fmt, _arguments, _argptr); 637 } 638 639 /*********************************************************************** 640 641 Is fatal enabled? 642 643 ***********************************************************************/ 644 645 final bool fatal () 646 { 647 return enabled (Level.Fatal); 648 } 649 650 /*********************************************************************** 651 652 Append a fatal message 653 654 ***********************************************************************/ 655 656 final void fatal (const(char[]) fmt, ...) 657 { 658 version (DigitalMarsX64) 659 { 660 va_list ap; 661 662 va_start(ap, fmt); 663 664 scope(exit) va_end(ap); 665 666 format (Level.Fatal, fmt, _arguments, ap); 667 } 668 else 669 format (Level.Fatal, fmt, _arguments, _argptr); 670 } 671 672 /*********************************************************************** 673 674 Return the name of this Logger (sans the appended dot). 675 676 ***********************************************************************/ 677 678 @property final const(char)[] name () 679 { 680 size_t i = name_.length; 681 if (i > 0) 682 --i; 683 return name_[0 .. i]; 684 } 685 686 /*********************************************************************** 687 688 Return the Level this logger is set to 689 690 ***********************************************************************/ 691 692 final Level level () 693 { 694 return level_; 695 } 696 697 /*********************************************************************** 698 699 Set the current level for this logger (and only this logger). 700 701 ***********************************************************************/ 702 703 final Logger level (Level l) 704 { 705 return level (l, false); 706 } 707 708 /*********************************************************************** 709 710 Set the current level for this logger, and (optionally) all 711 of its descendents. 712 713 ***********************************************************************/ 714 715 final Logger level (Level level, bool propagate) 716 { 717 level_ = level; 718 if (propagate) 719 foreach (log; host_) 720 if (log.isChildOf (name_)) 721 log.level_ = level; 722 return this; 723 } 724 725 /*********************************************************************** 726 727 Is this logger additive? That is, should we walk ancestors 728 looking for more appenders? 729 730 ***********************************************************************/ 731 732 @property final const bool additive () 733 { 734 return additive_; 735 } 736 737 /*********************************************************************** 738 739 Set the additive status of this logger. See bool additive(). 740 741 ***********************************************************************/ 742 743 @property final Logger additive (bool enabled) 744 { 745 additive_ = enabled; 746 return this; 747 } 748 749 /*********************************************************************** 750 751 Add (another) appender to this logger. Appenders are each 752 invoked for log events as they are produced. At most, one 753 instance of each appender will be invoked. 754 755 ***********************************************************************/ 756 757 final Logger add (Appender another) 758 { 759 assert (another); 760 another.next = appender_; 761 appender_ = another; 762 return this; 763 } 764 765 /*********************************************************************** 766 767 Remove all appenders from this Logger 768 769 ***********************************************************************/ 770 771 final Logger clear () 772 { 773 appender_ = null; 774 return this; 775 } 776 777 /*********************************************************************** 778 779 Get the current formatting buffer (null if none). 780 781 ***********************************************************************/ 782 783 @property final char[] buffer () 784 { 785 return buffer_; 786 } 787 788 /*********************************************************************** 789 790 Set the current formatting buffer. 791 792 Set to null to use the default internal buffer. 793 794 ***********************************************************************/ 795 796 @property final Logger buffer (char[] buf) 797 { 798 buffer_ = buf; 799 buffer_size_ = buf.length; 800 return this; 801 } 802 803 /*********************************************************************** 804 805 Get time since this application started 806 807 ***********************************************************************/ 808 809 @property final const TimeSpan runtime () 810 { 811 return Clock.now - Log.beginTime; 812 } 813 814 /*********************************************************************** 815 816 Send a message to this logger via its appender list. 817 818 ***********************************************************************/ 819 820 final Logger append (Level level, lazy const(char[]) exp) 821 { 822 if (host_.context.enabled (level_, level)) 823 { 824 LogEvent event; 825 826 // set the event attributes and append it 827 event.set (host_, level, exp, name.length ? name_[0..$-1] : "root"); 828 append (event); 829 } 830 return this; 831 } 832 833 /*********************************************************************** 834 835 Send a message to this logger via its appender list. 836 837 ***********************************************************************/ 838 839 private void append (LogEvent event) 840 { 841 // combine appenders from all ancestors 842 auto links = this; 843 Appender.Mask masks = 0; 844 do { 845 auto appender = links.appender_; 846 847 // this level have an appender? 848 while (appender) 849 { 850 auto mask = appender.mask; 851 852 // have we visited this appender already? 853 if ((masks & mask) is 0) 854 // is appender enabled for this level? 855 if (appender.level <= event.level) 856 { 857 // append message and update mask 858 appender.append (event); 859 masks |= mask; 860 } 861 // process all appenders for this node 862 appender = appender.next; 863 } 864 // process all ancestors 865 } while (links.additive_ && ((links = links.parent) !is null)); 866 } 867 868 /*********************************************************************** 869 870 Return a formatted string from the given arguments 871 872 ***********************************************************************/ 873 874 final char[] format (char[] buffer, const(char[]) formatStr, ...) 875 { 876 version (DigitalMarsX64) 877 { 878 va_list ap; 879 880 va_start(ap, formatStr); 881 882 scope(exit) va_end(ap); 883 884 return Format.vprint (buffer, formatStr, _arguments, ap); 885 } 886 else 887 return Format.vprint (buffer, formatStr, _arguments, _argptr); 888 889 } 890 891 /*********************************************************************** 892 893 Format and emit text from the given arguments 894 895 ***********************************************************************/ 896 897 final Logger format (Level level, const(char[]) fmt, TypeInfo[] types, ArgList args) 898 { 899 if (types.length) 900 { 901 if (buffer_ is null) 902 formatWithDefaultBuffer(level, fmt, types, args); 903 else 904 formatWithProvidedBuffer(level, fmt, types, args); 905 } 906 else 907 append (level, fmt); 908 return this; 909 } 910 911 private void formatWithDefaultBuffer(Level level, const(char)[] fmt, TypeInfo[] types, ArgList args) 912 { 913 char[2048] tmp = void; 914 formatWithBuffer(level, fmt, types, args, tmp); 915 } 916 917 private void formatWithProvidedBuffer(Level level, const(char)[] fmt, TypeInfo[] types, ArgList args) 918 { 919 formatWithBuffer(level, fmt, types, args, buffer_); 920 buffer_.length = buffer_size_; 921 } 922 923 private void formatWithBuffer(Level level, const(char)[] fmt, TypeInfo[] types, ArgList args, char[] buf) 924 { 925 append (level, Format.vprint (buf, fmt, types, args)); 926 } 927 928 /*********************************************************************** 929 930 See if the provided Logger name is a parent of this one. Note 931 that each Logger name has a '.' appended to the end, such that 932 name segments will not partially match. 933 934 ***********************************************************************/ 935 936 private final bool isChildOf (const(char[]) candidate) 937 { 938 auto len = candidate.length; 939 940 // possible parent if length is shorter 941 if (len < name_.length) 942 // does the prefix match? Note we append a "." to each 943 // (the root is a parent of everything) 944 return (len is 0 || 945 memcmp (&candidate[0], &name_[0], len) is 0); 946 return false; 947 } 948 949 /*********************************************************************** 950 951 See if the provided Logger is a better match as a parent of 952 this one. This is used to restructure the hierarchy when a 953 new logger instance is introduced 954 955 ***********************************************************************/ 956 957 private final bool isCloserAncestor (Logger other) 958 { 959 auto name = other.name_; 960 if (isChildOf (name)) 961 // is this a better (longer) match than prior parent? 962 if ((parent is null) || (name.length >= parent.name_.length)) 963 return true; 964 return false; 965 } 966 } 967 968 /******************************************************************************* 969 970 The Logger hierarchy implementation. We keep a reference to each 971 logger in a hash-table for convenient lookup purposes, plus keep 972 each logger linked to the others in an ordered group. Ordering 973 places shortest names at the head and longest ones at the tail, 974 making the job of identifying ancestors easier in an orderly 975 fashion. For example, when propagating levels across descendents 976 it would be a mistake to propagate to a child before all of its 977 ancestors were taken care of. 978 979 *******************************************************************************/ 980 981 private class Hierarchy : Logger.Context 982 { 983 private Logger root_; 984 private const(char)[] name_, 985 address_; 986 private Logger.Context context_; 987 private Logger[char[]] loggers; 988 989 990 /*********************************************************************** 991 992 Construct a hierarchy with the given name. 993 994 ***********************************************************************/ 995 996 this (const(char[]) name) 997 { 998 name_ = name; 999 address_ = "network"; 1000 1001 // insert a root node; the root has an empty name 1002 root_ = new Logger (this, ""); 1003 context_ = this; 1004 } 1005 1006 /********************************************************************** 1007 1008 **********************************************************************/ 1009 1010 final const const(char)[] label () 1011 { 1012 return ""; 1013 } 1014 1015 /********************************************************************** 1016 1017 1018 **********************************************************************/ 1019 1020 final const bool enabled (Level level, Level test) 1021 { 1022 return test >= level; 1023 } 1024 1025 /********************************************************************** 1026 1027 Return the name of this Hierarchy 1028 1029 **********************************************************************/ 1030 1031 @property final const const(char)[] name () 1032 { 1033 return name_; 1034 } 1035 1036 /********************************************************************** 1037 1038 Set the name of this Hierarchy 1039 1040 **********************************************************************/ 1041 1042 @property final void name (const(char[]) name) 1043 { 1044 name_ = name; 1045 } 1046 1047 /********************************************************************** 1048 1049 Return the address of this Hierarchy. This is typically 1050 attached when sending events to remote monitors. 1051 1052 **********************************************************************/ 1053 1054 @property final const const(char)[] address () 1055 { 1056 return address_; 1057 } 1058 1059 /********************************************************************** 1060 1061 Set the address of this Hierarchy. The address is attached 1062 used when sending events to remote monitors. 1063 1064 **********************************************************************/ 1065 1066 @property final void address (const(char[]) address) 1067 { 1068 address_ = address; 1069 } 1070 1071 /********************************************************************** 1072 1073 Return the diagnostic context. Useful for setting an 1074 override logging level. 1075 1076 **********************************************************************/ 1077 1078 @property final Logger.Context context () 1079 { 1080 return context_; 1081 } 1082 1083 /********************************************************************** 1084 1085 Set the diagnostic context. Not usually necessary, as a 1086 default was created. Useful when you need to provide a 1087 different implementation, such as a ThreadLocal variant. 1088 1089 **********************************************************************/ 1090 1091 @property final void context (Logger.Context context) 1092 { 1093 context_ = context; 1094 } 1095 1096 /*********************************************************************** 1097 1098 Return the root node. 1099 1100 ***********************************************************************/ 1101 1102 @property final Logger root () 1103 { 1104 return root_; 1105 } 1106 1107 /*********************************************************************** 1108 1109 Return the instance of a Logger with the provided label. If 1110 the instance does not exist, it is created at this time. 1111 1112 Note that an empty label is considered illegal, and will be 1113 ignored. 1114 1115 ***********************************************************************/ 1116 1117 final Logger lookup (const(char[]) label) 1118 { 1119 if (label.length) 1120 return inject (label, (const(char[]) name) 1121 {return new Logger (this, name);}); 1122 return null; 1123 } 1124 1125 /*********************************************************************** 1126 1127 traverse the set of configured loggers 1128 1129 ***********************************************************************/ 1130 1131 final int opApply (scope int delegate(ref Logger) dg) 1132 { 1133 int ret; 1134 1135 for (auto log=root; log; log = log.next) 1136 if ((ret = dg(log)) != 0) 1137 break; 1138 return ret; 1139 } 1140 1141 /*********************************************************************** 1142 1143 Return the instance of a Logger with the provided label. If 1144 the instance does not exist, it is created at this time. 1145 1146 ***********************************************************************/ 1147 1148 private Logger inject (const(char[]) label, scope Logger delegate(const(char[]) name) dg) 1149 { 1150 synchronized(this) 1151 { 1152 // try not to allocate unless you really need to 1153 char[255] stack_buffer; 1154 char[] buffer = stack_buffer; 1155 1156 if (buffer.length < label.length + 1) 1157 buffer.length = label.length + 1; 1158 1159 buffer[0 .. label.length] = label[]; 1160 buffer[label.length] = '.'; 1161 1162 auto name = buffer[0 .. label.length + 1]; 1163 auto l = name in loggers; 1164 1165 if (l is null) 1166 { 1167 // don't use the stack allocated buffer 1168 if (name.ptr is stack_buffer.ptr) 1169 name = name.dup; 1170 // create a new logger 1171 auto li = dg(name); 1172 l = &li; 1173 1174 // insert into linked list 1175 insert (li); 1176 1177 // look for and adjust children. Don't force 1178 // property inheritance on existing loggers 1179 update (li); 1180 1181 // insert into map 1182 loggers [name.idup] = li; 1183 } 1184 1185 return *l; 1186 } 1187 } 1188 1189 /*********************************************************************** 1190 1191 Loggers are maintained in a sorted linked-list. The order 1192 is maintained such that the shortest name is at the root, 1193 and the longest at the tail. 1194 1195 This is done so that updateLoggers() will always have a 1196 known environment to manipulate, making it much faster. 1197 1198 ***********************************************************************/ 1199 1200 private void insert (Logger l) 1201 { 1202 Logger prev, 1203 curr = root; 1204 1205 while (curr) 1206 { 1207 // insert here if the new name is shorter 1208 if (l.name.length < curr.name.length) 1209 if (prev is null) 1210 throw new IllegalElementException ("invalid hierarchy"); 1211 else 1212 { 1213 l.next = prev.next; 1214 prev.next = l; 1215 return; 1216 } 1217 else 1218 // find best match for parent of new entry 1219 // and inherit relevant properties (level, etc) 1220 propagate (l, curr, true); 1221 1222 // remember where insertion point should be 1223 prev = curr; 1224 curr = curr.next; 1225 } 1226 1227 // add to tail 1228 prev.next = l; 1229 } 1230 1231 /*********************************************************************** 1232 1233 Propagate hierarchical changes across known loggers. 1234 This includes changes in the hierarchy itself, and to 1235 the various settings of child loggers with respect to 1236 their parent(s). 1237 1238 ***********************************************************************/ 1239 1240 private void update (Logger changed, bool force=false) 1241 { 1242 foreach (logger; this) 1243 propagate (logger, changed, force); 1244 } 1245 1246 /*********************************************************************** 1247 1248 Propagate changes in the hierarchy downward to child Loggers. 1249 Note that while 'parent' is always changed, the adjustment of 1250 'level' is selectable. 1251 1252 ***********************************************************************/ 1253 1254 private void propagate (Logger logger, Logger changed, bool force=false) 1255 { 1256 // is the changed instance a better match for our parent? 1257 if (logger.isCloserAncestor (changed)) 1258 { 1259 // update parent (might actually be current parent) 1260 logger.parent = changed; 1261 1262 // if we don't have an explicit level set, inherit it 1263 // Be careful to avoid recursion, or other overhead 1264 if (force) 1265 logger.level_ = changed.level(); 1266 } 1267 } 1268 } 1269 1270 1271 1272 /******************************************************************************* 1273 1274 Contains all information about a logging event, and is passed around 1275 between methods once it has been determined that the invoking logger 1276 is enabled for output. 1277 1278 Note that Event instances are maintained in a freelist rather than 1279 being allocated each time, and they include a scratchpad area for 1280 EventLayout formatters to use. 1281 1282 *******************************************************************************/ 1283 1284 package struct LogEvent 1285 { 1286 private const(char)[] msg_, 1287 name_; 1288 private Time time_; 1289 private Level level_; 1290 private Hierarchy host_; 1291 1292 /*********************************************************************** 1293 1294 Set the various attributes of this event. 1295 1296 ***********************************************************************/ 1297 1298 void set (Hierarchy host, Level level, const(char[]) msg, const(char[]) name) 1299 { 1300 time_ = Log.time; 1301 level_ = level; 1302 host_ = host; 1303 name_ = name; 1304 msg_ = msg; 1305 } 1306 1307 /*********************************************************************** 1308 1309 Return the message attached to this event. 1310 1311 ***********************************************************************/ 1312 1313 const const(char)[] toString () 1314 { 1315 return msg_; 1316 } 1317 1318 /*********************************************************************** 1319 1320 Return the name of the logger which produced this event 1321 1322 ***********************************************************************/ 1323 1324 @property const const(char)[] name () 1325 { 1326 return name_; 1327 } 1328 1329 /*********************************************************************** 1330 1331 Return the logger level of this event. 1332 1333 ***********************************************************************/ 1334 1335 @property const Level level () 1336 { 1337 return level_; 1338 } 1339 1340 /*********************************************************************** 1341 1342 Return the hierarchy where the event was produced from 1343 1344 ***********************************************************************/ 1345 1346 @property Hierarchy host () 1347 { 1348 return host_; 1349 } 1350 1351 /*********************************************************************** 1352 1353 Return the time this event was produced, relative to the 1354 start of this executable 1355 1356 ***********************************************************************/ 1357 1358 @property const TimeSpan span () 1359 { 1360 return time_ - Log.beginTime; 1361 } 1362 1363 /*********************************************************************** 1364 1365 Return the time this event was produced relative to Epoch 1366 1367 ***********************************************************************/ 1368 1369 @property const const(Time) time () 1370 { 1371 return time_; 1372 } 1373 1374 /*********************************************************************** 1375 1376 Return time when the executable started 1377 1378 ***********************************************************************/ 1379 1380 @property const(Time) started () 1381 { 1382 return Log.beginTime; 1383 } 1384 1385 /*********************************************************************** 1386 1387 Return the logger level name of this event. 1388 1389 ***********************************************************************/ 1390 1391 @property const(char)[] levelName () 1392 { 1393 return Log.LevelNames[level_]; 1394 } 1395 1396 /*********************************************************************** 1397 1398 Convert a time value (in milliseconds) to ascii 1399 1400 ***********************************************************************/ 1401 1402 static char[] toMilli (char[] s, TimeSpan time) 1403 { 1404 assert (s.length > 0); 1405 long ms = time.millis; 1406 1407 size_t len = s.length; 1408 do { 1409 s[--len] = cast(char)(ms % 10 + '0'); 1410 ms /= 10; 1411 } while (ms && len); 1412 return s[len..s.length]; 1413 } 1414 } 1415 1416 1417 /******************************************************************************* 1418 1419 Base class for all Appenders. These objects are responsible for 1420 emitting messages sent to a particular logger. There may be more 1421 than one appender attached to any logger. The actual message is 1422 constructed by another class known as an EventLayout. 1423 1424 *******************************************************************************/ 1425 1426 public class Appender 1427 { 1428 alias int Mask; 1429 1430 private Appender next_; 1431 private Level level_; 1432 private Layout layout_; 1433 private static __gshared Layout generic; 1434 1435 /*********************************************************************** 1436 1437 Interface for all logging layout instances 1438 1439 Implement this method to perform the formatting of 1440 message content. 1441 1442 ***********************************************************************/ 1443 1444 interface Layout 1445 { 1446 void format (LogEvent event, scope size_t delegate(const(void)[]) dg); 1447 } 1448 1449 /*********************************************************************** 1450 1451 Return the mask used to identify this Appender. The mask 1452 is used to figure out whether an appender has already been 1453 invoked for a particular logger. 1454 1455 ***********************************************************************/ 1456 1457 @property abstract const Mask mask (); 1458 1459 /*********************************************************************** 1460 1461 Return the name of this Appender. 1462 1463 ***********************************************************************/ 1464 1465 @property abstract const const(char)[] name (); 1466 1467 /*********************************************************************** 1468 1469 Append a message to the output. 1470 1471 ***********************************************************************/ 1472 1473 abstract void append (LogEvent event); 1474 1475 /*********************************************************************** 1476 1477 Create an Appender and default its layout to LayoutSimple. 1478 1479 ***********************************************************************/ 1480 1481 this () 1482 { 1483 layout_ = generic; 1484 } 1485 1486 /*********************************************************************** 1487 1488 Create an Appender and default its layout to LayoutSimple. 1489 1490 ***********************************************************************/ 1491 1492 shared static this () 1493 { 1494 generic = new LayoutTimer; 1495 } 1496 1497 /*********************************************************************** 1498 1499 Return the current Level setting 1500 1501 ***********************************************************************/ 1502 1503 @property final Level level () 1504 { 1505 return level_; 1506 } 1507 1508 /*********************************************************************** 1509 1510 Return the current Level setting 1511 1512 ***********************************************************************/ 1513 1514 @property final Appender level (Level l) 1515 { 1516 level_ = l; 1517 return this; 1518 } 1519 1520 /*********************************************************************** 1521 1522 Static method to return a mask for identifying the Appender. 1523 Each Appender class should have a unique fingerprint so that 1524 we can figure out which ones have been invoked for a given 1525 event. A bitmask is a simple an efficient way to do that. 1526 1527 ***********************************************************************/ 1528 1529 protected Mask register (const(char)[] tag) 1530 { 1531 static Mask mask = 1; 1532 static Mask[immutable(char)[]] registry; 1533 1534 Mask* p = tag in registry; 1535 if (p) 1536 return *p; 1537 else 1538 { 1539 auto ret = mask; 1540 registry [tag] = mask; 1541 1542 if (mask < 0) 1543 throw new IllegalArgumentException ("too many unique registrations"); 1544 1545 mask <<= 1; 1546 return ret; 1547 } 1548 } 1549 1550 /*********************************************************************** 1551 1552 Set the current layout to be that of the argument, or the 1553 generic layout where the argument is null 1554 1555 ***********************************************************************/ 1556 1557 @property void layout (Layout how) 1558 { 1559 layout_ = how ? how : generic; 1560 } 1561 1562 /*********************************************************************** 1563 1564 Return the current Layout 1565 1566 ***********************************************************************/ 1567 1568 @property Layout layout () 1569 { 1570 return layout_; 1571 } 1572 1573 /*********************************************************************** 1574 1575 Attach another appender to this one 1576 1577 ***********************************************************************/ 1578 1579 @property void next (Appender appender) 1580 { 1581 next_ = appender; 1582 } 1583 1584 /*********************************************************************** 1585 1586 Return the next appender in the list 1587 1588 ***********************************************************************/ 1589 1590 @property Appender next () 1591 { 1592 return next_; 1593 } 1594 1595 /*********************************************************************** 1596 1597 Close this appender. This would be used for file, sockets, 1598 and such like. 1599 1600 ***********************************************************************/ 1601 1602 void close () 1603 { 1604 } 1605 } 1606 1607 1608 /******************************************************************************* 1609 1610 An appender that does nothing. This is useful for cutting and 1611 pasting, and for benchmarking the tango.log environment. 1612 1613 *******************************************************************************/ 1614 1615 public class AppendNull : Appender 1616 { 1617 private Mask mask_; 1618 1619 /*********************************************************************** 1620 1621 Create with the given Layout 1622 1623 ***********************************************************************/ 1624 1625 this (Layout how = null) 1626 { 1627 mask_ = register (name); 1628 layout (how); 1629 } 1630 1631 /*********************************************************************** 1632 1633 Return the fingerprint for this class 1634 1635 ***********************************************************************/ 1636 1637 @property override final const Mask mask () 1638 { 1639 return mask_; 1640 } 1641 1642 /*********************************************************************** 1643 1644 Return the name of this class 1645 1646 ***********************************************************************/ 1647 1648 @property override final const const(char)[] name () 1649 { 1650 return this.classinfo.name; 1651 } 1652 1653 /*********************************************************************** 1654 1655 Append an event to the output. 1656 1657 ***********************************************************************/ 1658 1659 override final void append (LogEvent event) 1660 { 1661 layout.format (event, (const(void)[]){return cast(size_t) 0;}); 1662 } 1663 } 1664 1665 1666 /******************************************************************************* 1667 1668 Append to a configured OutputStream 1669 1670 *******************************************************************************/ 1671 1672 public class AppendStream : Appender 1673 { 1674 private Mask mask_; 1675 private bool flush_; 1676 private OutputStream stream_; 1677 1678 /*********************************************************************** 1679 1680 Create with the given stream and layout 1681 1682 ***********************************************************************/ 1683 1684 this (OutputStream stream, bool flush = false, Appender.Layout how = null) 1685 { 1686 assert (stream); 1687 1688 mask_ = register (name); 1689 stream_ = stream; 1690 flush_ = flush; 1691 layout (how); 1692 } 1693 1694 /*********************************************************************** 1695 1696 Return the fingerprint for this class 1697 1698 ***********************************************************************/ 1699 1700 @property override final const Mask mask () 1701 { 1702 return mask_; 1703 } 1704 1705 /*********************************************************************** 1706 1707 Return the name of this class 1708 1709 ***********************************************************************/ 1710 1711 @property override const const(char)[] name () 1712 { 1713 return this.classinfo.name; 1714 } 1715 1716 /*********************************************************************** 1717 1718 Append an event to the output. 1719 1720 ***********************************************************************/ 1721 1722 override final void append (LogEvent event) 1723 { 1724 version(Win32) 1725 immutable char[] Eol = "\r\n"; 1726 else 1727 immutable char[] Eol = "\n"; 1728 1729 synchronized (stream_) 1730 { 1731 layout.format (event, (const(void)[] content){return stream_.write(content);}); 1732 stream_.write (Eol); 1733 if (flush_) 1734 stream_.flush(); 1735 } 1736 } 1737 } 1738 1739 /******************************************************************************* 1740 1741 A simple layout comprised only of time(ms), level, name, and message 1742 1743 *******************************************************************************/ 1744 1745 public class LayoutTimer : Appender.Layout 1746 { 1747 /*********************************************************************** 1748 1749 Subclasses should implement this method to perform the 1750 formatting of the actual message content. 1751 1752 ***********************************************************************/ 1753 1754 void format (LogEvent event, scope size_t delegate(const(void)[]) dg) 1755 { 1756 char[20] tmp = void; 1757 1758 dg (event.toMilli (tmp, event.span)); 1759 dg (" "); 1760 dg (event.levelName); 1761 dg (" ["); 1762 dg (event.name); 1763 dg ("] "); 1764 dg (event.host.context.label); 1765 dg ("- "); 1766 dg (event.toString()); 1767 } 1768 } 1769 1770 1771 /******************************************************************************* 1772 1773 *******************************************************************************/ 1774 1775 debug (Log) 1776 { 1777 import tango.io.Console; 1778 1779 void main() 1780 { 1781 Log.config (Cerr.stream); 1782 auto log = Log.lookup ("fu.bar"); 1783 log.level = log.Trace; 1784 // traditional usage 1785 log.trace ("hello {}", "world"); 1786 1787 char[100] buf; 1788 log (log.Trace, log.format(buf, "hello {}", "world")); 1789 1790 // formatted output 1791 /* / 1792 auto format = Log.format; 1793 log.info (format ("blah{}", 1)); 1794 1795 // snapshot 1796 auto snap = Log.snapshot (log, Level.Error); 1797 snap.format ("arg{}; ", 1); 1798 snap.format ("arg{}; ", 2); 1799 //log.trace (snap.format ("error! arg{}", 3)); 1800 snap.flush; 1801 */ 1802 } 1803 }