1 /*
2  * Geario - A cross-platform abstraction library with asynchronous I/O.
3  *
4  * Copyright (C) 2021-2022 Kerisy.com
5  *
6  * Website: https://www.kerisy.com
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module geario.logging.ConsoleLogger;
13 
14 // import geario.util.ThreadHelper;
15 import geario.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 geario.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 = "GEAR";
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 class ConsoleLogger {
119     private __gshared LogLevel g_logLevel = LogLevel.Trace;
120     private enum traceLevel = toString(LogLevel.Trace);
121     private enum infoLevel = toString(LogLevel.Info);
122     private enum warningLevel = toString(LogLevel.Warning);
123     private enum errorLevel = toString(LogLevel.Error);
124     private enum fatalLevel = toString(LogLevel.Fatal);
125     private enum offlLevel = toString(LogLevel.Off);
126 
127     static void SetLogLevel(LogLevel level) {
128         g_logLevel = level;
129     }
130 
131     static void Trace(string file = __FILE__, size_t line = __LINE__,
132             string func = __FUNCTION__, A...)(lazy A args) nothrow {
133         WriteFormatColor(LogLevel.Trace, Layout!(file, line, func)(LogFormat(args), traceLevel));
134     }
135 
136     static void Tracef(string file = __FILE__, size_t line = __LINE__,
137             string func = __FUNCTION__, A...)(lazy A args) nothrow {
138         WriteFormatColor(LogLevel.Trace, Layout!(file, line, func)(LogFormatf(args), traceLevel));
139     }
140 
141     static void Info(string file = __FILE__, size_t line = __LINE__,
142             string func = __FUNCTION__, A...)(lazy A args) nothrow {
143         WriteFormatColor(LogLevel.Info, Layout!(file, line, func)(LogFormat(args), infoLevel));
144     }
145 
146     static void Infof(string file = __FILE__, size_t line = __LINE__,
147             string func = __FUNCTION__, A...)(lazy A args) nothrow {
148         WriteFormatColor(LogLevel.Info, Layout!(file, line, func)(LogFormatf(args), infoLevel));
149     }
150 
151     static void Warning(string file = __FILE__, size_t line = __LINE__,
152             string func = __FUNCTION__, A...)(lazy A args) nothrow {
153         WriteFormatColor(LogLevel.Warning, Layout!(file, line,
154                 func)(LogFormat(args), warningLevel));
155     }
156 
157     static void Warningf(string file = __FILE__, size_t line = __LINE__,
158             string func = __FUNCTION__, A...)(lazy A args) nothrow {
159         WriteFormatColor(LogLevel.Warning, Layout!(file, line,
160                 func)(LogFormatf(args), warningLevel));
161     }
162 
163     static void Error(string file = __FILE__, size_t line = __LINE__,
164             string func = __FUNCTION__, A...)(lazy A args) nothrow {
165         WriteFormatColor(LogLevel.Error, Layout!(file, line, func)(LogFormat(args), errorLevel));
166     }
167 
168     static void Errorf(string file = __FILE__, size_t line = __LINE__,
169             string func = __FUNCTION__, A...)(lazy A args) nothrow {
170         WriteFormatColor(LogLevel.Error, Layout!(file, line, func)(LogFormatf(args), errorLevel));
171     }
172 
173     static void Fatal(string file = __FILE__, size_t line = __LINE__,
174             string func = __FUNCTION__, A...)(lazy A args) nothrow {
175         WriteFormatColor(LogLevel.Fatal, Layout!(file, line, func)(LogFormat(args), fatalLevel));
176     }
177 
178     static void Fatalf(string file = __FILE__, size_t line = __LINE__,
179             string func = __FUNCTION__, A...)(lazy A args) nothrow {
180         WriteFormatColor(LogLevel.Fatal, Layout!(file, line, func)(LogFormatf(args), fatalLevel));
181     }
182 
183     private static string LogFormatf(A...)(A args) {
184         Appender!string buffer;
185         formattedWrite(buffer, args);
186         return buffer.data;
187     }
188 
189     private static string LogFormat(A...)(A args) {
190         auto w = appender!string();
191         foreach (arg; args) {
192             alias A = typeof(arg);
193             static if (isAggregateType!A || is(A == enum)) {
194                 import std.format : formattedWrite;
195 
196                 formattedWrite(w, "%s", arg);
197             } else static if (isSomeString!A) {
198                 put(w, arg);
199             } else static if (isIntegral!A) {
200                 import std.conv : toTextRange;
201 
202                 toTextRange(arg, w);
203             } else static if (isBoolean!A) {
204                 put(w, arg ? "true" : "false");
205             } else static if (isSomeChar!A) {
206                 put(w, arg);
207             } else {
208                 import std.format : formattedWrite;
209 
210                 // Most general case
211                 formattedWrite(w, "%s", arg);
212             }
213         }
214         return w.data;
215     }
216 
217     private static string Layout(string file = __FILE__, size_t line = __LINE__,
218             string func = __FUNCTION__)(string msg, string level) {
219         enum lineNum = std.conv.to!string(line);
220         string time_prior = Clock.currTime.toString();
221         string tid = std.conv.to!string(cast(size_t)GetTid());
222 
223         // writeln("fullname: ",func);
224         string fun = func;
225         ptrdiff_t index = lastIndexOf(func, '.');
226         if (index != -1) {
227             if (func[index - 1] != ')') {
228                 ptrdiff_t idx = lastIndexOf(func, '.', index);
229                 if (idx != -1)
230                     index = idx;
231             }
232             fun = func[index + 1 .. $];
233         }
234 
235         return time_prior ~ " | " ~ tid ~ " | " ~ level ~ " | " ~ fun ~ " | " ~ msg
236             ~ " | " ~ file ~ ":" ~ lineNum;
237     }
238 
239     // private static string defaultLayout(string context, string msg, string level)
240     // {
241     //     string time_prior = Clock.currTime.toString();
242     //     string tid = std.conv.to!string(GetTid());
243 
244     //     return time_prior ~ " | " ~ tid ~ " | " ~ level ~ context ~ msg;
245     // }
246 
247     static string toString(LogLevel level) nothrow {
248         string r;
249         final switch (level) with (LogLevel) {
250         case Trace:
251             r = "Trace";
252             break;
253         case Info:
254             r = "Info";
255             break;
256         case Warning:
257             r = "Warning";
258             break;
259         case Error:
260             r = "Error";
261             break;
262         case Fatal:
263             r = "Fatal";
264             break;
265         case Off:
266             r = "Off";
267             break;
268         }
269         return r;
270     }
271 
272     private static void WriteFormatColor(LogLevel level, lazy string msg) nothrow {
273         if (level < g_logLevel)
274             return;
275 
276         version (Posix) {
277             version (Android) {
278                 string prior_color;
279                 android_LogPriority logPrioity = android_LogPriority.ANDROID_LOG_INFO;
280                 switch (level) with (LogLevel) {
281                 case Error:
282                 case Fatal:
283                     prior_color = PRINT_COLOR_RED;
284                     logPrioity = android_LogPriority.ANDROID_LOG_ERROR;
285                     break;
286                 case Warning:
287                     prior_color = PRINT_COLOR_YELLOW;
288                     logPrioity = android_LogPriority.ANDROID_LOG_WARN;
289                     break;
290                 case Info:
291                     prior_color = PRINT_COLOR_GREEN;
292                     break;
293                 default:
294                     prior_color = string.init;
295                 }
296 
297                 try {
298                     __android_log_write(logPrioity,
299                             LOG_TAG, toStringz(prior_color ~ msg ~ PRINT_COLOR_NONE));
300                 } catch(Exception ex) {
301                     collectException( {
302                         Write(PRINT_COLOR_RED); 
303                         Write(ex); 
304                         writeln(PRINT_COLOR_NONE); 
305                     }());
306                 }
307 
308             } else {
309                 string prior_color;
310                 switch (level) with (LogLevel) {
311                 case Error:
312                 case Fatal:
313                     prior_color = PRINT_COLOR_RED;
314                     break;
315                 case Warning:
316                     prior_color = PRINT_COLOR_YELLOW;
317                     break;
318                 case Info:
319                     prior_color = PRINT_COLOR_GREEN;
320                     break;
321                 default:
322                     prior_color = string.init;
323                 }
324                 try {
325                     writeln(prior_color ~ msg ~ PRINT_COLOR_NONE);
326                 } catch(Exception ex) {
327                     collectException( {
328                         write(PRINT_COLOR_RED); 
329                         write(ex); 
330                         writeln(PRINT_COLOR_NONE); 
331                     }());
332                 }
333             }
334 
335         } else version (Windows) {
336             enum defaultColor = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
337 
338             ushort color;
339             switch (level) with (LogLevel) {
340             case Error:
341             case Fatal:
342                 color = FOREGROUND_RED;
343                 break;
344             case Warning:
345                 color = FOREGROUND_GREEN | FOREGROUND_RED;
346                 break;
347             case Info:
348                 color = FOREGROUND_GREEN;
349                 break;
350             default:
351                 color = defaultColor;
352             }
353 
354             ConsoleHelper.writeWithAttribute(msg, color);
355         } else {
356             assert(false, "Unsupported OS.");
357         }
358     }
359 }
360 
361 alias Trace = ConsoleLogger.Trace;
362 alias Tracef = ConsoleLogger.Tracef;
363 alias Info = ConsoleLogger.Info;
364 alias Infof = ConsoleLogger.Infof;
365 alias Warning = ConsoleLogger.Warning;
366 alias Warningf = ConsoleLogger.Warningf;
367 alias Error = ConsoleLogger.Error;
368 alias Errorf = ConsoleLogger.Errorf;
369 // alias Critical = ConsoleLogger.Critical;
370 // alias criticalf = ConsoleLogger.criticalf;
371 
372 // alias LogDebug = Trace;
373 // alias LogDebugf = Tracef;
374 // alias LogInfo = Info;
375 // alias LogInfof = Infof;
376 // alias LogWarning = Warning;
377 // alias LogWarningf = Warningf;
378 // alias LogError = error;
379 // alias LogErrorf = errorf;