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 }