1 module sdlogger;
2 
3 public import std.experimental.logger;
4 
5 import std.format : formattedWrite;
6 import std.array : Appender;
7 import std.algorithm : findSplitAfter;
8 import std.string : lineSplitter, startsWith;
9 import std.stdio : stderr;
10 
11 /++ SD Simple Logger
12 
13     Used stderr for output with systemd specific syntax.
14 
15     For using: `sharedLog = new SDSimpleLogger;`
16  +/
17 class SDSimpleLogger : Logger
18 {
19     int[LogLevel] levelRemap;
20 
21     Appender!(char[]) buffer;
22 
23     string fileNameProc(string f) const @safe pure
24     { return f.findSplitAfter("source/")[1]; }
25 
26     this(LogLevel ll=LogLevel.all)
27     {
28         super(ll);
29         //                              journald codes
30         levelRemap[LogLevel.all]      = 7;
31         levelRemap[LogLevel.trace]    = 7; // debug
32         //                              6     info 
33         levelRemap[LogLevel.info]     = 5; // notice
34         levelRemap[LogLevel.warning]  = 4; // warning
35         levelRemap[LogLevel.error]    = 3; // error
36         levelRemap[LogLevel.critical] = 2; // crit
37         levelRemap[LogLevel.fatal]    = 1; // alert
38         levelRemap[LogLevel.off]      = 1;
39         levelRemap.rehash();
40 
41         buffer.reserve(1024);
42     }
43 
44     override void writeLogMsg(ref LogEntry p) @trusted
45     {
46         buffer.clear();
47         bool fline = true;
48         foreach (ln; p.msg.lineSplitter)
49         {
50             if (fline)
51                 formattedWrite(buffer, "<%d>[%s:%d] %s",
52                             levelRemap[p.logLevel],
53                             fileNameProc(p.file), p.line,
54                             ln);
55             else
56             {
57                 fline = false;
58                 formattedWrite(buffer, "\n<%d>  %s", ln);
59             }
60         }
61         stderr.writeln(buffer.data);
62     }
63 }
64 
65 /++ SD Simple Logger
66 
67     Used sd_journal_print for output.
68 
69     For using: `sharedLog = new SDJournalLogger;`
70  +/
71 class SDJournalLogger : SDSimpleLogger
72 {
73     import sdnotify;
74 
75     Appender!(char[])[8] bufs;
76 
77     this(LogLevel ll=LogLevel.all)
78     {
79         super(ll);
80         bufs[0] = buffer;
81         initSystemDLib();
82     }
83 
84     Appender!(char[]) buffer_priority, buffer_line;
85 
86     override void writeLogMsg(ref LogEntry p) @trusted
87     {
88         foreach (ref b; bufs) b.clear();
89 
90         formattedWrite(bufs[0], "MESSAGE=%s", p.msg);
91         formattedWrite(bufs[1], "PRIORITY=%s", levelRemap[p.logLevel]);
92         formattedWrite(bufs[2], "CODE_FILE=%s", fileNameProc(p.file));
93         formattedWrite(bufs[3], "CODE_LINE=%s", p.line);
94         formattedWrite(bufs[4], "CODE_FUNC=%s", p.prettyFuncName);
95         formattedWrite(bufs[5], "CODE_MODULE=%s", p.moduleName);
96         formattedWrite(bufs[6], "CODE_MODULE0=%s", p.moduleName.getUntil("."));
97         formattedWrite(bufs[7], "CODE_MODULE1=%s", p.moduleName.getUntil(".",1));
98 
99         iovec[bufs.length] iv;
100 
101         foreach (i, ref v; iv)
102         {
103             v.iov_base = bufs[i].data.ptr;
104             v.iov_len = cast(int)bufs[i].data.length;
105         }
106 
107         sd_journal_sendv(iv.ptr, cast(int)iv.length);
108     }
109 }
110 
111 private string getUntil(string orig, string fnd, int skip=0) @nogc @safe
112 {
113     foreach (i, ch; orig)
114     {
115         if (orig[i..$].startsWith(fnd))
116         {
117             if (skip <= 0) return orig[0..i];
118             skip--;
119         }
120     }
121     return orig;
122 }
123 
124 unittest
125 {
126     assert("abc".getUntil(".") == "abc");
127     assert("abc.def".getUntil(".") == "abc");
128     assert("abc.def.ij".getUntil(".") == "abc");
129     assert("abc.def.ij".getUntil(".",1) == "abc.def");
130     assert("abc.def.ij.aaa".getUntil(".",1) == "abc.def");
131     assert("abc.def.ij.aaa".getUntil(".",2) == "abc.def.ij");
132     assert("abc.def.ij.aaa".getUntil(".",10) == "abc.def.ij.aaa");
133 }