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 = "→ " ~ HTMLmessage[1..$]; 316 else if (message[0] == '<') HTMLmessage = "← " ~ HTMLmessage[1..$]; 317 else if (message[0] == '*') HTMLmessage = "♦ " ~ 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 }