1 // 2 // System.Diagnostics.DiagnosticsConfigurationHandler.cs 3 // 4 // Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation 5 // can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics 6 // 7 // Authors: 8 // John R. Hicks <angryjohn69@nc.rr.com> 9 // Jonathan Pryor <jonpryor@vt.edu> 10 // 11 // (C) 2002, 2005 12 // 13 14 // 15 // Permission is hereby granted, free of charge, to any person obtaining 16 // a copy of this software and associated documentation files (the 17 // "Software"), to deal in the Software without restriction, including 18 // without limitation the rights to use, copy, modify, merge, publish, 19 // distribute, sublicense, and/or sell copies of the Software, and to 20 // permit persons to whom the Software is furnished to do so, subject to 21 // the following conditions: 22 // 23 // The above copyright notice and this permission notice shall be 24 // included in all copies or substantial portions of the Software. 25 // 26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 // 34 using System; 35 using System.Collections; 36 using System.Collections.Specialized; 37 using System.Configuration; 38 using System.Reflection; 39 using System.Threading; 40 #if (XML_DEP) 41 using System.Xml; 42 #endif 43 namespace System.Diagnostics 44 { 45 /* 46 // It handles following elements in <system.diagnostics> : 47 // - <sharedListeners> [2.0] 48 // - <sources> 49 // - <source> 50 // - <listeners> (collection) 51 // - <switches> 52 // - <add name=string value=string /> 53 // - <trace autoflush=bool indentsize=int useGlobalLock=bool> 54 // - <listeners> 55 internal sealed class DiagnosticsConfiguration 56 { 57 #if NO_LOCK_FREE 58 private static object lock_ = new object(); 59 #endif 60 private static object settings; 61 62 public static IDictionary Settings { 63 get { 64 #if !NO_LOCK_FREE 65 if (settings == null) { 66 object s = ConfigurationSettings.GetConfig ("system.diagnostics"); 67 if (s == null) 68 throw new Exception ("INTERNAL configuration error: failed to get configuration 'system.diagnostics'"); 69 Thread.MemoryBarrier (); 70 while (Interlocked.CompareExchange (ref settings, s, null) == null) { 71 // do nothing; we're just setting settings. 72 } 73 Thread.MemoryBarrier (); 74 } 75 #else 76 lock (lock_) { 77 if (settings == null) 78 settings = ConfigurationSettings.GetConfig ("system.diagnostics"); 79 } 80 #endif 81 return (IDictionary) settings; 82 } 83 } 84 } 85 */ 86 87 #if (XML_DEP) 88 [Obsolete ("This class is obsoleted")] 89 public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler 90 { 91 TraceImplSettings configValues; 92 ElementHandler(IDictionary d, XmlNode node)93 delegate void ElementHandler (IDictionary d, XmlNode node); 94 95 IDictionary elementHandlers = new Hashtable (); 96 DiagnosticsConfigurationHandler()97 public DiagnosticsConfigurationHandler () 98 { 99 elementHandlers ["assert"] = new ElementHandler (AddAssertNode); 100 elementHandlers ["performanceCounters"] = new ElementHandler (AddPerformanceCountersNode); 101 elementHandlers ["switches"] = new ElementHandler (AddSwitchesNode); 102 elementHandlers ["trace"] = new ElementHandler (AddTraceNode); 103 elementHandlers ["sources"] = new ElementHandler (AddSourcesNode); 104 } 105 Create(object parent, object configContext, XmlNode section)106 public virtual object Create (object parent, object configContext, XmlNode section) 107 { 108 IDictionary d; 109 if (parent == null) 110 d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default); 111 else 112 d = (IDictionary) ((ICloneable)parent).Clone(); 113 114 if (d.Contains (TraceImplSettings.Key)) 115 configValues = (TraceImplSettings) d [TraceImplSettings.Key]; 116 else 117 d.Add (TraceImplSettings.Key, configValues = new TraceImplSettings ()); 118 119 // process <sharedListeners> first 120 foreach (XmlNode child in section.ChildNodes) { 121 switch (child.NodeType) { 122 case XmlNodeType.Element: 123 if (child.LocalName != "sharedListeners") 124 continue; 125 AddTraceListeners (d, child, GetSharedListeners (d)); 126 break; 127 } 128 } 129 130 foreach (XmlNode child in section.ChildNodes) { 131 XmlNodeType type = child.NodeType; 132 133 switch (type) { 134 /* ignore */ 135 case XmlNodeType.Whitespace: 136 case XmlNodeType.Comment: 137 continue; 138 case XmlNodeType.Element: 139 if (child.LocalName == "sharedListeners") 140 continue; 141 ElementHandler eh = (ElementHandler) elementHandlers [child.Name]; 142 if (eh != null) 143 eh (d, child); 144 else 145 ThrowUnrecognizedElement (child); 146 break; 147 default: 148 ThrowUnrecognizedElement (child); 149 break; 150 } 151 } 152 153 return d; 154 } 155 156 // Remarks: Both attribute are optional AddAssertNode(IDictionary d, XmlNode node)157 private void AddAssertNode (IDictionary d, XmlNode node) 158 { 159 XmlAttributeCollection c = node.Attributes; 160 string assertuienabled = GetAttribute (c, "assertuienabled", false, node); 161 string logfilename = GetAttribute (c, "logfilename", false, node); 162 ValidateInvalidAttributes (c, node); 163 if (assertuienabled != null) { 164 try { 165 d ["assertuienabled"] = bool.Parse (assertuienabled); 166 } 167 catch (Exception e) { 168 throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'", 169 e, node); 170 } 171 } 172 173 if (logfilename != null) 174 d ["logfilename"] = logfilename; 175 176 DefaultTraceListener dtl = (DefaultTraceListener) configValues.Listeners["Default"]; 177 if (dtl != null) { 178 if (assertuienabled != null) 179 dtl.AssertUiEnabled = (bool) d ["assertuienabled"]; 180 if (logfilename != null) 181 dtl.LogFileName = logfilename; 182 } 183 184 if (node.ChildNodes.Count > 0) 185 ThrowUnrecognizedElement (node.ChildNodes[0]); 186 } 187 AddPerformanceCountersNode(IDictionary d, XmlNode node)188 private void AddPerformanceCountersNode (IDictionary d, XmlNode node) 189 { 190 XmlAttributeCollection c = node.Attributes; 191 string filemappingsize = GetAttribute (c, "filemappingsize", false, node); 192 ValidateInvalidAttributes (c, node); 193 if (filemappingsize != null) { 194 try { 195 d ["filemappingsize"] = int.Parse (filemappingsize); 196 } 197 catch (Exception e) { 198 throw new ConfigurationException ("The `filemappingsize' attribute must be an integral value.", 199 e, node); 200 } 201 } 202 203 if (node.ChildNodes.Count > 0) 204 ThrowUnrecognizedElement (node.ChildNodes[0]); 205 } 206 207 // name and value attributes are required 208 // Docs do not define "remove" or "clear" elements, but .NET recognizes 209 // them AddSwitchesNode(IDictionary d, XmlNode node)210 private void AddSwitchesNode (IDictionary d, XmlNode node) 211 { 212 // There are no attributes on <switch/> 213 ValidateInvalidAttributes (node.Attributes, node); 214 215 IDictionary newNodes = new Hashtable (); 216 217 foreach (XmlNode child in node.ChildNodes) { 218 XmlNodeType t = child.NodeType; 219 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment) 220 continue; 221 if (t == XmlNodeType.Element) { 222 XmlAttributeCollection attributes = child.Attributes; 223 string name = null; 224 string value = null; 225 switch (child.Name) { 226 case "add": 227 name = GetAttribute (attributes, "name", true, child); 228 value = GetAttribute (attributes, "value", true, child); 229 newNodes [name] = GetSwitchValue (name, value); 230 break; 231 case "remove": 232 name = GetAttribute (attributes, "name", true, child); 233 newNodes.Remove (name); 234 break; 235 case "clear": 236 newNodes.Clear (); 237 break; 238 default: 239 ThrowUnrecognizedElement (child); 240 break; 241 } 242 ValidateInvalidAttributes (attributes, child); 243 } 244 else 245 ThrowUnrecognizedNode (child); 246 } 247 248 d [node.Name] = newNodes; 249 } 250 GetSwitchValue(string name, string value)251 private static object GetSwitchValue (string name, string value) 252 { 253 return value; 254 } 255 AddTraceNode(IDictionary d, XmlNode node)256 private void AddTraceNode (IDictionary d, XmlNode node) 257 { 258 AddTraceAttributes (d, node); 259 260 foreach (XmlNode child in node.ChildNodes) { 261 XmlNodeType t = child.NodeType; 262 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment) 263 continue; 264 if (t == XmlNodeType.Element) { 265 if (child.Name == "listeners") 266 AddTraceListeners (d, child, configValues.Listeners); 267 else 268 ThrowUnrecognizedElement (child); 269 ValidateInvalidAttributes (child.Attributes, child); 270 } 271 else 272 ThrowUnrecognizedNode (child); 273 } 274 } 275 276 // all attributes are optional AddTraceAttributes(IDictionary d, XmlNode node)277 private void AddTraceAttributes (IDictionary d, XmlNode node) 278 { 279 XmlAttributeCollection c = node.Attributes; 280 string autoflushConf = GetAttribute (c, "autoflush", false, node); 281 string indentsizeConf = GetAttribute (c, "indentsize", false, node); 282 ValidateInvalidAttributes (c, node); 283 if (autoflushConf != null) { 284 bool autoflush = false; 285 try { 286 autoflush = bool.Parse (autoflushConf); 287 d ["autoflush"] = autoflush; 288 } catch (Exception e) { 289 throw new ConfigurationException ("The `autoflush' attribute must be `true' or `false'", 290 e, node); 291 } 292 configValues.AutoFlush = autoflush; 293 } 294 if (indentsizeConf != null) { 295 int indentsize = 0; 296 try { 297 indentsize = int.Parse (indentsizeConf); 298 d ["indentsize"] = indentsize; 299 } catch (Exception e) { 300 throw new ConfigurationException ("The `indentsize' attribute must be an integral value.", 301 e, node); 302 } 303 configValues.IndentSize = indentsize; 304 } 305 } 306 GetSharedListeners(IDictionary d)307 private TraceListenerCollection GetSharedListeners (IDictionary d) 308 { 309 TraceListenerCollection shared_listeners = d ["sharedListeners"] as TraceListenerCollection; 310 if (shared_listeners == null) { 311 shared_listeners = new TraceListenerCollection (); 312 d ["sharedListeners"] = shared_listeners; 313 } 314 return shared_listeners; 315 } 316 AddSourcesNode(IDictionary d, XmlNode node)317 private void AddSourcesNode (IDictionary d, XmlNode node) 318 { 319 // FIXME: are there valid attributes? 320 ValidateInvalidAttributes (node.Attributes, node); 321 Hashtable sources = d ["sources"] as Hashtable; 322 if (sources == null) { 323 sources = new Hashtable (); 324 d ["sources"] = sources; 325 } 326 327 foreach (XmlNode child in node.ChildNodes) { 328 XmlNodeType t = child.NodeType; 329 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment) 330 continue; 331 if (t == XmlNodeType.Element) { 332 if (child.Name == "source") 333 AddTraceSource (d, sources, child); 334 else 335 ThrowUnrecognizedElement (child); 336 // ValidateInvalidAttributes (child.Attributes, child); 337 } 338 else 339 ThrowUnrecognizedNode (child); 340 } 341 } 342 AddTraceSource(IDictionary d, Hashtable sources, XmlNode node)343 private void AddTraceSource (IDictionary d, Hashtable sources, XmlNode node) 344 { 345 string name = null; 346 SourceLevels levels = SourceLevels.Error; 347 StringDictionary atts = new StringDictionary (); 348 foreach (XmlAttribute a in node.Attributes) { 349 switch (a.Name) { 350 case "name": 351 name = a.Value; 352 break; 353 case "switchValue": 354 levels = (SourceLevels) Enum.Parse (typeof (SourceLevels), a.Value); 355 break; 356 default: 357 atts [a.Name] = a.Value; 358 break; 359 } 360 } 361 if (name == null) 362 throw new ConfigurationException ("Mandatory attribute 'name' is missing in 'source' element."); 363 364 // ignore duplicate ones (no error occurs) 365 if (sources.ContainsKey (name)) 366 return; 367 368 TraceSourceInfo sinfo = new TraceSourceInfo (name, levels, configValues); 369 sources.Add (sinfo.Name, sinfo); 370 371 foreach (XmlNode child in node.ChildNodes) { 372 XmlNodeType t = child.NodeType; 373 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment) 374 continue; 375 if (t == XmlNodeType.Element) { 376 if (child.Name == "listeners") 377 AddTraceListeners (d, child, sinfo.Listeners); 378 else 379 ThrowUnrecognizedElement (child); 380 ValidateInvalidAttributes (child.Attributes, child); 381 } 382 else 383 ThrowUnrecognizedNode (child); 384 } 385 } 386 387 // only defines "add" and "remove", but "clear" also works 388 // for add, "name" is required; initializeData is optional; "type" is required in 1.x, optional in 2.0. AddTraceListeners(IDictionary d, XmlNode listenersNode, TraceListenerCollection listeners)389 private void AddTraceListeners (IDictionary d, XmlNode listenersNode, TraceListenerCollection listeners) 390 { 391 // There are no attributes on <listeners/> 392 ValidateInvalidAttributes (listenersNode.Attributes, listenersNode); 393 394 foreach (XmlNode child in listenersNode.ChildNodes) { 395 XmlNodeType t = child.NodeType; 396 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment) 397 continue; 398 if (t == XmlNodeType.Element) { 399 XmlAttributeCollection attributes = child.Attributes; 400 string name = null; 401 switch (child.Name) { 402 case "add": 403 AddTraceListener (d, child, attributes, listeners); 404 break; 405 case "remove": 406 name = GetAttribute (attributes, "name", true, child); 407 RemoveTraceListener (name); 408 break; 409 case "clear": 410 configValues.Listeners.Clear (); 411 break; 412 default: 413 ThrowUnrecognizedElement (child); 414 break; 415 } 416 ValidateInvalidAttributes (attributes, child); 417 } 418 else 419 ThrowUnrecognizedNode (child); 420 } 421 } 422 AddTraceListener(IDictionary d, XmlNode child, XmlAttributeCollection attributes, TraceListenerCollection listeners)423 private void AddTraceListener (IDictionary d, XmlNode child, XmlAttributeCollection attributes, TraceListenerCollection listeners) 424 { 425 string name = GetAttribute (attributes, "name", true, child); 426 string type = null; 427 428 #if CONFIGURATION_DEP 429 type = GetAttribute (attributes, "type", false, child); 430 if (type == null) { 431 // indicated by name. 432 TraceListener shared = GetSharedListeners (d) [name]; 433 if (shared == null) 434 throw new ConfigurationException (String.Format ("Shared trace listener {0} does not exist.", name)); 435 if (attributes.Count != 0) 436 throw new ConfigurationErrorsException (string.Format ( 437 "Listener '{0}' references a shared " + 438 "listener and can only have a 'Name' " + 439 "attribute.", name)); 440 shared.IndentSize = configValues.IndentSize; 441 listeners.Add (shared); 442 return; 443 } 444 #else 445 type = GetAttribute (attributes, "type", true, child); 446 #endif 447 448 Type t = Type.GetType (type); 449 if (t == null) 450 throw new ConfigurationException (string.Format ("Invalid Type Specified: {0}", type)); 451 452 object[] args; 453 Type[] types; 454 455 string initializeData = GetAttribute (attributes, "initializeData", false, child); 456 if (initializeData != null) { 457 args = new object[] { initializeData }; 458 types = new Type[] { typeof(string) }; 459 } else { 460 args = null; 461 types = Type.EmptyTypes; 462 } 463 464 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; 465 if (t.Assembly == GetType ().Assembly) 466 flags |= BindingFlags.NonPublic; 467 468 ConstructorInfo ctor = t.GetConstructor (flags, null, types, null); 469 if (ctor == null) 470 throw new ConfigurationException ("Couldn't find constructor for class " + type); 471 472 TraceListener l = (TraceListener) ctor.Invoke (args); 473 l.Name = name; 474 475 #if CONFIGURATION_DEP 476 string trace = GetAttribute (attributes, "traceOutputOptions", false, child); 477 if (trace != null) { 478 if (trace != trace.Trim ()) 479 throw new ConfigurationErrorsException (string.Format ( 480 "Invalid value '{0}' for 'traceOutputOptions'.", 481 trace), child); 482 483 TraceOptions trace_options; 484 485 try { 486 trace_options = (TraceOptions) Enum.Parse ( 487 typeof (TraceOptions), trace); 488 } catch (ArgumentException) { 489 throw new ConfigurationErrorsException (string.Format ( 490 "Invalid value '{0}' for 'traceOutputOptions'.", 491 trace), child); 492 } 493 494 l.TraceOutputOptions = trace_options; 495 } 496 497 string [] supported_attributes = l.GetSupportedAttributes (); 498 if (supported_attributes != null) { 499 for (int i = 0; i < supported_attributes.Length; i++) { 500 string key = supported_attributes [i]; 501 string value = GetAttribute (attributes, key, false, child); 502 if (value != null) 503 l.Attributes.Add (key, value); 504 } 505 } 506 #endif 507 508 l.IndentSize = configValues.IndentSize; 509 listeners.Add (l); 510 } 511 RemoveTraceListener(string name)512 private void RemoveTraceListener (string name) 513 { 514 try { 515 configValues.Listeners.Remove (name); 516 } 517 catch (ArgumentException) { 518 // The specified listener wasn't in the collection 519 // Ignore this; .NET does. 520 } 521 catch (Exception e) { 522 throw new ConfigurationException ( 523 string.Format ("Unknown error removing listener: {0}", name), 524 e); 525 } 526 } 527 GetAttribute(XmlAttributeCollection attrs, string attr, bool required, XmlNode node)528 private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node) 529 { 530 XmlAttribute a = attrs[attr]; 531 532 string r = null; 533 534 if (a != null) { 535 r = a.Value; 536 if (required) 537 ValidateAttribute (attr, r, node); 538 attrs.Remove (a); 539 } 540 else if (required) 541 ThrowMissingAttribute (attr, node); 542 543 return r; 544 } 545 ValidateAttribute(string attribute, string value, XmlNode node)546 private void ValidateAttribute (string attribute, string value, XmlNode node) 547 { 548 if (value == null || value.Length == 0) 549 throw new ConfigurationException (string.Format ("Required attribute '{0}' cannot be empty.", attribute), node); 550 } 551 ValidateInvalidAttributes(XmlAttributeCollection c, XmlNode node)552 private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node) 553 { 554 if (c.Count != 0) 555 ThrowUnrecognizedAttribute (c[0].Name, node); 556 } 557 ThrowMissingAttribute(string attribute, XmlNode node)558 private void ThrowMissingAttribute (string attribute, XmlNode node) 559 { 560 throw new ConfigurationException (string.Format ("Required attribute '{0}' not found.", attribute), node); 561 } 562 ThrowUnrecognizedNode(XmlNode node)563 private void ThrowUnrecognizedNode (XmlNode node) 564 { 565 throw new ConfigurationException ( 566 string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType), 567 node); 568 } 569 ThrowUnrecognizedElement(XmlNode node)570 private void ThrowUnrecognizedElement (XmlNode node) 571 { 572 throw new ConfigurationException ( 573 string.Format ("Unrecognized element '{0}'.", node.Name), 574 node); 575 } 576 ThrowUnrecognizedAttribute(string attribute, XmlNode node)577 private void ThrowUnrecognizedAttribute (string attribute, XmlNode node) 578 { 579 throw new ConfigurationException ( 580 string.Format ("Unrecognized attribute '{0}' on element <{1}/>.", attribute, node.Name), 581 node); 582 } 583 } 584 #endif 585 } 586 587