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 : ubyte
40     {
41         /// The lowest level: Used for programming debug statement
42         Debug,
43         /// Trace messages let the user "trace" the program behavior,
44         /// e.g. what function calls are made (or when they exit)
45         Trace,
46         /// Verbose message provide extra informations about the program
47         Verbose,
48         /// Informative message, this is the "default" value for the user
49         Info,
50         /// Warnings about potential issues
51         Warn,
52         /// Notify the user of a hard error
53         Error,
54         /// A Fatal error, which could lead to the program termination
55         Fatal,
56         /// No message should be output
57         None,
58     };
59 
60     /// Internal struct to associate a `Level` with its name
61     private struct Pair
62     {
63         /// The name associated with `value`
64         istring name;
65         /// An `ILogger.Level` value
66         Level value;
67     }
68 
69     /***************************************************************************
70 
71         Poor man's SmartEnum: We don't use SmartEnum directly because
72         it would change the public interface, and we accept any case anyway.
73 
74         This can be fixed when we drop D1 support.
75 
76     ***************************************************************************/
77 
78     private static immutable Pair[Level.max + 1] Pairs =
79     [
80         { "Debug",   Level.Debug },
81         { "Trace",   Level.Trace },
82         { "Verbose", Level.Verbose },
83         { "Info",    Level.Info },
84         { "Warn",    Level.Warn },
85         { "Error",   Level.Error },
86         { "Fatal",   Level.Fatal },
87         { "None",    Level.None },
88     ];
89 
90     /***************************************************************************
91 
92         Return the enum value associated with `name`, or a default value
93 
94         Params:
95             name = Case-independent string representation of an `ILogger.Level`
96                    If the name is not one of the logger, `def` is returned.
97             def  = Default value to return if no match is found for `name`
98 
99         Returns:
100             The `Level` value for `name`, or `def`
101 
102     ***************************************************************************/
103 
104     public static Level convert (cstring name, Level def = Level.Trace)
105     {
106         foreach (field; ILogger.Pairs)
107         {
108             if (field.name.length == name.length
109                 && !strncasecmp(name.ptr, field.name.ptr, name.length))
110                 return field.value;
111         }
112         return def;
113     }
114 
115     /***************************************************************************
116 
117         Return the name associated with level
118 
119         Params:
120             level = The `Level` to get the name for
121 
122         Returns:
123             The name associated with `level`.
124 
125     ***************************************************************************/
126 
127     public static istring convert (Level level)
128     {
129         return ILogger.Pairs[level].name;
130     }
131 
132 
133     /***************************************************************************
134 
135         Context for a hierarchy, used for customizing behaviour of log
136         hierarchies. You can use this to implement dynamic log-levels,
137         based upon filtering or some other mechanism
138 
139     ***************************************************************************/
140 
141     public interface Context
142     {
143         /// return a label for this context
144         public istring label ();
145 
146         /// first arg is the setting of the logger itself, and
147         /// the second arg is what kind of message we're being
148         /// asked to produce
149         public bool enabled (Level setting, Level target);
150     }
151 
152 
153     /***************************************************************************
154 
155         Returns:
156             `true` if this logger is enabed for the specified `Level`
157 
158         Params:
159             `Level` to test for, defaults to `Level.Fatal`.
160 
161     ***************************************************************************/
162 
163     public bool enabled (Level level = Level.Fatal);
164 
165     /***************************************************************************
166 
167         Returns:
168             The name of this `ILogger` (without the appended dot).
169 
170     ***************************************************************************/
171 
172     public cstring name ();
173 
174 
175     /***************************************************************************
176 
177         Returns:
178             The `Level` this `ILogger` is set to
179 
180     ***************************************************************************/
181 
182     public Level level ();
183 
184     /***************************************************************************
185 
186         Set the current `Level` for this logger (and only this logger).
187 
188         Params:
189             l = New `Level` value to set this logger to.
190 
191         Returns:
192             `this` for easy chaining
193 
194     ***************************************************************************/
195 
196     public ILogger level (Level l);
197 
198     /***************************************************************************
199 
200         Returns:
201             `true` if the logger is additive.
202             Additive loggers walk through ancestors looking for more appenders
203 
204     ***************************************************************************/
205 
206     public bool additive ();
207 
208     /***************************************************************************
209 
210         Set the additive status of this logger
211 
212         Additive loggers walk through ancestors looking for more appenders
213 
214         Params:
215             enabled = Whereas this logger is additive.
216 
217         Returns:
218             `this` for easy chaining
219 
220     ***************************************************************************/
221 
222     public ILogger additive (bool enabled);
223 
224     /***************************************************************************
225 
226         Send a message to this logger.
227 
228         Params:
229             level = Level at which to log the message
230             exp   = Lazily evaluated message string
231                     If the `level` is not enabled for this logger, it won't
232                     be evaluated.
233 
234         Returns:
235             `this` for easy chaining
236 
237     ***************************************************************************/
238 
239     public ILogger append (Level level, lazy cstring exp);
240 }
241 
242 unittest
243 {
244     test!("==")(ILogger.convert(ILogger.Level.Trace), "Trace");
245     test!("==")(ILogger.convert(ILogger.Level.Info), "Info");
246     test!("==")(ILogger.convert(ILogger.Level.Warn), "Warn");
247     test!("==")(ILogger.convert(ILogger.Level.Error), "Error");
248     test!("==")(ILogger.convert(ILogger.Level.Fatal), "Fatal");
249     test!("==")(ILogger.convert(ILogger.Level.None), "None");
250 }
251 
252 unittest
253 {
254     test!("==")(ILogger.convert("info"), ILogger.Level.Info);
255     test!("==")(ILogger.convert("Info"), ILogger.Level.Info);
256     test!("==")(ILogger.convert("INFO"), ILogger.Level.Info);
257     test!("==")(ILogger.convert("FATAL"), ILogger.Level.Fatal);
258     // Use the default value
259     test!("==")(ILogger.convert("Info!"), ILogger.Level.Trace);
260     test!("==")(ILogger.convert("Baguette", ILogger.Level.Warn),
261                 ILogger.Level.Warn);
262     // The first entry in the array
263     test!("==")(ILogger.convert("trace", ILogger.Level.Error),
264                 ILogger.Level.Trace);
265 }