1 module glued.application.logging; 2 3 import std.functional; 4 5 import glued.logging; 6 import glued.pathtree; 7 8 import optional; 9 10 alias Predicate(T) = bool delegate(T); 11 alias Callable(T) = void delegate(T); 12 13 class FilteringSink: LogSink { 14 private LogSink wrapped; 15 private Predicate!LogEvent predicate; 16 private Optional!(Callable!LogEvent) discardedConsumer; 17 18 this(LogSink wrapped, Predicate!LogEvent predicate){ 19 this.wrapped = wrapped; 20 this.predicate = predicate; 21 this.discardedConsumer = no!(Callable!LogEvent); 22 } 23 24 this(LogSink wrapped, Predicate!LogEvent predicate, Callable!LogEvent discardedConsumer){ 25 this.wrapped = wrapped; 26 this.predicate = predicate; 27 this.discardedConsumer = discardedConsumer.some; 28 } 29 30 void consume(LogEvent e){ 31 if (predicate(e)) { 32 wrapped.consume(e); 33 } else { 34 if (!discardedConsumer.empty){ 35 (discardedConsumer.front())(e); 36 } 37 } 38 } 39 } 40 41 alias PathExtractor = string delegate(LogEvent); 42 43 struct ModuleExtractors { 44 @property 45 static PathExtractor fromLogger() { return toDelegate((LogEvent e) => e.loggerLocation.moduleName); } 46 @property 47 static PathExtractor fromEvent() { return toDelegate((LogEvent e) => e.eventLocation.moduleName); } 48 } 49 50 Predicate!LogEvent levelConfig(PathTreeView!Level config, PathExtractor extractor=ModuleExtractors.fromEvent, Level defaultLevel=Level.ANY){ 51 return (LogEvent e) => (e.level >= config.get(extractor(e)).or(defaultLevel.some).front()); 52 } 53 54 //todo another source set 55 //todo this is an awfully trivial test suite, extend it 56 version(unittest){ 57 import std.datetime: SysTime; 58 import std.concurrency: Tid; 59 60 enum ActionTaken { UNKNOWN, CONSUMED, DISCARDED } 61 62 auto event(string loggerModule, string eventModule, Level lvl){ 63 return LogEvent( 64 lvl, 65 CodeLocation("", 0, loggerModule, "", "", ""), 66 CodeLocation("", 0, eventModule, "", "", ""), 67 "", 68 no!SysTime, 69 no!Tid 70 ); 71 } 72 73 ActionTaken whenFilteredWithPredicate(Predicate!LogEvent pred, LogEvent e){ 74 ActionTaken action = ActionTaken.UNKNOWN; 75 class Consume: LogSink { 76 void consume(LogEvent e){ 77 action = ActionTaken.CONSUMED; 78 } 79 } 80 void discard(LogEvent e){ action = ActionTaken.DISCARDED; } 81 new FilteringSink(new Consume, pred, &discard).consume(e); 82 return action; 83 } 84 } 85 86 unittest { 87 assert( 88 whenFilteredWithPredicate( 89 levelConfig( 90 new ConcretePathTree!Level, 91 ModuleExtractors.fromEvent, 92 Level.ANY 93 ), 94 event("a", "b", Level.INFO) 95 ) 96 == 97 ActionTaken.CONSUMED 98 ); 99 assert( 100 whenFilteredWithPredicate( 101 levelConfig( 102 new ConcretePathTree!Level, 103 ModuleExtractors.fromEvent, 104 Level.INFO 105 ), 106 event("a", "b", Level.DEBUG) 107 ) 108 == 109 ActionTaken.DISCARDED 110 ); 111 }