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