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 class Log { 96 static __gshared private LogLevel logLevel = LogLevel.Info; 97 static __gshared private std.stdio.File * logFile = null; 98 static __gshared private Mutex _mutex = null; 99 100 static public @property Mutex mutex() { 101 if (_mutex is null) 102 _mutex = new Mutex(); 103 return _mutex; 104 } 105 106 /// Redirects output to stdout 107 static public void setStdoutLogger() { 108 synchronized(mutex) { 109 logFile = &stdout; 110 } 111 } 112 113 /// Redirects output to stderr 114 static public void setStderrLogger() { 115 synchronized(mutex) { 116 logFile = &stderr; 117 } 118 } 119 120 /// Redirects output to file 121 static public void setFileLogger(File * file) { 122 synchronized(mutex) { 123 if (logFile !is null && logFile != &stdout && logFile != &stderr) { 124 logFile.close(); 125 destroy(logFile); 126 logFile = null; 127 } 128 logFile = file; 129 if (logFile !is null) 130 logFile.writeln("DlangUI log file"); 131 } 132 } 133 134 /// Sets log level (one of LogLevel) 135 static public void setLogLevel(LogLevel level) { 136 synchronized(mutex) { 137 logLevel = level; 138 i("Log level changed to ", level); 139 } 140 } 141 142 /// returns true if messages for level are enabled 143 static public bool isLogLevelEnabled(LogLevel level) { 144 return logLevel >= level; 145 } 146 147 /// returns true if debug log level is enabled 148 @property static public bool debugEnabled() { 149 return logLevel >= LogLevel.Debug; 150 } 151 152 /// returns true if trace log level is enabled 153 @property static public bool traceEnabled() { 154 return logLevel >= LogLevel.Trace; 155 } 156 157 /// returns true if warn log level is enabled 158 @property static public bool warnEnabled() { 159 return logLevel >= LogLevel.Warn; 160 } 161 162 /// Log level to name helper function 163 static public string logLevelName(LogLevel level) { 164 switch (level) with(LogLevel) 165 { 166 case Fatal: return "F"; 167 case Error: return "E"; 168 case Warn: return "W"; 169 case Info: return "I"; 170 case Debug: return "D"; 171 case Trace: return "V"; 172 default: return "?"; 173 } 174 } 175 version (Android) { 176 static android_LogPriority toAndroidLogPriority(LogLevel level) { 177 switch (level) with (LogLevel) { 178 /// Fatal error, cannot resume 179 case Fatal: 180 return android_LogPriority.ANDROID_LOG_FATAL; 181 /// Error 182 case Error: 183 return android_LogPriority.ANDROID_LOG_ERROR; 184 /// Warning 185 case Warn: 186 return android_LogPriority.ANDROID_LOG_WARN; 187 /// Informational message 188 case Info: 189 return android_LogPriority.ANDROID_LOG_INFO; 190 /// Debug message 191 case Debug: 192 return android_LogPriority.ANDROID_LOG_DEBUG; 193 /// Tracing message 194 case Trace: 195 default: 196 return android_LogPriority.ANDROID_LOG_VERBOSE; 197 } 198 } 199 } 200 /// Log message with arbitrary log level 201 static public void log(S...)(LogLevel level, S args) { 202 if (logLevel >= level) { 203 version (Android) { 204 import std.format; 205 import std.string : toStringz; 206 import std.format; 207 import std.conv : to; 208 char[] msg; 209 foreach(arg; args) { 210 msg ~= to!string(arg); 211 } 212 msg ~= cast(char)0; 213 __android_log_write(toAndroidLogPriority(level), ANDROID_LOG_TAG, msg.ptr); 214 } else { 215 if (logFile !is null && logFile.isOpen) { 216 SysTime ts = Clock.currTime(); 217 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)); 218 logFile.writeln(args); 219 logFile.flush(); 220 } 221 } 222 } 223 } 224 /// Log message with arbitrary log level with format string 225 static public void logf(S...)(LogLevel level, string fmt, S args) { 226 if (logLevel >= level) { 227 version (Android) { 228 import std.string : toStringz; 229 import std.format; 230 string msg = fmt.format(args); 231 __android_log_write(toAndroidLogPriority(level), ANDROID_LOG_TAG, msg.toStringz); 232 } else { 233 if (logFile !is null && logFile.isOpen) { 234 SysTime ts = Clock.currTime(); 235 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)); 236 logFile.writefln(fmt, args); 237 logFile.flush(); 238 } 239 } 240 } 241 } 242 /// Log verbose / trace message 243 static public void v(S...)(S args) { 244 synchronized(mutex) { 245 if (logLevel >= LogLevel.Trace) 246 log(LogLevel.Trace, args); 247 } 248 } 249 /// Log verbose / trace message with format string 250 static public void fv(S...)(S args) { 251 synchronized(mutex) { 252 if (logLevel >= LogLevel.Trace) 253 logf(LogLevel.Trace, args); 254 } 255 } 256 /// Log debug message 257 static public void d(S...)(S args) { 258 synchronized(mutex) { 259 if (logLevel >= LogLevel.Debug) 260 log(LogLevel.Debug, args); 261 } 262 } 263 /// Log debug message with format string 264 static public void fd(S...)(S args) { 265 synchronized(mutex) { 266 if (logLevel >= LogLevel.Debug) 267 logf(LogLevel.Debug, args); 268 } 269 } 270 /// Log info message 271 static public void i(S...)(S args) { 272 synchronized(mutex) { 273 if (logLevel >= LogLevel.Info) 274 log(LogLevel.Info, args); 275 } 276 } 277 /// Log info message 278 static public void fi(S...)(S args) { 279 synchronized(mutex) { 280 if (logLevel >= LogLevel.Info) 281 logf(LogLevel.Info, args); 282 } 283 } 284 /// Log warn message 285 static public void w(S...)(S args) { 286 synchronized(mutex) { 287 if (logLevel >= LogLevel.Warn) 288 log(LogLevel.Warn, args); 289 } 290 } 291 /// Log warn message 292 static public void fw(S...)(S args) { 293 synchronized(mutex) { 294 if (logLevel >= LogLevel.Warn) 295 logf(LogLevel.Warn, args); 296 } 297 } 298 /// Log error message 299 static public void e(S...)(S args) { 300 synchronized(mutex) { 301 if (logLevel >= LogLevel.Error) 302 log(LogLevel.Error, args); 303 } 304 } 305 /// Log error message 306 static public void fe(S...)(S args) { 307 synchronized(mutex) { 308 if (logLevel >= LogLevel.Error) 309 logf(LogLevel.Error, args); 310 } 311 } 312 /// Log fatal error message 313 static public void f(S...)(S args) { 314 synchronized(mutex) { 315 if (logLevel >= LogLevel.Fatal) 316 log(LogLevel.Fatal, args); 317 } 318 } 319 /// Log fatal error message 320 static public void ff(S...)(S args) { 321 synchronized(mutex) { 322 if (logLevel >= LogLevel.Fatal) 323 logf(LogLevel.Fatal, args); 324 } 325 } 326 327 version (Android) { 328 static public void setLogTag(const char * tag) { 329 ANDROID_LOG_TAG = tag; 330 } 331 } 332 } 333 334 debug { 335 private static __gshared bool _appShuttingDown = false; 336 337 @property bool appShuttingDown() { return _appShuttingDown; } 338 339 /// for debug purposes - sets shutdown flag to log widgets not destroyed in time. 340 void setAppShuttingDownFlag() { 341 _appShuttingDown = true; 342 } 343 } 344 345 void onResourceDestroyWhileShutdown(string resourceName, string objname = null) { 346 Log.e("Resource leak: destroying resource while shutdown! ", resourceName, " ", objname); 347 } 348 349 /// set to true when exiting main - to detect destructor calls for resources by GC 350 __gshared bool APP_IS_SHUTTING_DOWN = false; 351