1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.logging.ConsoleLogger;
13 
14 import hunt.concurrency.thread.Helper;
15 
16 import core.stdc.stdlib;
17 import core.runtime;
18 import core.thread;
19 
20 import std.stdio;
21 import std.datetime;
22 import std.format;
23 import std.range;
24 import std.conv;
25 import std.regex;
26 import std.typecons;
27 import std.traits;
28 import std.string;
29 
30 // ThreadID getTid()
31 // {
32 //     return Thread.getThis.id;
33 // }
34 
35 version (Windows) {
36     import core.sys.windows.wincon;
37     import core.sys.windows.winbase;
38     import core.sys.windows.windef;
39 
40     private __gshared HANDLE g_hout;
41     
42     shared static this() {
43         g_hout = GetStdHandle(STD_OUTPUT_HANDLE);
44         SetConsoleTextAttribute(g_hout, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE);
45     }
46 
47     void resetConsoleColor() {
48         SetConsoleTextAttribute(g_hout, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE);
49     }
50 }
51 
52 version (Posix) {
53     enum PRINT_COLOR_NONE = "\033[m";
54     enum PRINT_COLOR_RED = "\033[0;32;31m";
55     enum PRINT_COLOR_GREEN = "\033[0;32;32m";
56     enum PRINT_COLOR_YELLOW = "\033[1;33m";
57 }
58 
59 enum LogLevel {
60     Trace = 0,
61     Info = 1,
62     Warning = 2,
63     Error = 3,
64     Fatal = 4,
65     Off = 5
66 }
67 
68 /**
69 */
70 class ConsoleLogger {
71     private __gshared LogLevel g_logLevel = LogLevel.Trace;
72     private enum traceLevel = toString(LogLevel.Trace);
73     private enum infoLevel = toString(LogLevel.Info);
74     private enum warningLevel = toString(LogLevel.Warning);
75     private enum errorLevel = toString(LogLevel.Error);
76     private enum fatalLevel = toString(LogLevel.Fatal);
77     private enum offlLevel = toString(LogLevel.Off);
78 
79     static void setLogLevel(LogLevel level) {
80         g_logLevel = level;
81     }
82 
83     static void trace(string file = __FILE__, size_t line = __LINE__,
84             string func = __FUNCTION__, A...)(lazy A args) nothrow {
85         writeFormatColor(LogLevel.Trace, layout!(file, line, func)(logFormat(args), traceLevel));
86     }
87 
88     static void tracef(string file = __FILE__, size_t line = __LINE__,
89             string func = __FUNCTION__, A...)(lazy A args) nothrow {
90         writeFormatColor(LogLevel.Trace, layout!(file, line, func)(logFormatf(args), traceLevel));
91     }
92 
93     static void info(string file = __FILE__, size_t line = __LINE__,
94             string func = __FUNCTION__, A...)(lazy A args) nothrow {
95         writeFormatColor(LogLevel.Info, layout!(file, line, func)(logFormat(args), infoLevel));
96     }
97 
98     static void infof(string file = __FILE__, size_t line = __LINE__,
99             string func = __FUNCTION__, A...)(lazy A args) nothrow {
100         writeFormatColor(LogLevel.Info, layout!(file, line, func)(logFormatf(args), infoLevel));
101     }
102 
103     static void warning(string file = __FILE__, size_t line = __LINE__,
104             string func = __FUNCTION__, A...)(lazy A args) nothrow {
105         writeFormatColor(LogLevel.Warning, layout!(file, line,
106                 func)(logFormat(args), warningLevel));
107     }
108 
109     static void warningf(string file = __FILE__, size_t line = __LINE__,
110             string func = __FUNCTION__, A...)(lazy A args) nothrow {
111         writeFormatColor(LogLevel.Warning, layout!(file, line,
112                 func)(logFormatf(args), warningLevel));
113     }
114 
115     static void error(string file = __FILE__, size_t line = __LINE__,
116             string func = __FUNCTION__, A...)(lazy A args) nothrow {
117         writeFormatColor(LogLevel.Error, layout!(file, line, func)(logFormat(args), errorLevel));
118     }
119 
120     static void errorf(string file = __FILE__, size_t line = __LINE__,
121             string func = __FUNCTION__, A...)(lazy A args) nothrow {
122         writeFormatColor(LogLevel.Error, layout!(file, line, func)(logFormatf(args), errorLevel));
123     }
124 
125     static void fatal(string file = __FILE__, size_t line = __LINE__,
126             string func = __FUNCTION__, A...)(lazy A args) nothrow {
127         writeFormatColor(LogLevel.Fatal, layout!(file, line, func)(logFormat(args), fatalLevel));
128     }
129 
130     static void fatalf(string file = __FILE__, size_t line = __LINE__,
131             string func = __FUNCTION__, A...)(lazy A args) nothrow {
132         writeFormatColor(LogLevel.Fatal, layout!(file, line, func)(logFormatf(args), fatalLevel));
133     }
134 
135     private static string logFormatf(A...)(A args) {
136         Appender!string buffer;
137         formattedWrite(buffer, args);
138         return buffer.data;
139     }
140 
141     private static string logFormat(A...)(A args) {
142         auto w = appender!string();
143         foreach (arg; args) {
144             alias A = typeof(arg);
145             static if (isAggregateType!A || is(A == enum)) {
146                 import std.format : formattedWrite;
147 
148                 formattedWrite(w, "%s", arg);
149             }
150             else static if (isSomeString!A) {
151                 put(w, arg);
152             }
153             else static if (isIntegral!A) {
154                 import std.conv : toTextRange;
155 
156                 toTextRange(arg, w);
157             }
158             else static if (isBoolean!A) {
159                 put(w, arg ? "true" : "false");
160             }
161             else static if (isSomeChar!A) {
162                 put(w, arg);
163             }
164             else {
165                 import std.format : formattedWrite;
166 
167                 // Most general case
168                 formattedWrite(w, "%s", arg);
169             }
170         }
171         return w.data;
172     }
173 
174     private static string layout(string file = __FILE__, size_t line = __LINE__,
175             string func = __FUNCTION__)(string msg, string level) {
176         enum lineNum = std.conv.to!string(line);
177         string time_prior = Clock.currTime.toString();
178         string tid = std.conv.to!string(getTid());
179 
180         // writeln(func);
181         string fun = func;
182         ptrdiff_t index = lastIndexOf(func, '.');
183         if (index != -1) {
184             if(func[index -1] != ')') {
185                 ptrdiff_t idx = lastIndexOf(func, '.', index);
186                 if (idx != -1)
187                     index = idx;
188             }
189             fun = func[index + 1 .. $];
190         }
191 
192         return time_prior ~ " | " ~ tid ~ " | " ~ level ~ " | " ~ fun ~ " | " ~ msg
193             ~ " | " ~ file ~ ":" ~ lineNum;
194     }
195 
196     // private static string defaultLayout(string context, string msg, string level)
197     // {
198     //     string time_prior = Clock.currTime.toString();
199     //     string tid = std.conv.to!string(getTid());
200 
201     //     return time_prior ~ " | " ~ tid ~ " | " ~ level ~ context ~ msg;
202     // }
203 
204     static string toString(LogLevel level) nothrow {
205         string r;
206         final switch (level) with (LogLevel) {
207         case Trace:
208             r = "trace";
209             break;
210         case Info:
211             r = "info";
212             break;
213         case Warning:
214             r = "warning";
215             break;
216         case Error:
217             r = "error";
218             break;
219         case Fatal:
220             r = "fatal";
221             break;
222         case Off:
223             r = "off";
224             break;
225         }
226         return r;
227     }
228 
229     private static void writeFormatColor(LogLevel level, lazy string msg) nothrow {
230         if (level < g_logLevel)
231             return;
232 
233         version (Posix) {
234             string prior_color;
235             switch (level) with (LogLevel) {
236             case Error:
237             case Fatal:
238                 prior_color = PRINT_COLOR_RED;
239                 break;
240             case Warning:
241                 prior_color = PRINT_COLOR_YELLOW;
242                 break;
243             case Info:
244                 prior_color = PRINT_COLOR_GREEN;
245                 break;
246             default:
247                 prior_color = string.init;
248             }
249             import std.exception;
250             collectException(writeln(prior_color ~ msg ~ PRINT_COLOR_NONE));
251             
252         }
253         else version (Windows) {
254             import std.windows.charset;
255             import core.stdc.stdio;
256 
257             enum defaultColor = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
258 
259             ushort color;
260             switch (level) with (LogLevel) {
261             case Error:
262             case Fatal:
263                 color = FOREGROUND_RED;
264                 break;
265             case Warning:
266                 color = FOREGROUND_GREEN | FOREGROUND_RED;
267                 break;
268             case Info:
269                 color = FOREGROUND_GREEN;
270                 break;
271             default:
272                 color = defaultColor;
273             }
274 
275             SetConsoleTextAttribute(g_hout, color);
276             
277             try {
278                 printf("%s\n", toMBSz(msg));
279             } catch (Exception) {
280             }
281 
282             if (color != defaultColor)
283                 SetConsoleTextAttribute(g_hout, defaultColor);
284         }
285     }
286 }
287 
288 
289 alias trace = ConsoleLogger.trace;
290 alias tracef = ConsoleLogger.tracef;
291 alias info = ConsoleLogger.info;
292 alias infof = ConsoleLogger.infof;
293 alias warning = ConsoleLogger.warning;
294 alias warningf = ConsoleLogger.warningf;
295 alias error = ConsoleLogger.error;
296 alias errorf = ConsoleLogger.errorf;
297 // alias critical = ConsoleLogger.critical;
298 // alias criticalf = ConsoleLogger.criticalf;
299 
300 alias logDebug = trace;
301 alias logDebugf = tracef;
302 alias logInfo = info;
303 alias logInfof = infof;
304 alias logWarning = warning;
305 alias logWarningf = warningf;
306 alias logError = error;
307 alias logErrorf = errorf;