1 /++
2 Package provides static `Logger logger`
3 
4 Logger functions:
5 
6     void logger.error(Args...)( Args args );
7     void logger.warn (Args...)( Args args );
8     void logger.info (Args...)( Args args );
9     void logger.Debug(Args...)( Args args );
10     void logger.trace(Args...)( Args args );
11 
12     ...
13     logger.info( "format %s %f %d", "str", 3.14, 4 );
14     logger.Debug( "some info" );
15     logger.trace( 12 );
16     ...
17 
18 If program starts as `./program --log trace` output must be like this
19 
20     [000000.111427128][ INFO][module.function]: format str 3.14 4
21     [000000.111427128][DEBUG][module.function]: some info
22     [000000.111938579][TRACE][module.function]: 12
23 
24 If log function has string as first argument it tries to format other args to this
25 string, if it failed print converted to string and concatenated args.
26 
27 If program starts as `./program --log debug` output must be like this (without trace)
28 
29     [000000.111427128][ INFO][module.function]: format str 3.14 4
30 
31 Flag `--log` used for setting max level of logging output.
32 Default level is `error`. If log function called with greater level it's skipped.
33 Level has attitudes `off < fatal < error < warn < info < debug < trace`.
34 
35 Flag `--log` can be used with module name `./program --log draw.point:debug`.
36 It will set `debug` level for module `draw.point` and default to other.
37 
38 Flag `--log-use-min` is boolean flag. It forces logging system to skip output from
39 all child modules if their level greater than parent. Default is `false`.
40 
41 `./program --log trace --log draw:info --log draw.point:trace --log-use-min=true` 
42 skips all output from `logger.trace` and `logger.Debug` from whole draw.point,
43 and doesn't skip from other modules.
44 
45 `./program --log trace --log draw:info --log draw.point:trace` allow `log_trace`
46 and `log_debug` only from `draw.point` from module `draw`. For other modules in
47 `draw` sets level `info`
48 
49 You can compile program with `version=logonlyerror` for skip all
50 `trace`, `debug`, `info` and `warn` outputs in logger. It can improve program
51 release speed.
52 
53 ## Class logging
54 
55 Module provides some functional for useful logging classes.
56 
57 Example:
58 
59     module x;
60     import des.util.logger;
61     class A
62     {
63         mixin ClassLogger;
64         void func() { logger.trace( "hello" ); }
65     }
66 
67     module y;
68     import x;
69     class B : A { }
70 
71     auto b = new B;
72     ...
73     b.func();
74 
75 outputs:
76 
77     [000000.148628473][TRACE][x.A.func]: hello
78 
79 If create instance logger 
80 
81     class B : A { this(){ logger = new InstanceLogger(this); } }
82 
83 outputs:
84 
85     [000000.148628473][TRACE][y.B.func]: hello
86 
87 If create instance logger with instance name
88 
89     class B : A { this(){ logger = new InstanceLogger(this,"my object"); } }
90 
91 outputs:
92 
93     [000000.148628473][TRACE][y.B.[my object].func]: hello
94 
95 If create instance full logger
96 
97     class B : A { this(){ logger = new InstanceFullLogger(this); } }
98 
99 outputs:
100     [000000.148628473][TRACE][y.B.[x.A.func]]: hello
101 
102 If create instance full logger with name
103 
104     class B : A { this(){ logger = new InstanceFullLogger(this,"name"); } }
105 
106 outputs:
107 
108     [000000.148628473][TRACE][y.B.[name].[x.A.func]]: hello
109 
110 Flag `--log` can get full emitter string `y.B.[name].[x.A.func]`.
111 
112     ./program --log "y.B.[one]:trace" --log "y.B.[two]:debug"
113  +/
114 module des.util.logsys;
115 
116 public
117 {
118     import des.util.logsys.base;
119     import des.util.logsys.logcls;
120     import des.util.logsys.output;
121 }
122 
123 import des.util.logsys.rule;
124 
125 /// for simple adding logging to class
126 mixin template ClassLogger()
127 {
128     static if( !is( typeof( __logger ) ) )
129     {
130         private Logger __logger;
131         protected nothrow final @property
132         {
133             const(Logger) logger() const
134             {
135                 mixin( "static import " ~ __MODULE__ ~ ";" );
136                 if( __logger is null )
137                     mixin( "return " ~ __MODULE__ ~ ".logger;" );
138                 else return __logger;
139             }
140             void logger( Logger lg ) { __logger = lg; }
141         }
142     }
143 }
144 
145 Logger logger; ///
146 
147 static this() { logger = new Logger; }
148 
149 shared static this()
150 {
151     if( logrule !is null ) return;
152 
153     import core.runtime, std.getopt;
154     import std.stdio;
155     import std.file;
156 
157     logrule = new shared Rule;
158 
159     auto args = thisExePath ~ Runtime.args;
160     string[] logging;
161     bool use_minimal = false;
162     bool console_color = true;
163     string logfile;
164     
165     try
166     {
167         getopt( args,
168                 std.getopt.config.passThrough,
169                 "log", &logging,
170                 "log-use-min", &use_minimal,
171                 "log-console-color", &console_color,
172                 "log-file", &logfile,
173               );
174     }
175     catch( Exception e ) stderr.writefln( "bad log arguments: %s", e.msg );
176 
177     logoutput = new shared LogOutputHandler( console_color );
178 
179     if( logfile.length )
180         logoutput.append( "logfile", new shared FileLogOutput(logfile) );
181 
182     logrule.use_minimal = use_minimal;
183 
184     foreach( ln; logging )
185     {
186         auto sp = ln.split(":");
187         if( sp.length == 1 )
188         {
189             try logrule.setLevel( toLogLevel( sp[0] ) );
190             catch( Exception e )
191                 stderr.writefln( "log argument '%s' can't conv to LogLevel: %s", ln, e.msg );
192         }
193         else if( sp.length == 2 )
194         {
195             try
196             {
197                 auto level = toLogLevel( sp[1] );
198                 logrule.setLevel( level, sp[0] );
199             }
200             catch( Exception e )
201                 stderr.writefln( "log argument '%s' can't conv '%s' to LogLevel: %s", ln, sp[1], e.msg );
202         }
203         else stderr.writefln( "bad log argument: %s" );
204     }
205 
206     if( logging.length )
207     {
208         writeln( "[log use min]: ", use_minimal );
209         writeln( "[log rules]:\n", logrule.print() );
210     }
211 }