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;