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 }