1 /++ ### Simple example
2  +
3  + ---
4  + import des.log;
5  +
6  + void func()
7  + {
8  +     logger.fatal( "fatal" );
9  +     logger.error( "error" );
10  +     logger.warn( "warn" );
11  +     logger.info( "info" );
12  +     logger.Debug( "debug" );
13  +     logger.trace( "trace" );
14  + }
15  +
16  + void main()
17  + {
18  +     logger.fatal( "fatal message" );
19  +     logger.error( "error message" );
20  +     logger.warn( "warn message" );
21  +     logger.info( "info message" );
22  +     logger.Debug( "debug message" );
23  +     logger.trace( "trace message" );
24  +
25  +     func();
26  + }
27  + ---
28  +
29  + can have output like this:
30  +
31  + ---
32  + ./app
33  + [000000.000122258][FATAL][app.main]: fatal message
34  + [000000.000214030][ERROR][app.main]: error message
35  + [000000.000261067][FATAL][app.func]: fatal
36  + [000000.000285349][ERROR][app.func]: error
37  + ---
38  +
39 
40  + Flag `--log` used for setting max level of logging output.
41  + Default level is `error`. If log function called with greater level it's skipped.
42  + Level has attitudes `off < fatal < error < warn < info < debug < trace`.
43  +
44  +
45  + ---
46  + $ ./app --log app.func:debug
47  + [log use min]: false
48  + [log rules]:
49  + ERROR
50  + app : ERROR
51  +    func : DEBUG
52  + [000000.000162889][FATAL][app.main]: fatal message
53  + [000000.000207483][ERROR][app.main]: error message
54  + [000000.000242506][FATAL][app.func]: fatal
55  + [000000.000261887][ERROR][app.func]: error
56  + [000000.000285754][ WARN][app.func]: warn
57  + [000000.000304789][ INFO][app.func]: info
58  + [000000.000323652][DEBUG][app.func]: debug
59  + ---
60  +
61  +
62  + ---
63  + $ ./app --log info --log app.func:trace
64  + [log use min]: false
65  + [log rules]:
66  + INFO
67  + app : INFO
68  +    func : TRACE
69  + [000000.000245525][FATAL][app.main]: fatal message
70  + [000000.000308796][ERROR][app.main]: error message
71  + [000000.000338714][ WARN][app.main]: warn message
72  + [000000.000365555][ INFO][app.main]: info message
73  + [000000.000406501][FATAL][app.func]: fatal
74  + [000000.000434482][ERROR][app.func]: error
75  + [000000.000461296][ WARN][app.func]: warn
76  + [000000.000487242][ INFO][app.func]: info
77  + [000000.000512884][DEBUG][app.func]: debug
78  + [000000.000538288][TRACE][app.func]: trace
79  + ---
80  +
81  +
82  + Flag `--log` can be used with module name `./program --log draw.point:debug`.
83  + It will set `debug` level for module `draw.point` and default to other.
84  +
85  + Flag `--log-use-min` is boolean flag. It forces logging system to skip output from
86  + all child modules if their level greater than parent. Default is `false`.
87  +
88  + `./program --log trace --log draw:info --log draw.point:trace --log-use-min=true`
89  + skips all output from `logger.trace` and `logger.Debug` from whole draw.point,
90  + and doesn't skip from other modules.
91  +
92  + `./program --log trace --log draw:info --log draw.point:trace` allow `log_trace`
93  + and `log_debug` only from `draw.point` from module `draw`. For other modules in
94  + `draw` sets level `info`
95  +
96  + You can compile program with `version=des_log_onlyerror` for skip all
97  + `trace`, `debug`, `info` and `warn` outputs in logger. It can improve program
98  + release speed.
99  +
100  + ### Class logging
101  +
102  + Module provides some functional for useful logging classes.
103  +
104  + Example:
105  +
106  + ---
107  + module x;
108  + import des.log;
109  + class A
110  + {
111  +     mixin ClassLogger;
112  +     void func() { logger.trace( "hello" ); }
113  + }
114  + ---
115  + ---
116  + module y;
117  + import x;
118  + class B : A { }
119  + ---
120  + ---
121  + auto b = new B;
122  + b.func();
123  + ---
124  + 
125  + outputs:
126  + ---
127  + [000000.148628473][TRACE][x.A.func]: hello
128  + ---
129  + 
130  + If create instance logger
131  + ---
132  + class B : A { this(){ logger = new InstanceLogger(this); } }
133  + ---
134  + 
135  + outputs:
136  + ---
137  + [000000.148628473][TRACE][y.B.func]: hello
138  + ---
139  + 
140  + If create instance logger with instance name
141  + ---
142  + class B : A { this(){ logger = new InstanceLogger(this,"my object"); } }
143  + ---
144  + 
145  + outputs:
146  + ---
147  + [000000.148628473][TRACE][y.B.[my object].func]: hello
148  + ---
149  + 
150  + If create instance full logger
151  + ---
152  + class B : A { this(){ logger = new InstanceFullLogger(this); } }
153  + ---
154  + 
155  + outputs:
156  + ---
157  + [000000.148628473][TRACE][y.B.[x.A.func]]: hello
158  + ---
159  + 
160  + If create instance full logger with name
161  + 
162  + ---
163  + class B : A { this(){ logger = new InstanceFullLogger(this,"name"); } }
164  + ---
165  + 
166  + outputs:
167  + ---
168  + [000000.148628473][TRACE][y.B.[name].[x.A.func]]: hello
169  + ---
170  + 
171  + Flag `--log` can get full emitter string `y.B.[name].[x.A.func]`.
172  + ---
173  + ./program --log "y.B.[one]:trace" --log "y.B.[two]:debug"
174  + ---
175  +/
176 module des.log;
177 
178 public
179 {
180     import des.log.base;
181     import des.log.logcls;
182     import des.log.rule;
183     import des.log.output;
184 }
185 
186 import core.runtime, std.getopt;
187 import std.stdio;
188 import std.file;
189 
190 /// for simple adding logging to class
191 mixin template ClassLogger()
192 {
193     static if( !is( typeof( __logger ) ) )
194     {
195         private Logger __logger;
196         protected nothrow final @property
197         {
198             const(Logger) logger() const
199             {
200                 mixin( "static import " ~ __MODULE__ ~ ";" );
201                 if( __logger is null )
202                     mixin( "return " ~ __MODULE__ ~ ".logger;" );
203                 else return __logger;
204             }
205             void logger( Logger lg ) { __logger = lg; }
206         }
207     }
208 }
209 
210 Logger logger; ///
211 
212 static this() { logger = new Logger; }
213 
214 GetoptResult logger_getopt_result;
215 
216 shared static this()
217 {
218     if( g_rule !is null ) return;
219 
220     g_rule = new shared Rule;
221 
222     auto args = thisExePath ~ Runtime.args;
223     string[] logging;
224     bool use_minimal = false;
225     bool console_color = true;
226     string logfile;
227 
228     try
229     {
230         logger_getopt_result = getopt( args,
231                 std.getopt.config.passThrough,
232                 "log", &logging,
233                 "log-use-min", &use_minimal,
234                 "log-console-color", &console_color,
235                 "log-file", &logfile,
236               );
237     }
238     catch( Exception e ) stderr.writefln( "bad log arguments: %s", e.msg );
239 
240     g_output = new shared OutputHandler( console_color );
241 
242     if( logfile.length )
243         g_output.append( "logfile", new shared FileLogOutput(logfile) );
244 
245     g_rule.use_minimal = use_minimal;
246 
247     foreach( ln; logging )
248     {
249         auto sp = ln.split(":");
250         if( sp.length == 1 )
251         {
252             try g_rule.setLevel( toLogLevel( sp[0] ) );
253             catch( Exception e )
254                 stderr.writefln( "log argument '%s' can't conv to LogLevel: %s", ln, e.msg );
255         }
256         else if( sp.length == 2 )
257         {
258             try
259             {
260                 auto level = toLogLevel( sp[1] );
261                 g_rule.setLevel( level, sp[0] );
262             }
263             catch( Exception e )
264                 stderr.writefln( "log argument '%s' can't conv '%s' to LogLevel: %s", ln, sp[1], e.msg );
265         }
266         else stderr.writefln( "bad log argument: %s" );
267     }
268 
269     if( logging.length )
270     {
271         writeln( "[log use min]: ", use_minimal );
272         writeln( "[log rules]:\n", g_rule.print() );
273     }
274 }