1 /** 2 * Defines the Dash logger, using the std.logger proposal. 3 */ 4 module dash.utility.output; 5 import std.experimental.logger; 6 // Logging functions for the others 7 public import std.experimental.logger: log, logf, trace, tracef, info, infof, warning, warningf, error, errorf, critical, criticalf, fatal, fatalf; 8 9 import colorize; 10 11 deprecated( "Use trace() instead" ) 12 alias logDebug = trace; 13 deprecated( "Use info() instead" ) 14 alias logInfo = info; 15 deprecated( "Use info() instead" ) 16 alias logNotice = info; 17 deprecated( "Use warning() instead" ) 18 alias logWarning = warning; 19 deprecated( "Use error() instead" ) 20 alias logError = error; 21 deprecated( "Use fatal() instead" ) 22 alias logFatal = fatal; 23 24 /** 25 * Benchmark the running of a function, and log the result to the debug buffer. 26 * 27 * Params: 28 * func = The function to benchmark 29 * name = The name to print in the log 30 */ 31 void bench( alias func )( lazy string name ) 32 { 33 import std.datetime, core.time; 34 auto result = cast(Duration)benchmark!func( 1 ); 35 tracef( "%s time:\t\t\t%s", name, result ); 36 } 37 38 /** 39 * Dash's custom logger. 40 */ 41 abstract final class DashLogger 42 { 43 static MultiLogger multiLogger; 44 static DashConsoleLogger consoleLogger; 45 static DashFileLogger fileLogger; 46 static DashEditorLogger editorLogger; 47 48 static void setDefaults() 49 { 50 import dash.utility.config; 51 52 // Create loggers 53 consoleLogger = new DashConsoleLogger( config.logging.verbosities.outputVerbosity ); 54 fileLogger = new DashFileLogger( config.logging.verbosities.loggingVerbosity, config.logging.filePath ); 55 editorLogger = new DashEditorLogger(); 56 57 // Create multilogger 58 multiLogger = new MultiLogger( LogLevel.all ); 59 multiLogger.insertLogger( "Dash Console Logger", consoleLogger ); 60 multiLogger.insertLogger( "Dash Editor Logger", editorLogger ); 61 stdlog = multiLogger; 62 } 63 64 static void initialize() 65 { 66 import dash.utility.config; 67 68 // Reinitialize the logger with file path. 69 multiLogger.removeLogger( "Dash File Logger" ); 70 fileLogger = new DashFileLogger( config.logging.verbosities.loggingVerbosity, config.logging.filePath ); 71 multiLogger.insertLogger( "Dash File Logger", fileLogger ); 72 73 // Update levels of existing loggers. 74 consoleLogger.logLevel = config.logging.verbosities.outputVerbosity; 75 } 76 } 77 78 private: 79 /// Logs messages to the console 80 final class DashConsoleLogger : FileLogger 81 { 82 this( LogLevel level ) 83 { 84 import std.stdio: stdout; 85 86 super( stdout, level ); 87 } 88 89 override void writeLogMsg( ref LogEntry payload ) 90 { 91 import std.conv: to; 92 import std.path: baseName; 93 import std.array: appender; 94 import std.format: formattedWrite; 95 96 auto msg = appender!string; 97 98 // Log level and message 99 msg.formattedWrite( 100 "%s: %s", 101 payload.logLevel.to!string, 102 payload.msg, 103 ); 104 105 // Color and log file 106 file.cwritef( msg.data.color( payload.logLevel.getColor(), bg.init, payload.logLevel.getMode() ) ); 107 finishLogMsg(); 108 } 109 } 110 111 /// Logs messages to a file 112 final class DashFileLogger : FileLogger 113 { 114 this( LogLevel level, string filename ) 115 { 116 super( filename, level ); 117 } 118 119 override void writeLogMsg( ref LogEntry payload ) 120 { 121 import std.conv: to; 122 import std.path: baseName; 123 import std.array: appender; 124 import std.format: formattedWrite; 125 126 auto msg = appender!string; 127 128 // Log file and line info in the console only 129 msg.formattedWrite( 130 "[%s] {%s:%d} %s: %s", 131 payload.timestamp.toSimpleString(), 132 payload.file.baseName, 133 payload.line, 134 payload.logLevel.to!string, 135 payload.msg, 136 ); 137 138 // Color and print the message 139 logMsgPart( msg.data ); 140 finishLogMsg(); 141 } 142 } 143 144 /// Logs messages to the editor interface 145 final class DashEditorLogger : Logger 146 { 147 this() 148 { 149 // File not actually used for anything, but required by FileLogger 150 super( LogLevel.all ); 151 } 152 153 override void writeLogMsg( ref LogEntry payload ) 154 { 155 import dash.core.dgame; 156 157 static struct LogMessage 158 { 159 string file; 160 int line; 161 string funcName; 162 string prettyFuncName; 163 string moduleName; 164 LogLevel logLevel; 165 string timestamp; 166 string msg; 167 168 this( LogEntry entry ) 169 { 170 file = entry.file; 171 line = entry.line; 172 funcName = entry.funcName; 173 prettyFuncName = entry.prettyFuncName; 174 moduleName = entry.moduleName; 175 logLevel = entry.logLevel; 176 timestamp = entry.timestamp.toSimpleString(); 177 msg = entry.msg; 178 } 179 } 180 181 DGame.instance.editor.send( "dash:logger:message", LogMessage( payload ) ); 182 } 183 } 184 185 /// Helper to get the color of the log level 186 fg getColor( LogLevel level ) 187 { 188 final switch( level ) with( LogLevel ) with( fg ) 189 { 190 case trace: 191 case info: 192 case off: 193 case all: 194 return init; 195 case warning: 196 return yellow; 197 case error: 198 case critical: 199 case fatal: 200 return red; 201 } 202 } 203 204 /// Helper to get the print mode of the log level 205 mode getMode( LogLevel level ) 206 { 207 switch( level ) with( LogLevel ) with( mode ) 208 { 209 case critical: 210 return underline; 211 case fatal: 212 return bold; 213 default: 214 return init; 215 } 216 }