1 /*******************************************************************************
2 
3     The Logger hierarchy implementation.
4 
5     We keep a reference to each logger in a hash-table for convenient lookup
6     purposes, plus keep each logger linked to the others in an ordered group.
7     Ordering places shortest names at the head and longest ones at the tail,
8     making the job of identifying ancestors easier in an orderly fashion.
9     For example, when propagating levels across descendants it would be
10     a mistake to propagate to a child before all of its ancestors were
11     taken care of.
12 
13     Copyright:
14         Copyright (c) 2004 Kris Bell.
15         Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
16         All rights reserved.
17 
18     License:
19         Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
20         See LICENSE_TANGO.txt for details.
21 
22 *******************************************************************************/
23 
24 module ocean.util.log.Hierarchy;
25 
26 import ocean.core.ExceptionDefinitions;
27 import ocean.core.TypeConvert: assumeUnique;
28 import ocean.meta.types.Qualifiers;
29 import ocean.util.log.ILogger;
30 
31 
32 /// Ditto
33 package class HierarchyT (LoggerT) : ILogger.Context
34 {
35     private LoggerT             root_;
36     private istring             label_,
37                                 address_;
38     private ILogger.Context     context_;
39     private LoggerT[istring]    loggers;
40 
41 
42     /***************************************************************************
43 
44         Construct a hierarchy with the given name.
45 
46     ***************************************************************************/
47 
48     this (istring hlabel)
49     {
50         this.label_ = hlabel;
51         this.address_ = "network";
52 
53         // insert a root node; the root has an empty name
54         this.root_ = new LoggerT(this, "");
55         this.context_ = this;
56     }
57 
58     /***************************************************************************
59 
60         Returns:
61             The label associated with this Hierarchy
62 
63     ***************************************************************************/
64 
65     final istring label ()
66     {
67         return this.label_;
68     }
69 
70     /***************************************************************************
71 
72         Set the name of this Hierarchy
73 
74     ***************************************************************************/
75 
76     final void label (istring value)
77     {
78         this.label_ = value;
79     }
80 
81     /***************************************************************************
82 
83         Tells whether a given `level` is higher than another `test` level
84 
85     ***************************************************************************/
86 
87     final bool enabled (ILogger.Level level, ILogger.Level test)
88     {
89         return test >= level;
90     }
91 
92     /***************************************************************************
93 
94         Return the address of this Hierarchy.
95         This is typically attached when sending events to remote monitors.
96 
97     ***************************************************************************/
98 
99     final istring address ()
100     {
101         return this.address_;
102     }
103 
104     /***************************************************************************
105 
106         Set the address of this Hierarchy.
107         The address is attached used when sending events to remote monitors.
108 
109     ***************************************************************************/
110 
111     final void address (istring address)
112     {
113         this.address_ = address;
114     }
115 
116     /***************************************************************************
117 
118         Return the diagnostic context.
119         Useful for setting an override logging level.
120 
121     ***************************************************************************/
122 
123     final ILogger.Context context ()
124     {
125         return this.context_;
126     }
127 
128     /***************************************************************************
129 
130         Set the diagnostic context.
131 
132         Not usually necessary, as a default was created.
133         Useful when you need to provide a different implementation,
134         such as a ThreadLocal variant.
135 
136     ***************************************************************************/
137 
138     final void context (ILogger.Context context)
139     {
140         this.context_ = context;
141     }
142 
143     /***************************************************************************
144 
145         Return the root node.
146 
147     ***************************************************************************/
148 
149     final LoggerT root ()
150     {
151         return this.root_;
152     }
153 
154     /***************************************************************************
155 
156         Return the instance of a LoggerT with the provided label.
157         If the instance does not exist, it is created at this time.
158 
159         Note that an empty label is considered illegal, and will be ignored.
160 
161     ***************************************************************************/
162 
163     final LoggerT lookup (cstring label)
164     {
165         if (!label.length)
166             return null;
167 
168         return this.inject(
169             label,
170             (cstring name) { return new LoggerT (this, idup(name)); }
171             );
172     }
173 
174     /***************************************************************************
175 
176         Traverse the set of configured loggers
177 
178     ***************************************************************************/
179 
180     final int opApply (scope int delegate(ref LoggerT) dg)
181     {
182         int ret;
183 
184         for (auto log = this.root; log; log = log.next)
185             if ((ret = dg(log)) != 0)
186                 break;
187         return ret;
188     }
189 
190     /***************************************************************************
191 
192         Return the instance of a LoggerT with the provided label.
193         If the instance does not exist, it is created at this time.
194 
195     ***************************************************************************/
196 
197     private LoggerT inject (cstring label, scope LoggerT delegate(cstring name) dg)
198     {
199         // try not to allocate unless you really need to
200         char[255] stack_buffer;
201         mstring buffer = stack_buffer;
202 
203         if (buffer.length < label.length + 1)
204             buffer.length = label.length + 1;
205 
206         buffer[0 .. label.length] = label[];
207         buffer[label.length] = '.';
208 
209         auto name_ = buffer[0 .. label.length + 1];
210         cstring name;
211         auto l = name_ in loggers;
212 
213         if (l is null)
214         {
215             // don't use the stack allocated buffer
216             if (name_.ptr is stack_buffer.ptr)
217                 name = idup(name_);
218             else
219                 name = assumeUnique(name_);
220             // create a new logger
221             auto li = dg(name);
222             l = &li;
223 
224             // insert into linked list
225             insert (li);
226 
227             // look for and adjust children. Don't force
228             // property inheritance on existing loggers
229             update (li);
230 
231             // insert into map
232             loggers [name] = li;
233         }
234 
235         return *l;
236     }
237 
238     /***************************************************************************
239 
240         Loggers are maintained in a sorted linked-list. The order is maintained
241         such that the shortest name is at the root, and the longest at the tail.
242 
243         This is done so that updateLoggers() will always have a known
244         environment to manipulate, making it much faster.
245 
246     ***************************************************************************/
247 
248     private void insert (LoggerT l)
249     {
250         LoggerT prev,
251                 curr = this.root;
252 
253         while (curr)
254         {
255             // insert here if the new name is shorter
256             if (l.name.length < curr.name.length)
257                 if (prev is null)
258                     throw new IllegalElementException ("invalid hierarchy");
259                 else
260                 {
261                     l.next = prev.next;
262                     prev.next = l;
263                     return;
264                 }
265             else
266                 // find best match for parent of new entry
267                 // and inherit relevant properties (level, etc)
268                 this.propagate(l, curr, true);
269 
270             // remember where insertion point should be
271             prev = curr;
272             curr = curr.next;
273         }
274 
275         // add to tail
276         prev.next = l;
277     }
278 
279     /***************************************************************************
280 
281          Propagate hierarchical changes across known loggers.
282          This includes changes in the hierarchy itself, and to
283          the various settings of child loggers with respect to
284          their parent(s).
285 
286     ***************************************************************************/
287 
288     private void update (LoggerT changed, bool force = false)
289     {
290         foreach (logger; this)
291             this.propagate(logger, changed, force);
292     }
293 
294     /***************************************************************************
295 
296          Propagates the property to all child loggers.
297 
298          Params:
299             Property = property to set
300             T = type of the property
301             parent_name = name of the parent logger
302             value = value to set
303 
304     ***************************************************************************/
305 
306     package void propagateValue (istring property, T)
307         (istring parent_name, T value)
308     {
309         foreach (log; this)
310         {
311             if (log.isChildOf (parent_name))
312             {
313                 mixin("log." ~ property ~ " = value;");
314             }
315         }
316     }
317 
318     /***************************************************************************
319 
320         Propagate changes in the hierarchy downward to child Loggers.
321         Note that while 'parent' is always changed, the adjustment of
322         'level' is selectable.
323 
324     ***************************************************************************/
325 
326     private void propagate (LoggerT logger, LoggerT changed, bool force = false)
327     {
328         // is the changed instance a better match for our parent?
329         if (logger.isCloserAncestor(changed))
330         {
331             // update parent (might actually be current parent)
332             logger.parent = changed;
333 
334             // if we don't have an explicit level set, inherit it
335             // Be careful to avoid recursion, or other overhead
336             if (force)
337             {
338                 logger.level_ = changed.level;
339                 logger.collect_stats = changed.collect_stats;
340             }
341         }
342     }
343 }