Merge pull request #168 from tomaszkubacki/master
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / Logger.cs
1 //
2 // Author:
3 //      Atsushi Enomoto <atsushi@ximian.com>
4 //
5 // Copyright (C) 2011 Novell, Inc.  http://www.novell.com
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
26 using System;
27 using System.Collections.Generic;
28 using System.Configuration;
29 using System.Diagnostics;
30 using System.IO;
31 using System.ServiceModel;
32 using System.ServiceModel.Channels;
33 using System.ServiceModel.Configuration;
34 using System.ServiceModel.Diagnostics;
35 using System.Threading;
36 using System.Xml;
37 #if !MOONLIGHT
38 using System.Xml.XPath;
39 #endif
40
41 namespace System.ServiceModel
42 {
43         internal enum MessageLogSourceKind
44         {
45                 TransportSend,
46                 TransportReceive,
47                 ServiceLevelReceiveDatagram,
48                 ServiceLevelSendDatagram,
49                 // more, maybe for clients?
50         }
51
52         internal static class Logger
53         {
54 #if NET_2_1
55                 enum TraceEventType // dummy
56                 {
57                         Critical,
58                         Error,
59                         Warning,
60                         Information,
61                         Verbose,
62                         Start,
63                         Stop,
64                         Suspend,
65                         Resume,
66                         Transfer
67                 }
68 #endif
69
70                 const string xmlns = "http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace";
71                 static MessageLoggingSettings settings = new MessageLoggingSettings ();
72                 static int event_id;
73                 static TextWriter log_writer = TextWriter.Null;
74                 static XmlWriter xml_writer;
75 #if !NET_2_1
76                 static readonly TraceSource source = new TraceSource ("System.ServiceModel");
77                 static readonly TraceSource message_source = new TraceSource ("System.ServiceModel.MessageLogging");
78 #endif
79
80                 static Logger ()
81                 {
82                         var env =
83                                 Environment.GetEnvironmentVariable ("MOON_WCF_TRACE") ??
84                                 Environment.GetEnvironmentVariable ("MONO_WCF_TRACE");
85
86                         switch (env) {
87                         case "stdout":
88                                 log_writer = Console.Out;
89                                 break;
90                         case "stderr":
91                                 log_writer = Console.Error;
92                                 break;
93 #if !NET_2_1
94                         default:
95                                 try {
96                                         if (!String.IsNullOrEmpty (env))
97                                                 log_writer = File.CreateText (env);
98                                 } catch (Exception ex) {
99                                         Console.Error.WriteLine ("WARNING: WCF trace environment variable points to non-creatable file name: " + env);
100                                 }
101                                 break;
102 #endif
103                         }
104                         xml_writer = XmlWriter.Create (log_writer, new XmlWriterSettings () { OmitXmlDeclaration = true });
105
106 #if !NET_2_1
107                         message_source.Switch.Level = SourceLevels.Information;
108 #endif
109                 }
110
111                 #region logger methods
112
113                 public static void Critical (string message, params object [] args)
114                 {
115                         Log (TraceEventType.Critical, message, args);
116                 }
117
118                 public static void Error (string message, params object [] args)
119                 {
120                         Log (TraceEventType.Error, message, args);
121                 }
122                 
123                 public static void Warning (string message, params object [] args)
124                 {
125                         Log (TraceEventType.Warning, message, args);
126                 }
127                 
128                 public static void Info (string message, params object [] args)
129                 {
130                         Log (TraceEventType.Information, message, args);
131                 }
132                 
133                 public static void Verbose (string message, params object [] args)
134                 {
135                         Log (TraceEventType.Verbose, message, args);
136                 }
137                 
138                 // FIXME: do we need more?
139
140                 static void Log (TraceEventType eventType, string message, params object [] args)
141                 {
142                         event_id++;
143 #if NET_2_1
144                         log_writer.Write ("[{0}] ", event_id);
145 #endif
146                         TraceCore (TraceEventType.Information, event_id,
147                                 false, Guid.Empty, // FIXME
148                                 message, args);
149                         log_writer.WriteLine (message, args);
150                         log_writer.Flush ();
151 #if !NET_2_1
152                         source.TraceEvent (eventType, event_id, message, args);
153 #endif
154                 }
155                 
156                 #endregion
157                 
158                 #region message logging
159                 
160                 static readonly XmlWriterSettings xws = new XmlWriterSettings () { OmitXmlDeclaration = true };
161                 
162                 public static void LogMessage (MessageLogSourceKind sourceKind, ref Message msg, long maxMessageSize)
163                 {
164                         if (maxMessageSize > int.MaxValue)
165                                 throw new ArgumentOutOfRangeException ("maxMessageSize");
166                         var mb = msg.CreateBufferedCopy ((int) maxMessageSize);
167                         msg = mb.CreateMessage ();
168                         LogMessage (new MessageLogTraceRecord (sourceKind, msg.GetType (), mb));
169                 }
170                 
171                 public static void LogMessage (MessageLogTraceRecord log)
172                 {
173                         var sw = new StringWriter ();
174 #if NET_2_1
175                         var xw = XmlWriter.Create (sw, xws);
176 #else
177                         var doc = new XmlDocument ();
178                         var xw = doc.CreateNavigator ().AppendChild ();
179 #endif
180                         xw.WriteStartElement ("MessageLogTraceRecord", xmlns);
181                         xw.WriteStartAttribute ("Time");
182                         xw.WriteValue (log.Time);
183                         xw.WriteEndAttribute ();
184                         xw.WriteAttributeString ("Source", log.Source.ToString ());
185                         xw.WriteAttributeString ("Type", log.Type.FullName);
186                         var msg = log.Message.CreateMessage ();
187                         if (!msg.IsEmpty)
188                                 msg.WriteMessage (xw);
189                         xw.WriteEndElement ();
190                         xw.Close ();
191
192                         event_id++;
193
194 #if NET_2_1
195                         log_writer.Write ("[{0}] ", event_id);
196
197                         TraceCore (TraceEventType.Information, event_id, /*FIXME*/false, /*FIXME*/Guid.Empty, sw);
198 #else
199                         TraceCore (TraceEventType.Information, event_id, /*FIXME*/false, /*FIXME*/Guid.Empty, doc.CreateNavigator ());
200
201                         message_source.TraceData (TraceEventType.Information, event_id, doc.CreateNavigator ());
202 #endif
203
204                         log_writer.Flush ();
205                 }
206
207                 #endregion
208
209                 #region XmlWriterTraceListener compatibility
210                 static void TraceCore (//TraceEventCache eventCache,
211                         /*string source,*/ TraceEventType eventType, int id,
212                         bool hasRelatedActivity, Guid relatedActivity,
213                         /*int level, bool wrapData, */params object [] data)
214                 {
215                         string source = "mono(dummy)";
216                         int level = 2;
217                         bool wrapData = true;
218                         var w = xml_writer;
219
220                         w.WriteStartElement ("E2ETraceEvent", e2e_ns);
221
222                         // <System>
223                         w.WriteStartElement ("System", sys_ns);
224                         w.WriteStartElement ("EventID", sys_ns);
225                         w.WriteString (XmlConvert.ToString (id));
226                         w.WriteEndElement ();
227                         w.WriteStartElement ("Type", sys_ns);
228                         // ...what to write here?
229                         w.WriteString ("3");
230                         w.WriteEndElement ();
231                         w.WriteStartElement ("SubType", sys_ns);
232                         // FIXME: it does not seem always to match eventType value ...
233                         w.WriteAttributeString ("Name", eventType.ToString ());
234                         // ...what to write here?
235                         w.WriteString ("0");
236                         w.WriteEndElement ();
237                         // ...what to write here?
238                         w.WriteStartElement ("Level", sys_ns);
239                         w.WriteString (level.ToString ());
240                         w.WriteEndElement ();
241                         w.WriteStartElement ("TimeCreated", sys_ns);
242                         w.WriteAttributeString ("SystemTime", XmlConvert.ToString (DateTime.Now, XmlDateTimeSerializationMode.RoundtripKind));
243                         w.WriteEndElement ();
244                         w.WriteStartElement ("Source", sys_ns);
245                         w.WriteAttributeString ("Name", source);
246                         w.WriteEndElement ();
247                         w.WriteStartElement ("Correlation", sys_ns);
248                         w.WriteAttributeString ("ActivityID", String.Concat ("{", Guid.Empty, "}"));
249                         w.WriteEndElement ();
250                         w.WriteStartElement ("Execution", sys_ns);
251                         w.WriteAttributeString ("ProcessName", "mono (dummy)");
252                         w.WriteAttributeString ("ProcessID", "0");
253                         w.WriteAttributeString ("ThreadID", Thread.CurrentThread.ManagedThreadId.ToString ());
254                         w.WriteEndElement ();
255                         w.WriteStartElement ("Channel", sys_ns);
256                         // ...what to write here?
257                         w.WriteEndElement ();
258                         w.WriteStartElement ("Computer");
259                         w.WriteString ("localhost(dummy)");
260                         w.WriteEndElement ();
261
262                         w.WriteEndElement ();
263
264                         // <ApplicationData>
265                         w.WriteStartElement ("ApplicationData", e2e_ns);
266                         w.WriteStartElement ("TraceData", e2e_ns);
267                         foreach (object o in data) {
268                                 if (wrapData)
269                                         w.WriteStartElement ("DataItem", e2e_ns);
270 #if MOONLIGHT
271                                 // in moonlight we don't have XPathNavigator, so just use raw string...
272                                 if (o != null)
273                                         w.WriteString (o.ToString ());
274 #else
275                                 if (o is XPathNavigator)
276                                         // the output ignores xmlns difference between the parent (E2ETraceEvent and the content node).
277                                         // To clone such behavior, I took this approach.
278                                         w.WriteRaw (XPathNavigatorToString ((XPathNavigator) o));
279                                 else if (o != null)
280                                         w.WriteString (o.ToString ());
281 #endif
282                                 if (wrapData)
283                                         w.WriteEndElement ();
284                         }
285                         w.WriteEndElement ();
286                         w.WriteEndElement ();
287
288                         w.WriteEndElement ();
289
290                         w.Flush (); // for XmlWriter
291                         log_writer.WriteLine ();
292                         log_writer.Flush (); // for TextWriter
293                 }
294
295                 static readonly XmlWriterSettings xml_writer_settings = new XmlWriterSettings () { OmitXmlDeclaration = true };
296
297 #if !MOONLIGHT
298                 // I avoided OuterXml which includes indentation.
299                 static string XPathNavigatorToString (XPathNavigator nav)
300                 {
301                         var sw = new StringWriter ();
302                         using (var xw = XmlWriter.Create (sw, xml_writer_settings))
303                                 nav.WriteSubtree (xw);
304                         return sw.ToString ();
305                 }
306 #endif
307
308                 static readonly string e2e_ns = "http://schemas.microsoft.com/2004/06/E2ETraceEvent";
309                 static readonly string sys_ns = "http://schemas.microsoft.com/2004/06/windows/eventlog/system";
310                 #endregion
311         }
312 }