Merge pull request #2820 from kumpera/license-change-rebased
[mono.git] / mcs / class / System / System.Diagnostics / DefaultTraceListener.cs
1 //
2 // System.Diagnostics.DefaultTraceListener.cs
3 //
4 // Authors:
5 //   Jonathan Pryor (jonpryor@vt.edu)
6 //   Atsushi Enomoto (atsushi@ximian.com)
7 //
8 // Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation 
9 // can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics
10 //
11 // (C) 2002 Jonathan Pryor
12 // (C) 2007 Novell, Inc.
13 //
14
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.IO;
38 using System.Collections;
39 using System.Diagnostics;
40 using System.Reflection;
41 using System.Runtime.CompilerServices;
42 using System.Runtime.InteropServices;
43 using System.Threading;
44
45 namespace System.Diagnostics {
46         public class DefaultTraceListener : TraceListener {
47
48                 private static readonly bool OnWin32;
49
50                 private const string ConsoleOutTrace = "Console.Out";
51                 private const string ConsoleErrorTrace = "Console.Error";
52
53                 private static readonly string MonoTracePrefix;
54                 private static readonly string MonoTraceFile;
55
56                 static DefaultTraceListener ()
57                 {
58                         // Determine what platform we're on.  This impacts how where we send
59                         // messages.  On Win32 platforms (OnWin32 = true), we use the
60                         // `OutputDebugString' api.
61                         //
62                         // On Linux platforms, we use MONO_TRACE_LISTENER to figure things out.  See the
63                         // API documentation for more information on MONO_TRACE_LISTENER.
64                         OnWin32 = (Path.DirectorySeparatorChar == '\\');
65
66                         if (!OnWin32) {
67                                 // If we're running on Unix, we don't have OutputDebugString.
68                                 // Instead, send output to...wherever the MONO_TRACE_LISTENER environment
69                                 // variables says to.
70                                 String trace = Environment.GetEnvironmentVariable("MONO_TRACE_LISTENER");
71
72 #if MOBILE
73                                 if (trace == null)
74                                         trace = ConsoleOutTrace;
75 #endif
76
77                                 if (trace != null) {
78                                         string file = null;
79                                         string prefix = null;
80
81                                         if (trace.StartsWith (ConsoleOutTrace)) {
82                                                 file = ConsoleOutTrace;
83                                                 prefix = GetPrefix (trace, ConsoleOutTrace);
84                                         }
85                                         else if (trace.StartsWith (ConsoleErrorTrace)) {
86                                                 file = ConsoleErrorTrace;
87                                                 prefix = GetPrefix (trace, ConsoleErrorTrace);
88                                         }
89                                         else {
90                                                 file = trace;
91
92                                                 // We can't firgure out what the prefix would be, as ':' is a
93                                                 // valid filename character.  Thus, arbitrary files don't support
94                                                 // prefixes.
95                                                 //
96                                                 // I don't consider this to be a major issue.  Prefixes are useful 
97                                                 // with Console.Out and Console.Error to help separate trace
98                                                 // output from the actual program output.  Writing to an arbitrary
99                                                 // file doesn't introduce issues with disambiguation.
100                                                 prefix = "";
101                                         }
102
103                                         MonoTraceFile = file;
104                                         MonoTracePrefix = prefix;
105                                 }
106                         }
107                 }
108
109                 /**
110                  * Get the prefix for the specified variable.
111                  *
112                  * "Prefixes" are used in the MONO_TRACE_LISTENER variable, and specify text that
113                  * should precede each message printed to the console.  The prefix is
114                  * appended to the console location with a colon (':') separating them.
115                  * For example, if MONO_TRACE_LISTENER is "Console.Out:** my prefix", the prefix is
116                  * "** my prefix".
117                  *
118                  * Everything after the colon, if the colon is present, is used as the
119                  * prefix.
120                  *
121                  * @param       var             The current MONO_TRACE_LISTENER variable
122                  * @param       target  The name of the output location, e.g. "Console.Out"
123                  */
124                 private static string GetPrefix (string var, string target)
125                 {
126                         // actually, we permit any character to separate `target' and the prefix;
127                         // we just skip over target the ':' would be.  This means that a space or
128                         // anything else would suffice, as long as it was only a single
129                         // character.
130                         if (var.Length > target.Length)
131                                 return var.Substring (target.Length + 1);
132                         return "";
133                 }
134
135                 private string logFileName = null;
136
137                 private bool assertUiEnabled = false;
138
139                 public DefaultTraceListener () : base ("Default")
140                 {
141                 }
142
143                 [MonoTODO ("AssertUiEnabled defaults to False; should follow Environment.UserInteractive.")]
144                 public bool AssertUiEnabled {
145                         get { return assertUiEnabled; }
146                         set { assertUiEnabled = value; }
147                 }
148
149                 [MonoTODO]
150                 public string LogFileName {
151                         get {return logFileName;}
152                         set {logFileName = value;}
153                 }
154
155                 public override void Fail (string message)
156                 {
157                         base.Fail (message);
158                 }
159
160                 public override void Fail (string message, string detailMessage)
161                 {
162                         base.Fail (message, detailMessage);
163 #if !MOBILE
164                         if (ProcessUI (message, detailMessage) == DialogResult.Abort)
165                                 Thread.CurrentThread.Abort ();
166 #endif
167                         WriteLine (new StackTrace().ToString());
168                 }
169
170 #if !MOBILE
171                 DialogResult ProcessUI (string message, string detailMessage)
172                 {
173                         
174                         if (!AssertUiEnabled)
175                                 return DialogResult.None;
176
177                         object messageBoxButtonsAbortRetryIgnore;
178                         MethodInfo msgboxShow;
179                         
180                         try {
181                                 Assembly wfAsm = Assembly.Load (Consts.AssemblySystem_Windows_Forms);
182                                 if (wfAsm == null)
183                                     return DialogResult.None;
184                                 
185                                 Type buttons = wfAsm.GetType ("System.Windows.Forms.MessageBoxButtons");
186                                 messageBoxButtonsAbortRetryIgnore = Enum.Parse (buttons, "AbortRetryIgnore");
187                                 msgboxShow = wfAsm.GetType ("System.Windows.Forms.MessageBox").GetMethod (
188                                         "Show",
189                                         new Type [] {typeof (string), typeof (string), buttons});
190                         } catch {
191                                 return DialogResult.None;
192                         }
193
194                         if (msgboxShow == null || messageBoxButtonsAbortRetryIgnore == null)
195                                 return DialogResult.None;
196
197                         string caption = String.Format ("Assertion Failed: {0} to quit, {1} to debug, {2} to continue", "Abort", "Retry", "Ignore");
198                         string msg = String.Format ("{0}{1}{2}{1}{1}{3}", message, Environment.NewLine, detailMessage, new StackTrace ());
199
200                         switch (msgboxShow.Invoke (null, new object [] {msg, caption, messageBoxButtonsAbortRetryIgnore}).ToString ()) {
201                         case "Ignore":
202                                 return DialogResult.Ignore;
203                         case "Abort":
204                                 return DialogResult.Abort;
205                         default:
206                                 return DialogResult.Retry;
207                         }
208                 }
209
210                 enum DialogResult {
211                         None,
212                         Retry,
213                         Ignore,
214                         Abort
215                 }
216 #endif
217
218                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
219                 private extern static void WriteWindowsDebugString (string message);
220
221                 private void WriteDebugString (string message)
222                 {
223                         if (OnWin32)
224                                 WriteWindowsDebugString (message);
225                         else
226                                 WriteMonoTrace (message);
227                 }
228
229                 private void WriteMonoTrace (string message)
230                 {
231                         switch (MonoTraceFile) {
232                         case ConsoleOutTrace:
233                                 Console.Out.Write (message);
234                                 break;
235                         case ConsoleErrorTrace:
236                                 Console.Error.Write (message);
237                                 break;
238                         default:
239                                 WriteLogFile (message, MonoTraceFile);
240                                 break;
241                         }
242                 }
243
244                 private void WritePrefix ()
245                 {
246                         if (!OnWin32) {
247                                 WriteMonoTrace (MonoTracePrefix);
248                         }
249                 }
250
251                 private void WriteImpl (string message)
252                 {
253                         if (NeedIndent) {
254                                 WriteIndent ();
255                                 WritePrefix ();
256                         }
257
258                         if (Debugger.IsLogging())
259                                 Debugger.Log (0, null, message);
260                         else
261                                 WriteDebugString (message);
262
263                         WriteLogFile (message, LogFileName);
264                 }
265
266                 private void WriteLogFile (string message, string logFile)
267                 {
268                         string fname = logFile;
269                         if (fname != null && fname.Length != 0) {
270                                 FileInfo info = new FileInfo (fname);
271                                 StreamWriter sw = null;
272
273                                 // Open the file
274                                 try {
275                                         if (info.Exists)
276                                                 sw = info.AppendText ();
277                                         else
278                                                 sw = info.CreateText ();
279                                 }
280                                 catch {
281                                         // We weren't able to open the file for some reason.
282                                         // We can't write to the log file; so give up.
283                                         return;
284                                 }
285
286                                 using (sw) {
287                                         sw.Write (message);
288                                         sw.Flush ();
289                                 }
290                         }
291                 }
292
293                 public override void Write (string message)
294                 {
295                         WriteImpl (message);
296                 }
297
298                 public override void WriteLine (string message)
299                 {
300                         string msg = message + Environment.NewLine;
301                         WriteImpl (msg);
302
303                         NeedIndent = true;
304                 }
305         }
306 }
307