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.core.Verify;
24 import ocean.core.ExceptionDefinitions;
25 import ocean.io.model.IConduit;
26 import ocean.text.convert.Formatter;
27 import ocean.meta.types.Qualifiers;
28 import ocean.meta.types.Typedef;
29 import ocean.util.log.Event;
30 import ocean.util.log.ILogger;
31 
32 version (unittest)
33 {
34     import ocean.core.Test;
35     import ocean.time.Clock;
36     import ocean.time.Time;
37     import ocean.util.log.Hierarchy;
38     import ocean.util.log.Logger;
39 }
40 
41 
42 /// Base class for all Appenders
43 public class Appender
44 {
45     mixin(Typedef!(int, "Mask"));
46 
47     private Appender        next_;
48     private ILogger.Level   level_;
49     private Layout          layout_;
50     private static Layout   generic;
51 
52     /***************************************************************************
53 
54         Interface for all logging layout instances
55 
56         Implement this method to perform the formatting of message content.
57 
58     ***************************************************************************/
59 
60     public interface Layout
61     {
62         /// Convenience alias for implementing classes
63         protected alias FormatterSink = .FormatterSink;
64 
65         /***********************************************************************
66 
67             Format the provided `event` to the `sink` using `this` layout
68 
69             Params:
70               event = The log event to format
71               sink = Where to output the result
72 
73         ***********************************************************************/
74 
75         void format (LogEvent event, scope FormatterSink sink);
76     }
77 
78     /***************************************************************************
79 
80         Return the mask used to identify this Appender.
81 
82         The mask is used to figure out whether an appender has already been
83         invoked for a particular logger.
84 
85     ***************************************************************************/
86 
87     abstract Mask mask ();
88 
89     /// Return the name of this Appender.
90     abstract cstring name ();
91 
92     /***************************************************************************
93 
94         Append a message to the output.
95 
96         The event received is only valid for the duration of the `apppend`
97         call and shouldn't outlive the scope of `append`.
98         Moreover, as `Logger` use a class-local buffer, its tracing functions
99         which use formatting are not re-entrant and should not be called
100         from here.
101 
102         Params:
103             event = Event to log
104 
105     ***************************************************************************/
106 
107     abstract void append (LogEvent event);
108 
109     /// Create an Appender and default its layout to LayoutTimer.
110     public this ()
111     {
112         if (generic is null)
113             generic = new LayoutTimer;
114         this.layout_ = generic;
115     }
116 
117     /// Return the current Level setting
118     final ILogger.Level level ()
119     {
120         return this.level_;
121     }
122 
123     /// Return the current Level setting
124     final Appender level (ILogger.Level l)
125     {
126         this.level_ = l;
127         return this;
128     }
129 
130     /***************************************************************************
131 
132         Static method to return a mask for identifying the Appender.
133 
134         Each Appender class should have a unique fingerprint so that we can
135         figure out which ones have been invoked for a given event.
136         A bitmask is a simple an efficient way to do that.
137 
138     ***************************************************************************/
139 
140     protected Mask register (cstring tag)
141     {
142         static Mask mask = 1;
143         static Mask[istring] registry;
144 
145         Mask* p = tag in registry;
146         if (p)
147             return *p;
148         else
149         {
150             auto ret = mask;
151             registry [tag] = mask;
152 
153             if (mask < 0)
154                 throw new IllegalArgumentException ("too many unique registrations");
155 
156             mask <<= 1;
157             return ret;
158         }
159     }
160 
161     /***************************************************************************
162 
163         Set the current layout to be that of the argument, or the generic layout
164         where the argument is null
165 
166     ***************************************************************************/
167 
168     void layout (Layout how)
169     {
170         verify(generic !is null);
171         this.layout_ = how ? how : generic;
172     }
173 
174     /// Return the current Layout
175     Layout layout ()
176     {
177         return this.layout_;
178     }
179 
180     /// Attach another appender to this one
181     void next (Appender appender)
182     {
183         this.next_ = appender;
184     }
185 
186     /// Return the next appender in the list
187     Appender next ()
188     {
189         return this.next_;
190     }
191 
192     /// Close this appender. This would be used for file, sockets, and the like
193     void close ()
194     {
195     }
196 }
197 
198 
199 /*******************************************************************************
200 
201     An appender that does nothing.
202 
203     This is useful for cutting and pasting, and for benchmarking the ocean.log
204     environment.
205 
206 *******************************************************************************/
207 
208 public class AppendNull : Appender
209 {
210     private Mask mask_;
211 
212     /// Create with the given Layout
213     this (Layout how = null)
214     {
215         this.mask_ = this.register(name);
216         this.layout(how);
217     }
218 
219     /// Return the fingerprint for this class
220     final override Mask mask ()
221     {
222         return this.mask_;
223     }
224 
225     /// Return the name of this class
226     final override cstring name ()
227     {
228         return this.classinfo.name;
229     }
230 
231     /// Append an event to the output
232     final override void append (LogEvent event)
233     {
234         this.layout.format(event, (cstring) {});
235     }
236 }
237 
238 
239 /// Append to a configured OutputStream
240 public class AppendStream : Appender
241 {
242     private Mask            mask_;
243     private bool            flush_;
244     private OutputStream    stream_;
245 
246     ///Create with the given stream and layout
247     this (OutputStream stream, bool flush = false, Appender.Layout how = null)
248     {
249         verify(stream !is null);
250 
251         this.mask_ = register (name);
252         this.stream_ = stream;
253         this.flush_ = flush;
254         this.layout(how);
255     }
256 
257     /// Return the fingerprint for this class
258     final override Mask mask ()
259     {
260         return this.mask_;
261     }
262 
263     /// Return the name of this class
264     override istring name ()
265     {
266         return this.classinfo.name;
267     }
268 
269     /// Append an event to the output.
270     final override void append (LogEvent event)
271     {
272         static immutable istring Eol = "\n";
273 
274         this.layout.format(event, (cstring content) { this.stream_.write(content); });
275         this.stream_.write(Eol);
276         if (this.flush_)
277             this.stream_.flush;
278     }
279 }
280 
281 
282 /*******************************************************************************
283 
284     A simple layout comprised only of time(ms), level, name, and message
285 
286 *******************************************************************************/
287 
288 public class LayoutTimer : Appender.Layout
289 {
290     /***************************************************************************
291 
292         Subclasses should implement this method to perform the formatting
293          of the actual message content.
294 
295     ***************************************************************************/
296 
297     public override void format (LogEvent event, scope FormatterSink dg)
298     {
299         sformat(dg, "{} {} [{}] {}- {}", event.span.millis(), event.levelName,
300             event.name, event.host.label, event);
301     }
302 }
303 
304 unittest
305 {
306        mstring result = new mstring(2048);
307        result.length = 0;
308        assumeSafeAppend(result);
309 
310        scope dg = (cstring v) { result ~= v; };
311        scope layout = new LayoutTimer();
312        LogEvent event = {
313            msg_: "Have you met Ted?",
314            name_: "Barney",
315            time_: Clock.startTime() + TimeSpan.fromMillis(420),
316            level_: ILogger.Level.Warn,
317            host_: new HierarchyT!(Logger)("test"),
318        };
319 
320        testNoAlloc(layout.format(event, dg));
321        test!("==")(result, "420 Warn [Barney] test- Have you met Ted?");
322 }