1 // Written in the D programming language. 2 3 /** 4 This module provides logging utilities. 5 6 Use Log class static methods. 7 8 Synopsis: 9 10 ---- 11 import dlangui.core.logger; 12 13 // setup: 14 15 // use stderror for logging 16 setStderrLogger(); 17 // set log level 18 setLogLevel(LogLevel.Debug); 19 20 // usage: 21 22 // log debug message 23 Log.d("mouse clicked at ", x, ",", y); 24 // or with format string: 25 Log.fd("mouse clicked at %d,%d", x, y); 26 // log error message 27 Log.e("exception while reading file", e); 28 ---- 29 30 31 For Android, set log tag instead of setXXXLogger: 32 33 ---- 34 Log.setLogTag("myApp"); 35 ---- 36 37 Copyright: Vadim Lopatin, 2014 38 License: Boost License 1.0 39 Authors: Vadim Lopatin, coolreader.org@gmail.com 40 */ 41 module dlangui.core.logger; 42 43 import std.stdio; 44 import std.datetime : SysTime, Clock; 45 import core.sync.mutex; 46 47 version (Android) { 48 import android.log; 49 } 50 51 /// Log levels 52 enum LogLevel : int { 53 /// Fatal error, cannot resume 54 Fatal, 55 /// Error 56 Error, 57 /// Warning 58 Warn, 59 /// Informational message 60 Info, 61 /// Debug message 62 Debug, 63 /// Tracing message 64 Trace 65 } 66 67 /// Returns timestamp in milliseconds since 1970 UTC similar to Java System.currentTimeMillis() 68 @property long currentTimeMillis() { 69 static import std.datetime; 70 return std.datetime.Clock.currStdTime / 10000; 71 } 72 73 /** 74 75 Logging utilities 76 77 Setup example: 78 ---- 79 // use stderror for logging 80 setStderrLogger(); 81 // set log level 82 setLogLevel(LogLeve.Debug); 83 ---- 84 85 Logging example: 86 ---- 87 // log debug message 88 Log.d("mouse clicked at ", x, ",", y); 89 // log error message 90 Log.e("exception while reading file", e); 91 ---- 92 93 */ 94 95 private auto std_io_err_helper(alias v)() 96 { 97 static if (__VERSION__ < 2075) 98 return &v; 99 else 100 return &v(); 101 } 102 103 class Log { 104 static __gshared private LogLevel logLevel = LogLevel.Info; 105 static __gshared private std.stdio.File * logFile = null; 106 static __gshared private Mutex _mutex = null; 107 108 static public @property Mutex mutex() { 109 if (_mutex is null) 110 _mutex = new Mutex(); 111 return _mutex; 112 } 113 114 /// Redirects output to stdout 115 static public void setStdoutLogger() { 116 synchronized(mutex) { 117 logFile = std_io_err_helper!stdout; 118 } 119 } 120 121 /// Redirects output to stderr 122 static public void setStderrLogger() { 123 synchronized(mutex) { 124 logFile = std_io_err_helper!stderr; 125 } 126 } 127 128 /// Redirects output to file 129 static public void setFileLogger(File * file) { 130 synchronized(mutex) { 131 if (logFile !is null && *logFile != stdout && *logFile != stderr) { 132 logFile.close(); 133 destroy(logFile); 134 logFile = null; 135 } 136 logFile = file; 137 if (logFile !is null) 138 logFile.writeln("DlangUI log file"); 139 } 140 } 141 142 /// Sets log level (one of LogLevel) 143 static public void setLogLevel(LogLevel level) { 144 synchronized(mutex) { 145 logLevel = level; 146 i("Log level changed to ", level); 147 } 148 } 149 150 /// returns true if messages for level are enabled 151 static public bool isLogLevelEnabled(LogLevel level) { 152 return logLevel >= level; 153 } 154 155 /// returns true if debug log level is enabled 156 @property static public bool debugEnabled() { 157 return logLevel >= LogLevel.Debug; 158 } 159 160 /// returns true if trace log level is enabled 161 @property static public bool traceEnabled() { 162 return logLevel >= LogLevel.Trace; 163 } 164 165 /// returns true if warn log level is enabled 166 @property static public bool warnEnabled() { 167 return logLevel >= LogLevel.Warn; 168 } 169 170 /// Log level to name helper function 171 static public string logLevelName(LogLevel level) { 172 switch (level) with(LogLevel) 173 { 174 case Fatal: return "F"; 175 case Error: return "E"; 176 case Warn: return "W"; 177 case Info: return "I"; 178 case Debug: return "D"; 179 case Trace: return "V"; 180 default: return "?"; 181 } 182 } 183 version (Android) { 184 static android_LogPriority toAndroidLogPriority(LogLevel level) { 185 switch (level) with (LogLevel) { 186 /// Fatal error, cannot resume 187 case Fatal: 188 return android_LogPriority.ANDROID_LOG_FATAL; 189 /// Error 190 case Error: 191 return android_LogPriority.ANDROID_LOG_ERROR; 192 /// Warning 193 case Warn: 194 return android_LogPriority.ANDROID_LOG_WARN; 195 /// Informational message 196 case Info: 197 return android_LogPriority.ANDROID_LOG_INFO; 198 /// Debug message 199 case Debug: 200 return android_LogPriority.ANDROID_LOG_DEBUG; 201 /// Tracing message 202 case Trace: 203 default: 204 return android_LogPriority.ANDROID_LOG_VERBOSE; 205 } 206 } 207 } 208 /// Log message with arbitrary log level 209 static public void log(S...)(LogLevel level, S args) { 210 if (logLevel >= level) { 211 version (Android) { 212 import std.format; 213 import std.string : toStringz; 214 import std.format; 215 import std.conv : to; 216 char[] msg; 217 foreach(arg; args) { 218 msg ~= to!string(arg); 219 } 220 msg ~= cast(char)0; 221 __android_log_write(toAndroidLogPriority(level), ANDROID_LOG_TAG, msg.ptr); 222 } else { 223 if (logFile !is null && logFile.isOpen) { 224 SysTime ts = Clock.currTime(); 225 logFile.writef("%04d-%02d-%02d %02d:%02d:%02d.%03d %s ", ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.fracSecs.split!("msecs").msecs, logLevelName(level)); 226 logFile.writeln(args); 227 logFile.flush(); 228 } 229 } 230 } 231 } 232 /// Log message with arbitrary log level with format string 233 static public void logf(S...)(LogLevel level, string fmt, S args) { 234 if (logLevel >= level) { 235 version (Android) { 236 import std.string : toStringz; 237 import std.format; 238 string msg = fmt.format(args); 239 __android_log_write(toAndroidLogPriority(level), ANDROID_LOG_TAG, msg.toStringz); 240 } else { 241 if (logFile !is null && logFile.isOpen) { 242 SysTime ts = Clock.currTime(); 243 logFile.writef("%04d-%02d-%02d %02d:%02d:%02d.%03d %s ", ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.fracSecs.split!("msecs").msecs, logLevelName(level)); 244 logFile.writefln(fmt, args); 245 logFile.flush(); 246 } 247 } 248 } 249 } 250 /// Log verbose / trace message 251 static public void v(S...)(S args) { 252 synchronized(mutex) { 253 if (logLevel >= LogLevel.Trace) 254 log(LogLevel.Trace, args); 255 } 256 } 257 /// Log verbose / trace message with format string 258 static public void fv(S...)(S args) { 259 synchronized(mutex) { 260 if (logLevel >= LogLevel.Trace) 261 logf(LogLevel.Trace, args); 262 } 263 } 264 /// Log debug message 265 static public void d(S...)(S args) { 266 synchronized(mutex) { 267 if (logLevel >= LogLevel.Debug) 268 log(LogLevel.Debug, args); 269 } 270 } 271 /// Log debug message with format string 272 static public void fd(S...)(S args) { 273 synchronized(mutex) { 274 if (logLevel >= LogLevel.Debug) 275 logf(LogLevel.Debug, args); 276 } 277 } 278 /// Log info message 279 static public void i(S...)(S args) { 280 synchronized(mutex) { 281 if (logLevel >= LogLevel.Info) 282 log(LogLevel.Info, args); 283 } 284 } 285 /// Log info message 286 static public void fi(S...)(S args) { 287 synchronized(mutex) { 288 if (logLevel >= LogLevel.Info) 289 logf(LogLevel.Info, args); 290 } 291 } 292 /// Log warn message 293 static public void w(S...)(S args) { 294 synchronized(mutex) { 295 if (logLevel >= LogLevel.Warn) 296 log(LogLevel.Warn, args); 297 } 298 } 299 /// Log warn message 300 static public void fw(S...)(S args) { 301 synchronized(mutex) { 302 if (logLevel >= LogLevel.Warn) 303 logf(LogLevel.Warn, args); 304 } 305 } 306 /// Log error message 307 static public void e(S...)(S args) { 308 synchronized(mutex) { 309 if (logLevel >= LogLevel.Error) 310 log(LogLevel.Error, args); 311 } 312 } 313 /// Log error message 314 static public void fe(S...)(S args) { 315 synchronized(mutex) { 316 if (logLevel >= LogLevel.Error) 317 logf(LogLevel.Error, args); 318 } 319 } 320 /// Log fatal error message 321 static public void f(S...)(S args) { 322 synchronized(mutex) { 323 if (logLevel >= LogLevel.Fatal) 324 log(LogLevel.Fatal, args); 325 } 326 } 327 /// Log fatal error message 328 static public void ff(S...)(S args) { 329 synchronized(mutex) { 330 if (logLevel >= LogLevel.Fatal) 331 logf(LogLevel.Fatal, args); 332 } 333 } 334 335 version (Android) { 336 static public void setLogTag(const char * tag) { 337 ANDROID_LOG_TAG = tag; 338 } 339 } 340 } 341 342 debug { 343 private static __gshared bool _appShuttingDown = false; 344 345 @property bool appShuttingDown() { return _appShuttingDown; } 346 347 /// for debug purposes - sets shutdown flag to log widgets not destroyed in time. 348 void setAppShuttingDownFlag() { 349 _appShuttingDown = true; 350 } 351 } 352 353 void onResourceDestroyWhileShutdown(string resourceName, string objname = null) { 354 Log.e("Resource leak: destroying resource while shutdown! ", resourceName, " ", objname); 355 } 356 357 /// set to true when exiting main - to detect destructor calls for resources by GC 358 __gshared bool APP_IS_SHUTTING_DOWN = false;