1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module w4d.log;
8 import w4d.exception;
9 import std.conv,
10        std.format,
11        std.stdio,
12        std..string;
13 
14 /// An enum of log levels.
15 /// The uint value is priority of the level.
16 enum LogLevel : uint
17 {
18     Trace = 0,
19     Info,
20     Warn,
21     Error,
22     Fatal,
23 }
24 
25 /// Converts LogLevel to string.
26 @property toString ( LogLevel lv, bool colored )
27 {
28     string result = "";
29     final switch ( lv ) with ( LogLevel )
30     {
31         case Trace: result = "trace";
32                     break;
33         case Info : result = "info";
34                     break;
35         case Warn : result = "warn";
36                     break;
37         case Error: result = "error";
38                     break;
39         case Fatal: result = "fatal";
40                     break;
41     }
42     if ( colored ) {
43         result = lv.toColorCode ~ result ~ "\x1b[39m";
44     }
45     return result;
46 }
47 /// Converts LogLevel to color code in terminal.
48 @property toColorCode ( LogLevel lv )
49 {
50     final switch ( lv ) with ( LogLevel )
51     {
52         case Trace: return "\x1b[37m"; // white
53         case Info : return "\x1b[36m"; // cyan
54         case Warn : return "\x1b[33m"; // yellow
55         case Error: return "\x1b[35m"; // magenta
56         case Fatal: return "\x1b[31m"; // red
57     }
58 }
59 
60 /// A logger used in w4d library.
61 class W4dLogger
62 {
63     protected __gshared W4dLogger _instance;
64     ///
65     shared static this ()
66     {
67         new W4dLogger;
68     }
69     /// An unique instance of W4dLogger.
70     static @property instance ()
71     {
72         enforce( _instance, "W4dLogger isn't created yet." );
73         return _instance;
74     }
75 
76     protected File _output;
77     protected bool _colored;
78 
79     protected LogLevel _outputThreshold;
80     protected LogLevel _throwThreshold;
81 
82     ///
83     this ()
84     {
85         enforce( !_instance, "W4dLogger is created already." );
86         _instance = this;
87 
88         _output  = stdout;
89         _colored = true;
90 
91         _outputThreshold = LogLevel.Trace;
92         _throwThreshold  = LogLevel.Fatal;
93 
94         trace( "W4dLogger has been initialized." );
95     }
96     ~this ()
97     {
98         trace( "W4dLogger has been deleted." );
99     }
100 
101     /// Changes output file. Specify stdout to output to the terminal.
102     /// When colored is true, logs will be colored for the terminal.
103     void setOutputFile ( File f, bool colored = false )
104     {
105         _output  = f;
106         _colored = colored;
107     }
108     /// Changes threshold levels for handling logs.
109     void setThreshold ( LogLevel out_, LogLevel throw_ )
110     {
111         _outputThreshold = out_;
112         _throwThreshold  = throw_;
113     }
114 
115     protected string formatLog ( LogLevel lv, string text,
116             string file, size_t line, string func )
117     {
118         return ("[%s] %s\n"~
119             "    %s at %d (%s)").
120             format( lv.toString(_colored), text, file, line, func );
121     }
122 
123     protected void writeLog
124         ( string file = __FILE__, size_t line = __LINE__, string func = __FUNCTION__, Args... )
125         ( LogLevel lv, Args args )
126     {
127         if ( lv < _outputThreshold ) return;
128 
129         string text;
130         foreach ( arg; args ) {
131             text ~= arg.to!string ~ " ";
132         }
133         text = text.chop;
134 
135         auto formattedText = formatLog(
136                 lv, text, file, line, func );
137         _output.writeln( formattedText );
138 
139         if ( lv >= _throwThreshold ) {
140             auto msg = "%s (%s)".format( text, func );
141             throw new W4dException( msg, file, line );
142         }
143     }
144 
145     /// Makes new trace log.
146     void trace
147         ( string file = __FILE__, size_t line = __LINE__, string func = __FUNCTION__, Args... )
148         ( Args args )
149     {
150         writeLog!( file, line, func )( LogLevel.Trace, args );
151     }
152     /// Makes new info log.
153     void info
154         ( string file = __FILE__, size_t line = __LINE__, string func = __FUNCTION__, Args... )
155         ( Args args )
156     {
157         writeLog!( file, line, func )( LogLevel.Info, args );
158     }
159     /// Makes new warning log.
160     void warn
161         ( string file = __FILE__, size_t line = __LINE__, string func = __FUNCTION__, Args... )
162         ( Args args )
163     {
164         writeLog!( file, line, func )( LogLevel.Warn, args );
165     }
166     /// Makes new error log.
167     void error
168         ( string file = __FILE__, size_t line = __LINE__, string func = __FUNCTION__, Args... )
169         ( Args args )
170     {
171         writeLog!( file, line, func )( LogLevel.Error, args );
172     }
173     /// Makes new fatal log.
174     void fatal
175         ( string file = __FILE__, size_t line = __LINE__, string func = __FUNCTION__, Args... )
176         ( Args args )
177     {
178         writeLog!( file, line, func )( LogLevel.Fatal, args );
179     }
180 }
181 
182 /// Alias for the instance of W4dLogger.
183 alias Log = W4dLogger.instance;