2 // System.Diagnostics.DiagnosticsConfigurationHandler.cs
4 // Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation
5 // can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics
8 // John R. Hicks <angryjohn69@nc.rr.com>
9 // Jonathan Pryor <jonpryor@vt.edu>
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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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.
35 using System.Collections;
36 using System.Collections.Specialized;
37 using System.Configuration;
38 using System.Reflection;
39 using System.Threading;
43 namespace System.Diagnostics
46 // It handles following elements in <system.diagnostics> :
47 // - <sharedListeners> [2.0]
50 // - <listeners> (collection)
52 // - <add name=string value=string />
53 // - <trace autoflush=bool indentsize=int useGlobalLock=bool>
55 internal sealed class DiagnosticsConfiguration
58 private static object lock_ = new object();
60 private static object settings;
62 public static IDictionary Settings {
65 if (settings == null) {
66 object s = ConfigurationSettings.GetConfig ("system.diagnostics");
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.
73 Thread.MemoryBarrier ();
78 settings = ConfigurationSettings.GetConfig ("system.diagnostics");
81 return (IDictionary) settings;
88 [Obsolete ("This class is obsoleted")]
89 public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler
91 TraceImplSettings configValues;
93 delegate void ElementHandler (IDictionary d, XmlNode node);
95 IDictionary elementHandlers = new Hashtable ();
97 public DiagnosticsConfigurationHandler ()
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);
106 public virtual object Create (object parent, object configContext, XmlNode section)
110 d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
112 d = (IDictionary) ((ICloneable)parent).Clone();
114 if (d.Contains (TraceImplSettings.Key))
115 configValues = (TraceImplSettings) d [TraceImplSettings.Key];
117 d.Add (TraceImplSettings.Key, configValues = new TraceImplSettings ());
119 // process <sharedListeners> first
120 foreach (XmlNode child in section.ChildNodes) {
121 switch (child.NodeType) {
122 case XmlNodeType.Element:
123 if (child.LocalName != "sharedListeners")
125 AddTraceListeners (d, child, GetSharedListeners (d));
130 foreach (XmlNode child in section.ChildNodes) {
131 XmlNodeType type = child.NodeType;
135 case XmlNodeType.Whitespace:
136 case XmlNodeType.Comment:
138 case XmlNodeType.Element:
139 if (child.LocalName == "sharedListeners")
141 ElementHandler eh = (ElementHandler) elementHandlers [child.Name];
145 ThrowUnrecognizedElement (child);
148 ThrowUnrecognizedElement (child);
156 // Remarks: Both attribute are optional
157 private void AddAssertNode (IDictionary d, XmlNode node)
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) {
165 d ["assertuienabled"] = bool.Parse (assertuienabled);
167 catch (Exception e) {
168 throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'",
173 if (logfilename != null)
174 d ["logfilename"] = logfilename;
176 DefaultTraceListener dtl = (DefaultTraceListener) configValues.Listeners["Default"];
178 if (assertuienabled != null)
179 dtl.AssertUiEnabled = (bool) d ["assertuienabled"];
180 if (logfilename != null)
181 dtl.LogFileName = logfilename;
184 if (node.ChildNodes.Count > 0)
185 ThrowUnrecognizedElement (node.ChildNodes[0]);
188 private void AddPerformanceCountersNode (IDictionary d, XmlNode node)
190 XmlAttributeCollection c = node.Attributes;
191 string filemappingsize = GetAttribute (c, "filemappingsize", false, node);
192 ValidateInvalidAttributes (c, node);
193 if (filemappingsize != null) {
195 d ["filemappingsize"] = int.Parse (filemappingsize);
197 catch (Exception e) {
198 throw new ConfigurationException ("The `filemappingsize' attribute must be an integral value.",
203 if (node.ChildNodes.Count > 0)
204 ThrowUnrecognizedElement (node.ChildNodes[0]);
207 // name and value attributes are required
208 // Docs do not define "remove" or "clear" elements, but .NET recognizes
210 private void AddSwitchesNode (IDictionary d, XmlNode node)
212 // There are no attributes on <switch/>
213 ValidateInvalidAttributes (node.Attributes, node);
215 IDictionary newNodes = new Hashtable ();
217 foreach (XmlNode child in node.ChildNodes) {
218 XmlNodeType t = child.NodeType;
219 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
221 if (t == XmlNodeType.Element) {
222 XmlAttributeCollection attributes = child.Attributes;
225 switch (child.Name) {
227 name = GetAttribute (attributes, "name", true, child);
228 value = GetAttribute (attributes, "value", true, child);
229 newNodes [name] = GetSwitchValue (name, value);
232 name = GetAttribute (attributes, "name", true, child);
233 newNodes.Remove (name);
239 ThrowUnrecognizedElement (child);
242 ValidateInvalidAttributes (attributes, child);
245 ThrowUnrecognizedNode (child);
248 d [node.Name] = newNodes;
251 private static object GetSwitchValue (string name, string value)
256 private void AddTraceNode (IDictionary d, XmlNode node)
258 AddTraceAttributes (d, node);
260 foreach (XmlNode child in node.ChildNodes) {
261 XmlNodeType t = child.NodeType;
262 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
264 if (t == XmlNodeType.Element) {
265 if (child.Name == "listeners")
266 AddTraceListeners (d, child, configValues.Listeners);
268 ThrowUnrecognizedElement (child);
269 ValidateInvalidAttributes (child.Attributes, child);
272 ThrowUnrecognizedNode (child);
276 // all attributes are optional
277 private void AddTraceAttributes (IDictionary d, XmlNode node)
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;
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'",
292 configValues.AutoFlush = autoflush;
294 if (indentsizeConf != null) {
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.",
303 configValues.IndentSize = indentsize;
307 private TraceListenerCollection GetSharedListeners (IDictionary d)
309 TraceListenerCollection shared_listeners = d ["sharedListeners"] as TraceListenerCollection;
310 if (shared_listeners == null) {
311 shared_listeners = new TraceListenerCollection ();
312 d ["sharedListeners"] = shared_listeners;
314 return shared_listeners;
317 private void AddSourcesNode (IDictionary d, XmlNode node)
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;
327 foreach (XmlNode child in node.ChildNodes) {
328 XmlNodeType t = child.NodeType;
329 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
331 if (t == XmlNodeType.Element) {
332 if (child.Name == "source")
333 AddTraceSource (d, sources, child);
335 ThrowUnrecognizedElement (child);
336 // ValidateInvalidAttributes (child.Attributes, child);
339 ThrowUnrecognizedNode (child);
343 private void AddTraceSource (IDictionary d, Hashtable sources, XmlNode node)
346 SourceLevels levels = SourceLevels.Error;
347 StringDictionary atts = new StringDictionary ();
348 foreach (XmlAttribute a in node.Attributes) {
354 levels = (SourceLevels) Enum.Parse (typeof (SourceLevels), a.Value);
357 atts [a.Name] = a.Value;
362 throw new ConfigurationException ("Mandatory attribute 'name' is missing in 'source' element.");
364 // ignore duplicate ones (no error occurs)
365 if (sources.ContainsKey (name))
368 TraceSourceInfo sinfo = new TraceSourceInfo (name, levels, configValues);
369 sources.Add (sinfo.Name, sinfo);
371 foreach (XmlNode child in node.ChildNodes) {
372 XmlNodeType t = child.NodeType;
373 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
375 if (t == XmlNodeType.Element) {
376 if (child.Name == "listeners")
377 AddTraceListeners (d, child, sinfo.Listeners);
379 ThrowUnrecognizedElement (child);
380 ValidateInvalidAttributes (child.Attributes, child);
383 ThrowUnrecognizedNode (child);
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.
389 private void AddTraceListeners (IDictionary d, XmlNode listenersNode, TraceListenerCollection listeners)
391 // There are no attributes on <listeners/>
392 ValidateInvalidAttributes (listenersNode.Attributes, listenersNode);
394 foreach (XmlNode child in listenersNode.ChildNodes) {
395 XmlNodeType t = child.NodeType;
396 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
398 if (t == XmlNodeType.Element) {
399 XmlAttributeCollection attributes = child.Attributes;
401 switch (child.Name) {
403 AddTraceListener (d, child, attributes, listeners);
406 name = GetAttribute (attributes, "name", true, child);
407 RemoveTraceListener (name);
410 configValues.Listeners.Clear ();
413 ThrowUnrecognizedElement (child);
416 ValidateInvalidAttributes (attributes, child);
419 ThrowUnrecognizedNode (child);
423 private void AddTraceListener (IDictionary d, XmlNode child, XmlAttributeCollection attributes, TraceListenerCollection listeners)
425 string name = GetAttribute (attributes, "name", true, child);
428 #if CONFIGURATION_DEP
429 type = GetAttribute (attributes, "type", false, child);
431 // indicated by name.
432 TraceListener shared = GetSharedListeners (d) [name];
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);
445 type = GetAttribute (attributes, "type", true, child);
448 Type t = Type.GetType (type);
450 throw new ConfigurationException (string.Format ("Invalid Type Specified: {0}", type));
455 string initializeData = GetAttribute (attributes, "initializeData", false, child);
456 if (initializeData != null) {
457 args = new object[] { initializeData };
458 types = new Type[] { typeof(string) };
461 types = Type.EmptyTypes;
464 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
465 if (t.Assembly == GetType ().Assembly)
466 flags |= BindingFlags.NonPublic;
468 ConstructorInfo ctor = t.GetConstructor (flags, null, types, null);
470 throw new ConfigurationException ("Couldn't find constructor for class " + type);
472 TraceListener l = (TraceListener) ctor.Invoke (args);
475 #if CONFIGURATION_DEP
476 string trace = GetAttribute (attributes, "traceOutputOptions", false, child);
478 if (trace != trace.Trim ())
479 throw new ConfigurationErrorsException (string.Format (
480 "Invalid value '{0}' for 'traceOutputOptions'.",
483 TraceOptions trace_options;
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'.",
494 l.TraceOutputOptions = trace_options;
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);
503 l.Attributes.Add (key, value);
508 l.IndentSize = configValues.IndentSize;
512 private void RemoveTraceListener (string name)
515 configValues.Listeners.Remove (name);
517 catch (ArgumentException) {
518 // The specified listener wasn't in the collection
519 // Ignore this; .NET does.
521 catch (Exception e) {
522 throw new ConfigurationException (
523 string.Format ("Unknown error removing listener: {0}", name),
528 private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node)
530 XmlAttribute a = attrs[attr];
537 ValidateAttribute (attr, r, node);
541 ThrowMissingAttribute (attr, node);
546 private void ValidateAttribute (string attribute, string value, XmlNode node)
548 if (value == null || value.Length == 0)
549 throw new ConfigurationException (string.Format ("Required attribute '{0}' cannot be empty.", attribute), node);
552 private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node)
555 ThrowUnrecognizedAttribute (c[0].Name, node);
558 private void ThrowMissingAttribute (string attribute, XmlNode node)
560 throw new ConfigurationException (string.Format ("Required attribute '{0}' not found.", attribute), node);
563 private void ThrowUnrecognizedNode (XmlNode node)
565 throw new ConfigurationException (
566 string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType),
570 private void ThrowUnrecognizedElement (XmlNode node)
572 throw new ConfigurationException (
573 string.Format ("Unrecognized element '{0}'.", node.Name),
577 private void ThrowUnrecognizedAttribute (string attribute, XmlNode node)
579 throw new ConfigurationException (
580 string.Format ("Unrecognized attribute '{0}' on element <{1}/>.", attribute, node.Name),