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