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