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 }