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 }