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.conv;
21 import std.datetime;
22 import std.exception;
23 import std.format;
24 import std.range;
25 import std.regex;
26 import std.stdio;
27 import std.string;
28 import std.typecons;
29 import std.traits;
30 
31 // ThreadID getTid()
32 // {
33 //     return Thread.getThis.id;
34 // }
35 
36 version (Windows) {
37     import core.sys.windows.wincon;
38     import core.sys.windows.winbase;
39     import core.sys.windows.windef;
40     import hunt.system.WindowsHelper;
41 
42 }
43 
44 version (Posix) {
45     enum PRINT_COLOR_NONE = "\033[m";
46     enum PRINT_COLOR_RED = "\033[0;32;31m";
47     enum PRINT_COLOR_GREEN = "\033[0;32;32m";
48     enum PRINT_COLOR_YELLOW = "\033[1;33m";
49 }
50 
51 version (Android) {
52     import core.stdc.stdarg : va_end, va_list, va_start;
53     import core.sys.posix.sys.types;
54 
55     enum {
56         AASSET_MODE_UNKNOWN,
57         AASSET_MODE_RANDOM,
58         AASSET_MODE_STREAMING,
59         AASSET_MODE_BUFFER
60     }
61 
62     enum android_LogPriority {
63         ANDROID_LOG_UNKNOWN,
64         ANDROID_LOG_DEFAULT,
65         ANDROID_LOG_VERBOSE,
66         ANDROID_LOG_DEBUG,
67         ANDROID_LOG_INFO,
68         ANDROID_LOG_WARN,
69         ANDROID_LOG_ERROR,
70         ANDROID_LOG_FATAL,
71         ANDROID_LOG_SILENT
72     }
73 
74     enum LOG_TAG = "HUNT";
75 
76     // dfmt off
77     extern (C):
78     @system:
79     nothrow:
80     @nogc:
81     // dfmt on
82 
83     struct AAssetManager;
84     struct AAssetDir;
85     struct AAsset;
86 
87     AAssetDir* AAssetManager_openDir(AAssetManager* mgr, const(char)* dirName);
88     AAsset* AAssetManager_open(AAssetManager* mgr, const(char)* filename, int mode);
89     const(char)* AAssetDir_getNextFileName(AAssetDir* assetDir);
90     void AAssetDir_rewind(AAssetDir* assetDir);
91     void AAssetDir_close(AAssetDir* assetDir);
92     int AAsset_read(AAsset* asset, void* buf, size_t count);
93     off_t AAsset_seek(AAsset* asset, off_t offset, int whence);
94     void AAsset_close(AAsset* asset);
95     const(void)* AAsset_getBuffer(AAsset* asset);
96     off_t AAsset_getLength(AAsset* asset);
97     off_t AAsset_getRemainingLength(AAsset* asset);
98     int AAsset_openFileDescriptor(AAsset* asset, off_t* outStart, off_t* outLength);
99     int AAsset_isAllocated(AAsset* asset);
100 
101     int __android_log_write(int prio, const(char)* tag, const(char)* text);
102     int __android_log_print(int prio, const(char)* tag, const(char)* fmt, ...);
103     int __android_log_vprint(int prio, const(char)* tag, const(char)* fmt, va_list ap);
104     void __android_log_assert(const(char)* cond, const(char)* tag, const(char)* fmt, ...);
105 
106 }
107 
108 enum LogLevel {
109     Trace = 0,
110     Info = 1,
111     Warning = 2,
112     Error = 3,
113     Fatal = 4,
114     Off = 5
115 }
116 
117 /**
118 */
119 class ConsoleLogger {
120     private __gshared LogLevel g_logLevel = LogLevel.Trace;
121     private enum traceLevel = toString(LogLevel.Trace);
122     private enum infoLevel = toString(LogLevel.Info);
123     private enum warningLevel = toString(LogLevel.Warning);
124     private enum errorLevel = toString(LogLevel.Error);
125     private enum fatalLevel = toString(LogLevel.Fatal);
126     private enum offlLevel = toString(LogLevel.Off);
127 
128     static void setLogLevel(LogLevel level) {
129         g_logLevel = level;
130     }
131 
132     static void trace(string file = __FILE__, size_t line = __LINE__,
133             string func = __FUNCTION__, A...)(lazy A args) nothrow {
134         writeFormatColor(LogLevel.Trace, layout!(file, line, func)(logFormat(args), traceLevel));
135     }
136 
137     static void tracef(string file = __FILE__, size_t line = __LINE__,
138             string func = __FUNCTION__, A...)(lazy A args) nothrow {
139         writeFormatColor(LogLevel.Trace, layout!(file, line, func)(logFormatf(args), traceLevel));
140     }
141 
142     static void info(string file = __FILE__, size_t line = __LINE__,
143             string func = __FUNCTION__, A...)(lazy A args) nothrow {
144         writeFormatColor(LogLevel.Info, layout!(file, line, func)(logFormat(args), infoLevel));
145     }
146 
147     static void infof(string file = __FILE__, size_t line = __LINE__,
148             string func = __FUNCTION__, A...)(lazy A args) nothrow {
149         writeFormatColor(LogLevel.Info, layout!(file, line, func)(logFormatf(args), infoLevel));
150     }
151 
152     static void warning(string file = __FILE__, size_t line = __LINE__,
153             string func = __FUNCTION__, A...)(lazy A args) nothrow {
154         writeFormatColor(LogLevel.Warning, layout!(file, line,
155                 func)(logFormat(args), warningLevel));
156     }
157 
158     static void warningf(string file = __FILE__, size_t line = __LINE__,
159             string func = __FUNCTION__, A...)(lazy A args) nothrow {
160         writeFormatColor(LogLevel.Warning, layout!(file, line,
161                 func)(logFormatf(args), warningLevel));
162     }
163 
164     static void error(string file = __FILE__, size_t line = __LINE__,
165             string func = __FUNCTION__, A...)(lazy A args) nothrow {
166         writeFormatColor(LogLevel.Error, layout!(file, line, func)(logFormat(args), errorLevel));
167     }
168 
169     static void errorf(string file = __FILE__, size_t line = __LINE__,
170             string func = __FUNCTION__, A...)(lazy A args) nothrow {
171         writeFormatColor(LogLevel.Error, layout!(file, line, func)(logFormatf(args), errorLevel));
172     }
173 
174     static void fatal(string file = __FILE__, size_t line = __LINE__,
175             string func = __FUNCTION__, A...)(lazy A args) nothrow {
176         writeFormatColor(LogLevel.Fatal, layout!(file, line, func)(logFormat(args), fatalLevel));
177     }
178 
179     static void fatalf(string file = __FILE__, size_t line = __LINE__,
180             string func = __FUNCTION__, A...)(lazy A args) nothrow {
181         writeFormatColor(LogLevel.Fatal, layout!(file, line, func)(logFormatf(args), fatalLevel));
182     }
183 
184     private static string logFormatf(A...)(A args) {
185         Appender!string buffer;
186         formattedWrite(buffer, args);
187         return buffer.data;
188     }
189 
190     private static string logFormat(A...)(A args) {
191         auto w = appender!string();
192         foreach (arg; args) {
193             alias A = typeof(arg);
194             static if (isAggregateType!A || is(A == enum)) {
195                 import std.format : formattedWrite;
196 
197                 formattedWrite(w, "%s", arg);
198             } else static if (isSomeString!A) {
199                 put(w, arg);
200             } else static if (isIntegral!A) {
201                 import std.conv : toTextRange;
202 
203                 toTextRange(arg, w);
204             } else static if (isBoolean!A) {
205                 put(w, arg ? "true" : "false");
206             } else static if (isSomeChar!A) {
207                 put(w, arg);
208             } else {
209                 import std.format : formattedWrite;
210 
211                 // Most general case
212                 formattedWrite(w, "%s", arg);
213             }
214         }
215         return w.data;
216     }
217 
218     private static string layout(string file = __FILE__, size_t line = __LINE__,
219             string func = __FUNCTION__)(string msg, string level) {
220         enum lineNum = std.conv.to!string(line);
221         string time_prior = Clock.currTime.toString();
222         string tid = std.conv.to!string(cast(size_t)getTid());
223 
224         // writeln("fullname: ",func);
225         string fun = func;
226         ptrdiff_t index = lastIndexOf(func, '.');
227         if (index != -1) {
228             if (func[index - 1] != ')') {
229                 ptrdiff_t idx = lastIndexOf(func, '.', index);
230                 if (idx != -1)
231                     index = idx;
232             }
233             fun = func[index + 1 .. $];
234         }
235 
236         return time_prior ~ " | " ~ tid ~ " | " ~ level ~ " | " ~ fun ~ " | " ~ msg
237             ~ " | " ~ file ~ ":" ~ lineNum;
238     }
239 
240     // private static string defaultLayout(string context, string msg, string level)
241     // {
242     //     string time_prior = Clock.currTime.toString();
243     //     string tid = std.conv.to!string(getTid());
244 
245     //     return time_prior ~ " | " ~ tid ~ " | " ~ level ~ context ~ msg;
246     // }
247 
248     static string toString(LogLevel level) nothrow {
249         string r;
250         final switch (level) with (LogLevel) {
251         case Trace:
252             r = "trace";
253             break;
254         case Info:
255             r = "info";
256             break;
257         case Warning:
258             r = "warning";
259             break;
260         case Error:
261             r = "error";
262             break;
263         case Fatal:
264             r = "fatal";
265             break;
266         case Off:
267             r = "off";
268             break;
269         }
270         return r;
271     }
272 
273     private static void writeFormatColor(LogLevel level, lazy string msg) nothrow {
274         if (level < g_logLevel)
275             return;
276 
277         version (Posix) {
278             version (Android) {
279                 string prior_color;
280                 android_LogPriority logPrioity = android_LogPriority.ANDROID_LOG_INFO;
281                 switch (level) with (LogLevel) {
282                 case Error:
283                 case Fatal:
284                     prior_color = PRINT_COLOR_RED;
285                     logPrioity = android_LogPriority.ANDROID_LOG_ERROR;
286                     break;
287                 case Warning:
288                     prior_color = PRINT_COLOR_YELLOW;
289                     logPrioity = android_LogPriority.ANDROID_LOG_WARN;
290                     break;
291                 case Info:
292                     prior_color = PRINT_COLOR_GREEN;
293                     break;
294                 default:
295                     prior_color = string.init;
296                 }
297 
298                 try {
299                     __android_log_write(logPrioity,
300                             LOG_TAG, toStringz(prior_color ~ msg ~ PRINT_COLOR_NONE));
301                 } catch(Exception ex) {
302                     collectException( {
303                         write(PRINT_COLOR_RED); 
304                         write(ex); 
305                         writeln(PRINT_COLOR_NONE); 
306                     }());
307                 }
308 
309             } else {
310                 string prior_color;
311                 switch (level) with (LogLevel) {
312                 case Error:
313                 case Fatal:
314                     prior_color = PRINT_COLOR_RED;
315                     break;
316                 case Warning:
317                     prior_color = PRINT_COLOR_YELLOW;
318                     break;
319                 case Info:
320                     prior_color = PRINT_COLOR_GREEN;
321                     break;
322                 default:
323                     prior_color = string.init;
324                 }
325                 try {
326                     writeln(prior_color ~ msg ~ PRINT_COLOR_NONE);
327                 } catch(Exception ex) {
328                     collectException( {
329                         write(PRINT_COLOR_RED); 
330                         write(ex); 
331                         writeln(PRINT_COLOR_NONE); 
332                     }());
333                 }
334             }
335 
336         } else version (Windows) {
337             enum defaultColor = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
338 
339             ushort color;
340             switch (level) with (LogLevel) {
341             case Error:
342             case Fatal:
343                 color = FOREGROUND_RED;
344                 break;
345             case Warning:
346                 color = FOREGROUND_GREEN | FOREGROUND_RED;
347                 break;
348             case Info:
349                 color = FOREGROUND_GREEN;
350                 break;
351             default:
352                 color = defaultColor;
353             }
354 
355             ConsoleHelper.writeWithAttribute(msg, color);
356         } else {
357             assert(false, "Unsupported OS.");
358         }
359     }
360 }
361 
362 alias trace = ConsoleLogger.trace;
363 alias tracef = ConsoleLogger.tracef;
364 alias info = ConsoleLogger.info;
365 alias infof = ConsoleLogger.infof;
366 alias warning = ConsoleLogger.warning;
367 alias warningf = ConsoleLogger.warningf;
368 alias error = ConsoleLogger.error;
369 alias errorf = ConsoleLogger.errorf;
370 // alias critical = ConsoleLogger.critical;
371 // alias criticalf = ConsoleLogger.criticalf;
372 
373 alias logDebug = trace;
374 alias logDebugf = tracef;
375 alias logInfo = info;
376 alias logInfof = infof;
377 alias logWarning = warning;
378 alias logWarningf = warningf;
379 alias logError = error;
380 alias logErrorf = errorf;