1 /******************************************************************************* 2 3 Define the base classes for all Appenders 4 5 Appenders are objects that are responsible for emitting messages sent 6 to a particular logger. There may be more than one appender attached 7 to any logger. 8 The actual message is constructed by another class known as an EventLayout. 9 10 Copyright: 11 Copyright (c) 2004 Kris Bell. 12 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 13 All rights reserved. 14 15 License: 16 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 17 See LICENSE_TANGO.txt for details. 18 19 *******************************************************************************/ 20 21 module ocean.util.log.Appender; 22 23 import ocean.transition; 24 import ocean.core.Verify; 25 import ocean.core.ExceptionDefinitions; 26 import ocean.io.model.IConduit; 27 import ocean.util.log.Event; 28 import ocean.util.log.model.ILogger; 29 30 31 /// Base class for all Appenders 32 public class Appender 33 { 34 mixin(Typedef!(int, "Mask")); 35 36 private Appender next_; 37 private ILogger.Level level_; 38 private Layout layout_; 39 private static Layout generic; 40 41 /*************************************************************************** 42 43 Interface for all logging layout instances 44 45 Implement this method to perform the formatting of message content. 46 47 ***************************************************************************/ 48 49 public interface Layout 50 { 51 void format (LogEvent event, scope void delegate(cstring) dg); 52 } 53 54 /*************************************************************************** 55 56 Return the mask used to identify this Appender. 57 58 The mask is used to figure out whether an appender has already been 59 invoked for a particular logger. 60 61 ***************************************************************************/ 62 63 abstract Mask mask (); 64 65 /// Return the name of this Appender. 66 abstract cstring name (); 67 68 /*************************************************************************** 69 70 Append a message to the output. 71 72 The event received is only valid for the duration of the `apppend` 73 call and shouldn't outlive the scope of `append`. 74 Moreover, as `Logger` use a class-local buffer, its tracing functions 75 which use formatting are not re-entrant and should not be called 76 from here. 77 78 Params: 79 event = Event to log 80 81 ***************************************************************************/ 82 83 abstract void append (LogEvent event); 84 85 /// Create an Appender and default its layout to LayoutTimer. 86 public this () 87 { 88 if (generic is null) 89 generic = new LayoutTimer; 90 this.layout_ = generic; 91 } 92 93 /// Return the current Level setting 94 final ILogger.Level level () 95 { 96 return this.level_; 97 } 98 99 /// Return the current Level setting 100 final Appender level (ILogger.Level l) 101 { 102 this.level_ = l; 103 return this; 104 } 105 106 /*************************************************************************** 107 108 Static method to return a mask for identifying the Appender. 109 110 Each Appender class should have a unique fingerprint so that we can 111 figure out which ones have been invoked for a given event. 112 A bitmask is a simple an efficient way to do that. 113 114 ***************************************************************************/ 115 116 protected Mask register (cstring tag) 117 { 118 static Mask mask = 1; 119 static Mask[istring] registry; 120 121 Mask* p = tag in registry; 122 if (p) 123 return *p; 124 else 125 { 126 auto ret = mask; 127 registry [tag] = mask; 128 129 if (mask < 0) 130 throw new IllegalArgumentException ("too many unique registrations"); 131 132 mask <<= 1; 133 return ret; 134 } 135 } 136 137 /*************************************************************************** 138 139 Set the current layout to be that of the argument, or the generic layout 140 where the argument is null 141 142 ***************************************************************************/ 143 144 void layout (Layout how) 145 { 146 verify(generic !is null); 147 this.layout_ = how ? how : generic; 148 } 149 150 /// Return the current Layout 151 Layout layout () 152 { 153 return this.layout_; 154 } 155 156 /// Attach another appender to this one 157 void next (Appender appender) 158 { 159 this.next_ = appender; 160 } 161 162 /// Return the next appender in the list 163 Appender next () 164 { 165 return this.next_; 166 } 167 168 /// Close this appender. This would be used for file, sockets, and the like 169 void close () 170 { 171 } 172 } 173 174 175 /******************************************************************************* 176 177 An appender that does nothing. 178 179 This is useful for cutting and pasting, and for benchmarking the ocean.log 180 environment. 181 182 *******************************************************************************/ 183 184 public class AppendNull : Appender 185 { 186 private Mask mask_; 187 188 /// Create with the given Layout 189 this (Layout how = null) 190 { 191 this.mask_ = this.register(name); 192 this.layout(how); 193 } 194 195 /// Return the fingerprint for this class 196 final override Mask mask () 197 { 198 return this.mask_; 199 } 200 201 /// Return the name of this class 202 final override cstring name () 203 { 204 return this.classinfo.name; 205 } 206 207 /// Append an event to the output 208 final override void append (LogEvent event) 209 { 210 this.layout.format(event, (cstring) {}); 211 } 212 } 213 214 215 /// Append to a configured OutputStream 216 public class AppendStream : Appender 217 { 218 private Mask mask_; 219 private bool flush_; 220 private OutputStream stream_; 221 222 ///Create with the given stream and layout 223 this (OutputStream stream, bool flush = false, Appender.Layout how = null) 224 { 225 verify(stream !is null); 226 227 this.mask_ = register (name); 228 this.stream_ = stream; 229 this.flush_ = flush; 230 this.layout(how); 231 } 232 233 /// Return the fingerprint for this class 234 final override Mask mask () 235 { 236 return this.mask_; 237 } 238 239 /// Return the name of this class 240 override istring name () 241 { 242 return this.classinfo.name; 243 } 244 245 /// Append an event to the output. 246 final override void append (LogEvent event) 247 { 248 static immutable istring Eol = "\n"; 249 250 this.layout.format(event, (cstring content) { this.stream_.write(content); }); 251 this.stream_.write(Eol); 252 if (this.flush_) 253 this.stream_.flush; 254 } 255 } 256 257 258 /******************************************************************************* 259 260 A simple layout comprised only of time(ms), level, name, and message 261 262 *******************************************************************************/ 263 264 public class LayoutTimer : Appender.Layout 265 { 266 /*************************************************************************** 267 268 Subclasses should implement this method to perform the formatting 269 of the actual message content. 270 271 ***************************************************************************/ 272 273 public override void format (LogEvent event, scope void delegate(cstring) dg) 274 { 275 char[20] tmp = void; 276 277 dg(event.toMilli (tmp, event.span)); 278 dg(" "); 279 dg(event.levelName); 280 dg(" ["); 281 dg(event.name); 282 dg("] "); 283 dg(event.host.label); 284 dg("- "); 285 dg(event.toString); 286 } 287 }