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