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;