1 2 module logging.facade; 3 4 public import logging.formatters; 5 public import logging.levels; 6 public import logging.sinks; 7 8 private import std.datetime; 9 private import std.string; 10 private import logging.utils; 11 12 public struct log 13 { 14 public static void log(LogLevel loglevel, string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args) 15 { 16 _facade.log(loglevel, m, func, line, Clock.currTime(), fmt.format(args), abbr_thread_id()); 17 } 18 19 public static void error(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args) 20 { 21 log!(LogLevel.Error,m,func,line,Args)(fmt, args); 22 } 23 24 public static void warning(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args) 25 { 26 log!(LogLevel.Warning,m,func,line,Args)(fmt, args); 27 } 28 29 public alias warning warn; 30 31 public static void info(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args) 32 { 33 log!(LogLevel.Info,m,func,line,Args)(fmt, args); 34 } 35 36 public static void debg(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args) 37 { 38 log!(LogLevel.Debug,m,func,line,Args)(fmt, args); 39 } 40 41 public static void trace(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args) 42 { 43 debug log!(LogLevel.Trace,m,func,line,Args)(fmt, args); 44 } 45 46 public static void trace(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__)() 47 { 48 debug log!(LogLevel.Trace,m,func,line)("trace"); 49 } 50 51 public static void add_sink(LogSink s) 52 { 53 _facade.add_sink(s); 54 } 55 56 public static void remove_all_sinks() 57 { 58 _facade.remove_all_sinks(); 59 } 60 61 shared static this() 62 { 63 _facade = new FacadeImpl(); 64 } 65 66 __gshared FacadeImpl _facade; 67 } 68 69 // not quite beautiful: couldnt overcome type system for synchronized class 70 71 private class FacadeImpl 72 { 73 public void log(LogLevel loglevel, string m, string func, size_t line, SysTime time, lazy string msg, uint thread_id) 74 { 75 synchronized(this) 76 { 77 foreach(l; _sinks) 78 { 79 l.log(loglevel, m, func, line, Clock.currTime(), msg, abbr_thread_id()); 80 } 81 } 82 } 83 84 public void add_sink(LogSink sink) 85 { 86 synchronized(this) 87 { 88 _sinks ~= sink; 89 } 90 } 91 92 public void remove_all_sinks() 93 { 94 synchronized(this) 95 { 96 _sinks = null; 97 } 98 } 99 100 private LogSink[] _sinks; 101 } 102 103 unittest 104 { 105 import std.concurrency; 106 import std.stdio; 107 import std.file; 108 import std.uuid; 109 110 static class ThreadedFormatter : Formatter 111 { 112 override string format(LogLevel loglevel, string m, string func, size_t line, SysTime st, string msg, uint thread_id) 113 { 114 return "T#%02s %s %s".format(abbr_thread_id(), LOGLEVEL_STR[loglevel], msg); 115 } 116 } 117 118 static void worker(int i) 119 { 120 log.info("worker %s", i); 121 send(ownerTid(),1); 122 } 123 124 log.remove_all_sinks(); 125 log.info("should not be logged"); 126 127 string tmpnam = randomUUID().toString(); 128 log.add_sink(new FileLogSink(tmpnam, new ThreadedFormatter)); 129 130 log.info("should be logged from main thread"); 131 132 auto tid = spawn(&worker, 1); 133 receiveOnly!int(); 134 135 log.remove_all_sinks(); 136 137 auto text = readText(tmpnam); 138 139 auto expected_text = `T#00 INFO should be logged from main thread 140 T#01 INFO worker 1 141 `; 142 143 assert(text == expected_text); 144 }