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 }