1 /*
2  *Copyright (C) 2018 Laurent Tréguier
3  *
4  *This file is part of DLS.
5  *
6  *DLS is free software: you can redistribute it and/or modify
7  *it under the terms of the GNU General Public License as published by
8  *the Free Software Foundation, either version 3 of the License, or
9  *(at your option) any later version.
10  *
11  *DLS is distributed in the hope that it will be useful,
12  *but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *GNU General Public License for more details.
15  *
16  *You should have received a copy of the GNU General Public License
17  *along with DLS.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 module dls.protocol.logger;
22 
23 import dls.protocol.interfaces : InitializeParams, MessageType;
24 
25 private shared _logger = new shared LspLogger();
26 private immutable int[InitializeParams.Trace] traceToType;
27 private immutable string[MessageType] messageSeverity;
28 private immutable logMessageFormat = "[%.24s] [%s] %s";
29 
30 shared static this()
31 {
32     import std.experimental.logger : LogLevel, globalLogLevel;
33 
34     globalLogLevel = LogLevel.off;
35 
36     //dfmt off
37     traceToType = [
38         InitializeParams.Trace.off      : 0,
39         InitializeParams.Trace.messages : MessageType.info,
40         InitializeParams.Trace.verbose  : MessageType.log
41     ];
42 
43     messageSeverity = [
44         MessageType.log     : "D",
45         MessageType.info    : "I",
46         MessageType.warning : "W",
47         MessageType.error   : "E"
48     ];
49     //dfmt on
50 }
51 
52 @property shared(LspLogger) logger()
53 {
54     return _logger;
55 }
56 
57 private shared class LspLogger
58 {
59     import dls.protocol.interfaces : MessageType;
60     import std.format : format;
61 
62     private int _messageType;
63 
64     @property void trace(const InitializeParams.Trace t)
65     {
66         _messageType = traceToType[t];
67     }
68 
69     void log(Args...)(const string message, const Args args) const
70     {
71         sendMessage(format(message, args), MessageType.log);
72     }
73 
74     void info(Args...)(const string message, const Args args) const
75     {
76         sendMessage(format(message, args), MessageType.info);
77     }
78 
79     void warning(Args...)(const string message, const Args args) const
80     {
81         sendMessage(format(message, args), MessageType.warning);
82     }
83 
84     void error(Args...)(const string message, const Args args) const
85     {
86         sendMessage(format(message, args), MessageType.error);
87     }
88 
89     private void sendMessage(const string message, const MessageType type) const
90     {
91         import dls.protocol.interfaces : LogMessageParams;
92         import dls.protocol.jsonrpc : send;
93         import dls.protocol.messages.methods : Window;
94         import dls.protocol.state : initOptions;
95         import std.datetime : Clock;
96         import std.file : mkdirRecurse;
97         import std.format : format;
98         import std.path : dirName;
99         import std.stdio : File;
100 
101         if (type <= _messageType)
102         {
103             if (initOptions.logFile.length > 0)
104             {
105                 static bool firstLog = true;
106 
107                 if (firstLog)
108                 {
109                     mkdirRecurse(dirName(initOptions.logFile));
110                 }
111 
112                 synchronized
113                 {
114                     auto log = File(initOptions.logFile, firstLog ? "w" : "a");
115                     log.writefln(logMessageFormat, Clock.currTime.toString(),
116                             messageSeverity[type], message);
117                     log.flush();
118                 }
119 
120                 if (firstLog)
121                 {
122                     firstLog = false;
123                 }
124             }
125 
126             send(Window.logMessage, new LogMessageParams(type, format(logMessageFormat,
127                     Clock.currTime.toString(), messageSeverity[type], message)));
128         }
129     }
130 }