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