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