1 module vibe_json_logger.logger;
2 
3 private import vibe.core.log : Logger, LogLine, LogLevel;
4 
5 /**
6     JSON based logger for logging to regular files or stdout/stderr
7 */
8 final class JSONLogger : Logger
9 {
10     import std.stdio : File;
11     import std.format : formattedWrite;
12 
13     private
14     {
15         File m_infoFile;
16         File m_diagFile;
17         File m_curFile;
18     }
19 
20     this(File info_file, File diag_file)
21     {
22         m_infoFile = info_file;
23         m_diagFile = diag_file;
24 
25         multilineLogger = true;
26     }
27 
28     this(string filename)
29     {
30         auto f = File(filename, "ab");
31         this(f, f);
32     }
33 
34     this()
35     {
36         import std.stdio : stderr, stdout;
37 
38         this(stdout, stderr);
39     }
40 
41     override void beginLine(ref LogLine msg) @safe
42     {
43         string pref;
44         final switch (msg.level)
45         {
46         case LogLevel.trace:
47             pref = "TRACE";
48             m_curFile = m_diagFile;
49             break;
50         case LogLevel.debugV:
51             pref = "DEBUG_VERBOSE";
52             m_curFile = m_diagFile;
53             break;
54         case LogLevel.debug_:
55             pref = "DEBUG";
56             m_curFile = m_diagFile;
57             break;
58         case LogLevel.diagnostic:
59             pref = "DIAGNOSTIC";
60             m_curFile = m_diagFile;
61             break;
62         case LogLevel.info:
63             pref = "INFO";
64             m_curFile = m_infoFile;
65             break;
66         case LogLevel.warn:
67             pref = "WARN";
68             m_curFile = m_diagFile;
69             break;
70         case LogLevel.error:
71             pref = "ERROR";
72             m_curFile = m_diagFile;
73             break;
74         case LogLevel.critical:
75             pref = "CRITICAL";
76             m_curFile = m_diagFile;
77             break;
78         case LogLevel.fatal:
79             pref = "FATAL";
80             m_curFile = m_diagFile;
81             break;
82         case LogLevel.none:
83             assert(false);
84         }
85 
86         auto dst = m_curFile.lockingTextWriter;
87 
88         dst.put('{');
89         dst.formattedWrite!(`"timestamp":"%s",`)(msg.time.toISOExtString);
90 
91         if (msg.threadName.length)
92             dst.formattedWrite!(`"threadName":"%s",`)(msg.threadName);
93 
94         dst.formattedWrite!(`"threadID":"%08X",`)(msg.threadID);
95         import vibe.core.task : Task;
96 
97         dst.put(`"taskID":"`);
98         Task.getThis().getDebugID(dst);
99         dst.put(`",`);
100         dst.formattedWrite!(`"level":"%s",`)(pref);
101         dst.formattedWrite!(`"file":"%s",`)(msg.file);
102         dst.formattedWrite!(`"line":%d,`)(msg.line);
103 
104         dst.put(`"message":"`);
105     }
106 
107     override void put(scope const(char)[] text) @safe
108     {
109         import std.array : replace;
110 
111         m_curFile.write(text.replace(`"`, `\"`));
112     }
113 
114     override void endLine() @safe
115     {
116         m_curFile.writeln(`"}`);
117         m_curFile.flush();
118     }
119 }