1 module systemd.stdlogger;
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             {
52                 fline = false;
53                 formattedWrite(buffer, "<%d>[%s:%d] %s",
54                             levelRemap[p.logLevel],
55                             fileNameProc(p.file), p.line,
56                             ln);
57             }
58             else formattedWrite(buffer, "\n<%d>  %s", ln);
59         }
60         stderr.writeln(buffer.data);
61     }
62 }
63 
64 /++ SD Simple Logger
65 
66     Used sd_journal_print for output.
67 
68     For using: `sharedLog = new SDJournalLogger;`
69  +/
70 class SDJournalLogger : SDSimpleLogger
71 {
72     import systemd.base;
73     import systemd.journal;
74 
75     Appender!(char[])[8] bufs;
76 
77     this(LogLevel ll=LogLevel.all)
78     {
79         super(ll);
80         bufs[0] = buffer;
81         foreach (ref b; bufs) b.reserve(128);
82 
83         // if not inited
84         initSystemDLib();
85     }
86 
87     Appender!(char[]) buffer_priority, buffer_line;
88 
89     override void writeLogMsg(ref LogEntry p) @trusted
90     {
91         foreach (ref b; bufs) b.clear();
92 
93         formattedWrite(bufs[0], "MESSAGE=%s", p.msg);
94         formattedWrite(bufs[1], "PRIORITY=%s", levelRemap[p.logLevel]);
95         formattedWrite(bufs[2], "CODE_FILE=%s", fileNameProc(p.file));
96         formattedWrite(bufs[3], "CODE_LINE=%s", p.line);
97         formattedWrite(bufs[4], "CODE_FUNC=%s", p.prettyFuncName);
98         formattedWrite(bufs[5], "CODE_MODULE=%s", p.moduleName);
99         formattedWrite(bufs[6], "CODE_MODULE0=%s", p.moduleName.getUntil("."));
100         formattedWrite(bufs[7], "CODE_MODULE1=%s", p.moduleName.getUntil(".",1));
101 
102         iovec[bufs.length] iv;
103 
104         foreach (i, ref v; iv)
105         {
106             v.iov_base = bufs[i].data.ptr;
107             v.iov_len = cast(int)bufs[i].data.length;
108         }
109 
110         sd_journal_sendv(iv.ptr, cast(int)iv.length);
111     }
112 }
113 
114 private string getUntil(string orig, string fnd, int skip=0) @nogc @safe
115 {
116     foreach (i, ch; orig)
117     {
118         if (orig[i..$].startsWith(fnd))
119         {
120             if (skip <= 0) return orig[0..i];
121             skip--;
122         }
123     }
124     return orig;
125 }
126 
127 unittest
128 {
129     assert("abc".getUntil(".") == "abc");
130     assert("abc.def".getUntil(".") == "abc");
131     assert("abc.def.ij".getUntil(".") == "abc");
132     assert("abc.def.ij".getUntil(".",1) == "abc.def");
133     assert("abc.def.ij.aaa".getUntil(".",1) == "abc.def");
134     assert("abc.def.ij.aaa".getUntil(".",2) == "abc.def.ij");
135     assert("abc.def.ij.aaa".getUntil(".",10) == "abc.def.ij.aaa");
136 }