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 }