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;