BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / System / System.Diagnostics / XmlWriterTraceListener.cs
1 //
2 // XmlWriterTraceListener.cs
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 // Copyright (C) 2007 Novell, Inc  http://www.novell.com
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 #if NET_2_0 && XML_DEP
32
33 using System;
34 using System.IO;
35 using System.Diagnostics;
36 using System.Threading;
37 using System.Xml;
38 using System.Xml.XPath;
39
40 namespace System.Diagnostics
41 {
42         public class XmlWriterTraceListener : TextWriterTraceListener
43         {
44                 static readonly string e2e_ns = "http://schemas.microsoft.com/2004/06/E2ETraceEvent";
45                 static readonly string sys_ns = "http://schemas.microsoft.com/2004/06/windows/eventlog/system";
46                 static readonly string default_name = "XmlWriter";
47                 XmlWriter w;
48
49                 public XmlWriterTraceListener (string filename)
50                         : this (filename, default_name)
51                 {
52                 }
53
54                 public XmlWriterTraceListener (string filename, string name)
55                         : this (new StreamWriter (new FileStream (filename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)), name)
56                 {
57                 }
58
59                 public XmlWriterTraceListener (Stream stream)
60                         : this (stream, default_name)
61                 {
62                 }
63
64                 public XmlWriterTraceListener (Stream writer, string name)
65                         : this (new StreamWriter (writer), name)
66                 {
67                 }
68
69                 public XmlWriterTraceListener (TextWriter writer)
70                         : this (writer, default_name)
71                 {
72                 }
73
74                 public XmlWriterTraceListener (TextWriter writer, string name)
75                         : base (name)
76                 {
77                         XmlWriterSettings settings = new XmlWriterSettings ();
78                         settings.OmitXmlDeclaration = true;
79                         w = XmlWriter.Create (writer, settings);
80                 }
81
82                 public override void Close ()
83                 {
84                         w.Close ();
85                 }
86
87                 public override void Fail (string message, string detailMessage)
88                 {
89                         TraceEvent (null, null, TraceEventType.Error,
90                                     0, String.Concat (message, " ", detailMessage));
91                 }
92
93                 public override void TraceData (TraceEventCache eventCache,
94                         string source, TraceEventType eventType, int id,
95                         object data)
96                 {
97                         TraceCore (eventCache, source, eventType, id, false,
98                                    Guid.Empty, 2, true, data);
99                 }
100
101                 [MonoLimitation ("level is not always correct")]
102                 public override void TraceData (TraceEventCache eventCache,
103                         string source, TraceEventType eventType, int id,
104                         params object [] data)
105                 {
106                         TraceCore (eventCache, source, eventType, id, false,
107                                    Guid.Empty, 2, true, data);
108                 }
109
110                 [MonoLimitation ("level is not always correct")]
111                 public override void TraceEvent (TraceEventCache eventCache,
112                         string source, TraceEventType eventType, int id,
113                         string message)
114                 {
115                         TraceCore (eventCache, source, eventType,
116                                    id, false, Guid.Empty, 2, true, message);
117                 }
118
119                 [MonoLimitation ("level is not always correct")]
120                 public override void TraceEvent (TraceEventCache eventCache,
121                         string source, TraceEventType eventType, int id,
122                         string format, params object [] args)
123                 {
124                         TraceCore (eventCache, source, eventType,
125                                    id, false, Guid.Empty, 2, true, String.Format (format, args));
126                 }
127
128                 public override void TraceTransfer (TraceEventCache eventCache,
129                         string source, int id, string message,
130                         Guid relatedActivityId)
131                 {
132                         TraceCore (eventCache, source, TraceEventType.Transfer,
133                                    id, true, relatedActivityId, 255, true, message);
134                 }
135
136                 public override void Write (string message)
137                 {
138                         WriteLine (message);
139                 }
140
141                 [MonoLimitation ("level is not always correct")]
142                 public override void WriteLine (string message)
143                 {
144                         // FIXME: what is the correct level?
145                         TraceCore (null, "Trace", TraceEventType.Information,
146                                    0, false, Guid.Empty, 8, false, message);
147                 }
148
149                 void TraceCore (TraceEventCache eventCache,
150                         string source, TraceEventType eventType, int id,
151                         bool hasRelatedActivity, Guid relatedActivity,
152                         int level, bool wrapData, params object [] data)
153                 {
154                         Process p = eventCache != null ?
155                                 Process.GetProcessById (eventCache.ProcessId) :
156                                 Process.GetCurrentProcess ();
157
158                         w.WriteStartElement ("E2ETraceEvent", e2e_ns);
159
160                         // <System>
161                         w.WriteStartElement ("System", sys_ns);
162                         w.WriteStartElement ("EventID", sys_ns);
163                         w.WriteString (XmlConvert.ToString (id));
164                         w.WriteEndElement ();
165                         // FIXME: find out what should be written
166                         w.WriteStartElement ("Type", sys_ns);
167                         w.WriteString ("3");
168                         w.WriteEndElement ();
169                         w.WriteStartElement ("SubType", sys_ns);
170                         // FIXME: it does not seem always to match eventType value ...
171                         w.WriteAttributeString ("Name", eventType.ToString ());
172                         // FIXME: find out what should be written
173                         w.WriteString ("0");
174                         w.WriteEndElement ();
175                         // FIXME: find out what should be written
176                         w.WriteStartElement ("Level", sys_ns);
177                         w.WriteString (level.ToString ());
178                         w.WriteEndElement ();
179                         w.WriteStartElement ("TimeCreated", sys_ns);
180                         w.WriteAttributeString ("SystemTime", XmlConvert.ToString (eventCache != null ? eventCache.DateTime : DateTime.Now));
181                         w.WriteEndElement ();
182                         w.WriteStartElement ("Source", sys_ns);
183                         w.WriteAttributeString ("Name", source);
184                         w.WriteEndElement ();
185                         w.WriteStartElement ("Correlation", sys_ns);
186                         w.WriteAttributeString ("ActivityID", String.Concat ("{", Guid.Empty, "}"));
187                         w.WriteEndElement ();
188                         w.WriteStartElement ("Execution", sys_ns);
189                         // FIXME: which should I use here?
190                         //w.WriteAttributeString ("ProcessName", p.ProcessName);
191                         w.WriteAttributeString ("ProcessName", p.MainModule.ModuleName);
192                         w.WriteAttributeString ("ProcessID", p.Id.ToString ());
193                         w.WriteAttributeString ("ThreadID", eventCache != null ? eventCache.ThreadId : Thread.CurrentThread.ManagedThreadId.ToString ());
194                         w.WriteEndElement ();
195                         w.WriteStartElement ("Channel", sys_ns);
196                         // FIXME: find out what should be written.
197                         w.WriteEndElement ();
198                         w.WriteStartElement ("Computer");
199                         w.WriteString (p.MachineName);
200                         w.WriteEndElement ();
201
202                         w.WriteEndElement ();
203
204                         // <ApplicationData>
205                         w.WriteStartElement ("ApplicationData", e2e_ns);
206                         w.WriteStartElement ("TraceData", e2e_ns);
207                         foreach (object o in data) {
208                                 if (wrapData)
209                                         w.WriteStartElement ("DataItem", e2e_ns);
210                                 if (o is XPathNavigator)
211                                         // the output ignores xmlns difference between the parent (E2ETraceEvent and the content node).
212                                         // To clone such behavior, I took this approach.
213                                         w.WriteRaw (XPathNavigatorToString ((XPathNavigator) o));
214                                 else if (o != null)
215                                         w.WriteString (o.ToString ());
216                                 if (wrapData)
217                                         w.WriteEndElement ();
218                         }
219                         w.WriteEndElement ();
220                         w.WriteEndElement ();
221
222                         w.WriteEndElement ();
223
224                         w.Flush (); // for XmlWriter
225                         Flush (); // for TextWriter
226                 }
227
228                 static readonly XmlWriterSettings xml_writer_settings = new XmlWriterSettings () { OmitXmlDeclaration = true };
229
230                 // I avoided OuterXml which includes indentation.
231                 string XPathNavigatorToString (XPathNavigator nav)
232                 {
233                         var sw = new StringWriter ();
234                         using (var xw = XmlWriter.Create (sw, xml_writer_settings))
235                                 nav.WriteSubtree (xw);
236                         return sw.ToString ();
237                 }
238         }
239 }
240
241 #endif