1 module gfm.core.log;
2 
3 import std.stream,
4        std.stdio,
5        std.string;
6 
7 import std.logger;
8 
9 version(Windows)
10 {
11     import core.sys.windows.windows;
12 }
13 
14 /**
15 Provides a common logging interface for GFM.
16 
17 Bugs: 
18     Log is not thread-safe. Messages will get squashed when output by multiple threads.
19 
20 Deprecated: 
21     This whole module will go away when std.logger is there.
22 */
23 class Log
24 {
25     protected
26     {
27         /// Custom loggers must implement this one method.
28         abstract void logMessage(MessageType type, lazy string message);        
29     }
30 
31     public
32     {
33         /// Log message severities.
34         enum MessageType
35         {
36             DEBUG   = 0, /// This level is intended for debugging and should not happen in production.
37             INFO    = 1, /// This level is for informational messages.
38             WARNING = 2, /// An error which is not fatal and was recovered.
39             ERROR   = 3, /// A serious error that can't be recovered.
40         }
41 
42         /// Logs a message.
43         final void message(MessageType type, lazy string message)
44         {
45             return logMessage(type, message);
46         }
47 
48         /// Logs a formatted message.
49         final void messagef(Args...)(MessageType type, Args args)
50         {
51             return logMessage(type, format(args));
52         }
53 
54         // <shortcuts>
55 
56         /// Logs an INFO message.
57         final void info(lazy string s)
58         {
59             logMessage(Log.MessageType.INFO, s);
60         }
61 
62         /// Logs an INFO message, with formatting.
63         final void infof(Args...)(Args args)
64         {
65             logMessage(Log.MessageType.INFO, format(args));
66         }
67 
68         /// Logs a DEBUG message, with formatting.
69         final void crap(lazy string s)
70         {
71             logMessage(Log.MessageType.DEBUG, s);
72         }
73 
74         /// Logs a DEBUG message, with formatting.
75         final void crapf(Args...)(Args args)
76         {
77             logMessage(Log.MessageType.DEBUG, format(args));
78         }
79 
80         /// Logs a WARNING message, with formatting.
81         final void warn(lazy string s)
82         {
83             logMessage(Log.MessageType.WARNING, s);
84         }
85 
86         /// Logs a WARNING message, with formatting.
87         final void warnf(Args...)(Args args)
88         {
89             logMessage(Log.MessageType.WARNING, format(args));
90         }
91 
92         /// Logs an ERROR message, with formatting.
93         final void error(lazy string s)
94         {
95             logMessage(Log.MessageType.ERROR, s);
96         }
97 
98         /// Logs an ERROR message, with formatting.
99         final void errorf(Args...)(Args args)
100         {
101             logMessage(Log.MessageType.ERROR, format(args));
102         }        
103 
104         // </shortcuts>        
105     }
106 
107     private string getTopic(MessageType type)
108     {
109         final switch (type)
110         {
111             case MessageType.DEBUG: return "debug";
112             case MessageType.INFO: return "info";
113             case MessageType.WARNING: return "warn";
114             case MessageType.ERROR: return "error";
115         }
116     }
117 }
118 
119 /**
120 
121 Dispatch log messages to multiple loggers.
122 
123  */
124 deprecated("Use the logger package instead") final class MultiLog : Log
125 {
126     public
127     {
128         this(Log[] logs)
129         {
130             _logs = logs;
131         }
132     }
133 
134     protected
135     {
136         override void logMessage(MessageType type, lazy string message)
137         {
138             foreach(log; _logs)
139                 log.logMessage(type, message);
140         }
141     }
142 
143     private Log[] _logs;
144 }
145 
146 
147 /**
148 
149 Throw-away log messages.
150 
151 */
152 deprecated("Use a custom Logger instead from the logger package") final class NullLog : Log
153 {
154     protected
155     {
156         override void logMessage(MessageType type, lazy string message)
157         {
158         }
159     }
160 }
161 
162 /**
163 
164 Displays coloured log messages in the console.
165 
166 */
167 deprecated("Use ConsoleLogger instead") final class ConsoleLog : Log
168 {
169     public
170     {
171         this()
172         {
173             version(Windows)
174             {
175                 _console = GetStdHandle(STD_OUTPUT_HANDLE);
176 
177                 // saves console attributes
178                 _savedInitialColor = (0 != GetConsoleScreenBufferInfo(_console, &consoleInfo));
179             }
180         }
181 
182         ~this()
183         {
184             close();
185         }
186 
187         void close()
188         {
189             version(Windows)
190             {
191                 // restore initial console attributes
192                 if (_savedInitialColor)
193                 {
194                     SetConsoleTextAttribute(_console, consoleInfo.wAttributes);
195                     _savedInitialColor = false;
196                 }
197             }
198         }
199     }
200 
201     protected
202     {
203         override void logMessage(MessageType type, lazy string message)
204         {
205             version(Windows)
206             {
207                 if (_console !is null)
208                 {
209                     final switch (type)
210                     {
211                         case MessageType.DEBUG: 
212                             SetConsoleTextAttribute(_console, 11);
213                             break;
214                         case MessageType.INFO: 
215                             SetConsoleTextAttribute(_console, 15);
216                             break;
217                         case MessageType.WARNING: 
218                             SetConsoleTextAttribute(_console, 14);
219                             break;
220                         case MessageType.ERROR: 
221                             SetConsoleTextAttribute(_console, 12);
222                             break;
223                     }
224                 }
225             }
226             writefln("%s: %s", getTopic(type), message);
227         }
228     }
229 
230     private
231     {
232         version(Windows)
233         {
234             HANDLE _console;
235             bool _savedInitialColor;
236             CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
237         }
238     }
239 }
240 
241 
242 /**
243 
244 Output log messages in a file.
245 
246 */
247 deprecated("Use FileLogger instead from the logger package") final class FileLog : Log
248 {
249     public
250     {
251         this(string filename = "log.html")
252         {
253             _indent = 0;
254             try
255             {
256                 _logFile = new std.stream.BufferedFile(filename, FileMode.OutNew);
257                 _logFile.writefln("<html>");
258                 _logFile.writefln("<head>");
259                 _logFile.writefln("<style>");
260                 _logFile.writefln("body { margin: 0px; padding: 0px; font-family: courier new, courier; font-size: 9pt; color: white; background-color: #000000; }");
261                 _logFile.writefln("div { margin: 5px 5px 5px 5px; }");
262                 _logFile.writefln(".debug { color: #9b7766; text-align: left;}");
263                 _logFile.writefln(".info { color: #80cf49; text-align: left;}");
264                 _logFile.writefln(".warn { color: #ff8020; text-align: left; text-decoration: bold;}");
265                 _logFile.writefln(".error { color: #ff2020; text-align: left; text-decoration: bold;}");
266                 _logFile.writefln("b { color: #ffff20; text-align: left; }");
267                 _logFile.writefln("</style>");
268                 _logFile.writefln("</head>");
269                 _logFile.writefln("<body>");
270             }
271             catch(StreamException e)
272             {
273                 _logFile = null;
274                 writefln("%s", e.msg);
275             }
276         }
277     }
278 
279     private
280     {
281         Stream _logFile;
282         int _indent;
283     }
284 
285     public
286     {
287         override void logMessage(MessageType type, lazy string message)
288         {
289             if (message.length == 0) return;
290 
291             if (message[0] == '<')
292             {
293                 if (_indent > 0)
294                     --_indent;
295             }
296 
297             int usedIndent = _indent;
298 
299             if (message[0] == '>')
300             {
301                 if (_indent < 32)
302                     ++_indent;
303             }
304 
305             string topic = getTopic(type);
306             string indentString1 = "";
307             for(int i = 0; i < usedIndent; ++i)
308             {
309                 indentString1 ~= "    ";
310             }
311 
312             if (_logFile !is null)
313             {
314                 string HTMLmessage = message;
315                 if (message[0] == '>') HTMLmessage = "&rarr; " ~ HTMLmessage[1..$];
316                 else if (message[0] == '<') HTMLmessage = "&larr; " ~ HTMLmessage[1..$];
317                 else if (message[0] == '*') HTMLmessage = "&diams; " ~ HTMLmessage[1..$];
318 
319                 try
320                 {
321                     _logFile.writefln("<div class=\"%s\" style=\"margin-left: %dpt;\" ><b>[%s]</b> %s</div>", topic, usedIndent * 30, topic, HTMLmessage);
322                     _logFile.flush();
323                 }
324                 catch(WriteException e)
325                 {
326                     writefln(e.msg);
327                 }
328             }
329         }
330     }
331 }
332 
333 /// Gets the default logger.
334 /// Returns: the default logging object, which writes to both a file and the console.
335 deprecated("Use LogManager.defaultLogger() from the logger package instead") Log defaultLog()
336 {
337     Log consoleLogger = new ConsoleLog();
338     Log fileLogger = new FileLog("output_log.html");
339     return new MultiLog( [ consoleLogger, fileLogger ] );
340 }
341 
342 
343 // Because default std.logger logger is a bit verbose, and lacks colors.
344 class ConsoleLogger : Logger
345 {
346     public
347     {
348         this()
349         {
350             super("", LogLevel.info);
351 
352             version(Windows)
353             {
354                 _console = GetStdHandle(STD_OUTPUT_HANDLE);
355 
356                 // saves console attributes
357                 _savedInitialColor = (0 != GetConsoleScreenBufferInfo(_console, &consoleInfo));
358             }
359         }
360 
361         override void writeLogMsg(ref LoggerPayload payload) @trusted
362         {
363             LogLevel logLevel;
364             synchronized(this)
365             {
366                 version(Windows)
367                 {
368                     switch(payload.logLevel)
369                     {
370                         case LogLevel.info:
371                             SetConsoleTextAttribute(_console, 15);
372                             break;
373 
374                         case LogLevel.warning:
375                             SetConsoleTextAttribute(_console, 14);
376                             break;
377 
378                         case LogLevel.error:
379                         case LogLevel.critical:
380                         case LogLevel.fatal:
381                             SetConsoleTextAttribute(_console, 12);
382                             break;
383 
384                         case LogLevel.unspecific:
385                         case LogLevel.trace:
386                         default:
387                             SetConsoleTextAttribute(_console, 11); 
388                     }
389                 }
390 
391                 import std.stdio;
392                 writefln("%s: %s", logLevelToString(payload.logLevel), payload.msg);
393             }
394         }
395 
396         ~this()
397         {
398             close();
399         }
400 
401         void close()
402         {
403             version(Windows)
404             {
405                 // restore initial console attributes
406                 if (_savedInitialColor)
407                 {
408                     SetConsoleTextAttribute(_console, consoleInfo.wAttributes);
409                     _savedInitialColor = false;
410                 }
411             }
412         }
413     }
414 
415     private
416     {
417         version(Windows)
418         {
419             HANDLE _console;
420             bool _savedInitialColor;
421             CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
422         }
423 
424         static pure string logLevelToString(const LogLevel lv)
425         {
426             switch(lv)
427             {
428                 case LogLevel.unspecific:
429                     return "";
430                 case LogLevel.trace:
431                     return "trace";
432                 case LogLevel.info:
433                     return "info";
434                 case LogLevel.warning:
435                     return "warning";
436                 case LogLevel.error:
437                     return "error";
438                 case LogLevel.critical:
439                     return "critical";
440                 case LogLevel.fatal:
441                     return "fatal";
442                 default:
443                     assert(false);
444             }
445         }
446     }
447 }