1 /*******************************************************************************
2 
3     Base interface for Loggers implementation
4 
5     Note:
6         The formatting primitives (error, info, warn...) are not part of the
7         interface anymore, as they can be templated functions.
8 
9     Copyright:
10         Copyright (c) 2004 Kris Bell.
11         Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
12         All rights reserved.
13 
14     License:
15         Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
16         See LICENSE_TANGO.txt for details.
17 
18     Version: Initial release: May 2004
19 
20     Authors: Kris
21 
22 *******************************************************************************/
23 
24 module ocean.util.log.ILogger;
25 
26 import ocean.meta.types.Qualifiers;
27 import core.sys.posix.strings;
28 
29 version (unittest)
30 {
31     import ocean.core.Test;
32 }
33 
34 
35 /// Ditto
36 interface ILogger
37 {
38     /// Defines the level at which a message can be logged
39     public enum Level
40     {
41         ///
42         Trace = 0,
43         ///
44         Info,
45         ///
46         Warn,
47         ///
48         Error,
49         ///
50         Fatal,
51         ///
52         None
53     };
54 
55     /// Internal struct to associate a `Level` with its name
56     private struct Pair
57     {
58         /// The name associated with `value`
59         istring name;
60         /// An `ILogger.Level` value
61         Level value;
62     }
63 
64     /***************************************************************************
65 
66         Poor man's SmartEnum: We don't use SmartEnum directly because
67         it would change the public interface, and we accept any case anyway.
68 
69         This can be fixed when we drop D1 support.
70 
71     ***************************************************************************/
72 
73     private static immutable Pair[Level.max + 1] Pairs =
74     [
75         { "Trace",  Level.Trace },
76         { "Info",   Level.Info },
77         { "Warn",   Level.Warn },
78         { "Error",  Level.Error },
79         { "Fatal",  Level.Fatal },
80         { "None",   Level.None }
81     ];
82 
83     /***************************************************************************
84 
85         Return the enum value associated with `name`, or a default value
86 
87         Params:
88             name = Case-independent string representation of an `ILogger.Level`
89                    If the name is not one of the logger, `def` is returned.
90             def  = Default value to return if no match is found for `name`
91 
92         Returns:
93             The `Level` value for `name`, or `def`
94 
95     ***************************************************************************/
96 
97     public static Level convert (cstring name, Level def = Level.Trace)
98     {
99         foreach (field; ILogger.Pairs)
100         {
101             if (field.name.length == name.length
102                 && !strncasecmp(name.ptr, field.name.ptr, name.length))
103                 return field.value;
104         }
105         return def;
106     }
107 
108     /***************************************************************************
109 
110         Return the name associated with level
111 
112         Params:
113             level = The `Level` to get the name for
114 
115         Returns:
116             The name associated with `level`.
117 
118     ***************************************************************************/
119 
120     public static istring convert (Level level)
121     {
122         return ILogger.Pairs[level].name;
123     }
124 
125 
126     /***************************************************************************
127 
128         Context for a hierarchy, used for customizing behaviour of log
129         hierarchies. You can use this to implement dynamic log-levels,
130         based upon filtering or some other mechanism
131 
132     ***************************************************************************/
133 
134     public interface Context
135     {
136         /// return a label for this context
137         public istring label ();
138 
139         /// first arg is the setting of the logger itself, and
140         /// the second arg is what kind of message we're being
141         /// asked to produce
142         public bool enabled (Level setting, Level target);
143     }
144 
145 
146     /***************************************************************************
147 
148         Returns:
149             `true` if this logger is enabed for the specified `Level`
150 
151         Params:
152             `Level` to test for, defaults to `Level.Fatal`.
153 
154     ***************************************************************************/
155 
156     public bool enabled (Level level = Level.Fatal);
157 
158     /***************************************************************************
159 
160         Returns:
161             The name of this `ILogger` (without the appended dot).
162 
163     ***************************************************************************/
164 
165     public cstring name ();
166 
167 
168     /***************************************************************************
169 
170         Returns:
171             The `Level` this `ILogger` is set to
172 
173     ***************************************************************************/
174 
175     public Level level ();
176 
177     /***************************************************************************
178 
179         Set the current `Level` for this logger (and only this logger).
180 
181         Params:
182             l = New `Level` value to set this logger to.
183 
184         Returns:
185             `this` for easy chaining
186 
187     ***************************************************************************/
188 
189     public ILogger level (Level l);
190 
191     /***************************************************************************
192 
193         Returns:
194             `true` if the logger is additive.
195             Additive loggers walk through ancestors looking for more appenders
196 
197     ***************************************************************************/
198 
199     public bool additive ();
200 
201     /***************************************************************************
202 
203         Set the additive status of this logger
204 
205         Additive loggers walk through ancestors looking for more appenders
206 
207         Params:
208             enabled = Whereas this logger is additive.
209 
210         Returns:
211             `this` for easy chaining
212 
213     ***************************************************************************/
214 
215     public ILogger additive (bool enabled);
216 
217     /***************************************************************************
218 
219         Send a message to this logger.
220 
221         Params:
222             level = Level at which to log the message
223             exp   = Lazily evaluated message string
224                     If the `level` is not enabled for this logger, it won't
225                     be evaluated.
226 
227         Returns:
228             `this` for easy chaining
229 
230     ***************************************************************************/
231 
232     public ILogger append (Level level, lazy cstring exp);
233 }
234 
235 unittest
236 {
237     test!("==")(ILogger.convert(ILogger.Level.Trace), "Trace");
238     test!("==")(ILogger.convert(ILogger.Level.Info), "Info");
239     test!("==")(ILogger.convert(ILogger.Level.Warn), "Warn");
240     test!("==")(ILogger.convert(ILogger.Level.Error), "Error");
241     test!("==")(ILogger.convert(ILogger.Level.Fatal), "Fatal");
242     test!("==")(ILogger.convert(ILogger.Level.None), "None");
243 }
244 
245 unittest
246 {
247     test!("==")(ILogger.convert("info"), ILogger.Level.Info);
248     test!("==")(ILogger.convert("Info"), ILogger.Level.Info);
249     test!("==")(ILogger.convert("INFO"), ILogger.Level.Info);
250     test!("==")(ILogger.convert("FATAL"), ILogger.Level.Fatal);
251     // Use the default value
252     test!("==")(ILogger.convert("Info!"), ILogger.Level.Trace);
253     test!("==")(ILogger.convert("Baguette", ILogger.Level.Warn),
254                 ILogger.Level.Warn);
255     // The first entry in the array
256     test!("==")(ILogger.convert("trace", ILogger.Level.Error),
257                 ILogger.Level.Trace);
258 }