1 module coloredlogger;
2 
3 import std.experimental.logger;
4 import std.stdio : File;
5 import std.concurrency : Tid;
6 import std.datetime : SysTime;
7 
8 
9 class ColoredLogger : FileLogger
10 {
11     // See this list: https://wiki.archlinux.org/index.php/Color_Bash_Prompt#List_of_colors_for_prompt_and_Bash
12     enum Color : string
13     {
14         Clear  = "\033c",  // clear console
15         Normal = "\033[0m",  // reset color
16         Black  = "\033[1;30m",
17         Red    = "\033[1;31m",
18         Green  = "\033[1;32m",
19         Yellow = "\033[1;33m",
20         Blue   = "\033[1;34m",
21         Purple = "\033[1;35m",
22         Cyan   = "\033[1;36m",
23         White  = "\033[1;37m"
24     }
25 
26 
27   private:
28     immutable string[LogLevel] _colorMap;
29 
30 
31   public:
32     this(File file, const LogLevel lv = LogLevel.info) @safe
33     {
34         this(file, null, lv);
35     }
36 
37     this(File file, in string[LogLevel] colorMap, const LogLevel lv = LogLevel.info) @safe
38     {
39         super(file, lv);
40         _colorMap = buildColorMap(colorMap);
41     }
42 
43     override protected void beginLogMsg(string file, int line, string funcName,
44                                         string prettyFuncName, string moduleName, LogLevel logLevel,
45                                         Tid threadId, SysTime timestamp, Logger logger) @safe
46     {
47 
48         this.file.lockingTextWriter().put(_colorMap[logLevel]);
49         super.beginLogMsg(file, line, funcName, prettyFuncName, moduleName, logLevel, threadId, timestamp, logger);
50     }
51 
52     override protected void finishLogMsg()
53     {
54         auto lt = this.file.lockingTextWriter();
55         lt.put(cast(string)Color.Normal);
56         lt.put('\n');
57         this.file.flush();
58     }
59 
60 
61   private:
62     immutable(string[LogLevel]) buildColorMap(in string[LogLevel] colorMap) @trusted pure
63     {
64         import std.exception : assumeUnique;
65         import std.traits : EnumMembers;
66 
67         string[LogLevel] result;
68 
69         foreach (ll; EnumMembers!LogLevel) {
70             if (ll in colorMap) {
71                 result[ll] = colorMap[ll];
72                 continue;
73             }
74 
75             // Default color mapping
76             final switch (ll) {
77             case LogLevel.all, LogLevel.off:
78                 break;
79             case LogLevel.trace:
80                 result[ll] = Color.Blue;
81                 break;
82             case LogLevel.info:
83                 result[ll] = Color.Green;
84                 break;
85             case LogLevel.warning:
86                 result[ll] = Color.Yellow;
87                 break;
88             case LogLevel.error:
89                 result[ll] = Color.Purple;
90                 break;
91             case LogLevel.critical:
92                 result[ll] = Color.Red;
93                 break;
94             case LogLevel.fatal:
95                 result[ll] = Color.Cyan;
96                 break;
97             }
98         }
99 
100         return result.assumeUnique;
101     }
102 }