The Labs \ Source Viewer \ SSCLI \ System.Diagnostics \ TextWriterTraceListener

  1. //------------------------------------------------------------------------------
  2. // <copyright file="TextWriterTraceListener.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. /*
  16. */
  17. namespace System.Diagnostics
  18. {
  19.     using System;
  20.     using System.IO;
  21.     using System.Text;
  22.     using System.Security.Permissions;
  23.     using System.Runtime.InteropServices;
  24.     using Microsoft.Win32;
  25.     using System.Runtime.Versioning;
  26.    
  27.     /// <devdoc>
  28.     /// <para>Directs tracing or debugging output to
  29.     /// a <see cref='T:System.IO.TextWriter'/> or to a <see cref='T:System.IO.Stream'/>,
  30.     /// such as <see cref='F:System.Console.Out'/> or <see cref='T:System.IO.FileStream'/>.</para>
  31.     /// </devdoc>
  32.     [HostProtection(Synchronization = true)]
  33.     public class TextWriterTraceListener : TraceListener
  34.     {
  35.         internal TextWriter writer;
  36.         string fileName = null;
  37.        
  38.         /// <devdoc>
  39.         /// <para>Initializes a new instance of the <see cref='System.Diagnostics.TextWriterTraceListener'/> class with
  40.         /// <see cref='System.IO.TextWriter'/>
  41.         /// as the output recipient.</para>
  42.         /// </devdoc>
  43.         public TextWriterTraceListener()
  44.         {
  45.         }
  46.        
  47.         /// <devdoc>
  48.         /// <para>Initializes a new instance of the <see cref='System.Diagnostics.TextWriterTraceListener'/> class, using the
  49.         /// stream as the recipient of the debugging and tracing output.</para>
  50.         /// </devdoc>
  51.         public TextWriterTraceListener(Stream stream) : this(stream, string.Empty)
  52.         {
  53.         }
  54.        
  55.         /// <devdoc>
  56.         /// <para>Initializes a new instance of the <see cref='System.Diagnostics.TextWriterTraceListener'/> class with the
  57.         /// specified name and using the stream as the recipient of the debugging and tracing output.</para>
  58.         /// </devdoc>
  59.         public TextWriterTraceListener(Stream stream, string name) : base(name)
  60.         {
  61.             if (stream == null)
  62.                 throw new ArgumentNullException("stream");
  63.             this.writer = new StreamWriter(stream);
  64.         }
  65.        
  66.         /// <devdoc>
  67.         /// <para>Initializes a new instance of the <see cref='System.Diagnostics.TextWriterTraceListener'/> class using the
  68.         /// specified writer as recipient of the tracing or debugging output.</para>
  69.         /// </devdoc>
  70.         public TextWriterTraceListener(TextWriter writer) : this(writer, string.Empty)
  71.         {
  72.         }
  73.        
  74.         /// <devdoc>
  75.         /// <para>Initializes a new instance of the <see cref='System.Diagnostics.TextWriterTraceListener'/> class with the
  76.         /// specified name and using the specified writer as recipient of the tracing or
  77.         /// debugging
  78.         /// output.</para>
  79.         /// </devdoc>
  80.         public TextWriterTraceListener(TextWriter writer, string name) : base(name)
  81.         {
  82.             if (writer == null)
  83.                 throw new ArgumentNullException("writer");
  84.             this.writer = writer;
  85.         }
  86.        
  87.         /// <devdoc>
  88.         /// <para>[To be supplied.]</para>
  89.         /// </devdoc>
  90.         [ResourceExposure(ResourceScope.Machine)]
  91.         public TextWriterTraceListener(string fileName)
  92.         {
  93.             this.fileName = fileName;
  94.         }
  95.        
  96.         /// <devdoc>
  97.         /// <para>[To be supplied.]</para>
  98.         /// </devdoc>
  99.         [ResourceExposure(ResourceScope.Machine)]
  100.         public TextWriterTraceListener(string fileName, string name) : base(name)
  101.         {
  102.             this.fileName = fileName;
  103.         }
  104.        
  105.         /// <devdoc>
  106.         /// <para> Indicates the text writer that receives the tracing
  107.         /// or debugging output.</para>
  108.         /// </devdoc>
  109.         public TextWriter Writer {
  110.             get {
  111.                 EnsureWriter();
  112.                 return writer;
  113.             }
  114.            
  115.             set { writer = value; }
  116.         }
  117.        
  118.         /// <devdoc>
  119.         /// <para>Closes the <see cref='System.Diagnostics.TextWriterTraceListener.Writer'/> so that it no longer
  120.         /// receives tracing or debugging output.</para>
  121.         /// </devdoc>
  122.         public override void Close()
  123.         {
  124.             if (writer != null)
  125.                 writer.Close();
  126.            
  127.             writer = null;
  128.         }
  129.        
  130.         /// <internalonly/>
  131.         /// <devdoc>
  132.         /// </devdoc>
  133.         protected override void Dispose(bool disposing)
  134.         {
  135.             if (disposing)
  136.                 this.Close();
  137.         }
  138.        
  139.         /// <devdoc>
  140.         /// <para>Flushes the output buffer for the <see cref='System.Diagnostics.TextWriterTraceListener.Writer'/>.</para>
  141.         /// </devdoc>
  142.         public override void Flush()
  143.         {
  144.             if (!EnsureWriter())
  145.                 return;
  146.             writer.Flush();
  147.         }
  148.        
  149.         /// <devdoc>
  150.         /// <para>Writes a message
  151.         /// to this instance's <see cref='System.Diagnostics.TextWriterTraceListener.Writer'/>.</para>
  152.         /// </devdoc>
  153.         public override void Write(string message)
  154.         {
  155.             if (!EnsureWriter())
  156.                 return;
  157.             if (NeedIndent)
  158.                 WriteIndent();
  159.             writer.Write(message);
  160.         }
  161.        
  162.         /// <devdoc>
  163.         /// <para>Writes a message
  164.         /// to this instance's <see cref='System.Diagnostics.TextWriterTraceListener.Writer'/> followed by a line terminator. The
  165.         /// default line terminator is a carriage return followed by a line feed (\r\n).</para>
  166.         /// </devdoc>
  167.         public override void WriteLine(string message)
  168.         {
  169.             if (!EnsureWriter())
  170.                 return;
  171.             if (NeedIndent)
  172.                 WriteIndent();
  173.             writer.WriteLine(message);
  174.             NeedIndent = true;
  175.         }
  176.        
  177.         private static Encoding GetEncodingWithFallback(Encoding encoding)
  178.         {
  179.             // Clone it and set the "?" replacement fallback
  180.             Encoding fallbackEncoding = (Encoding)encoding.Clone();
  181.             fallbackEncoding.EncoderFallback = EncoderFallback.ReplacementFallback;
  182.             fallbackEncoding.DecoderFallback = DecoderFallback.ReplacementFallback;
  183.            
  184.             return fallbackEncoding;
  185.         }
  186.        
  187.         // This uses a machine resource, scoped by the fileName variable.
  188.         [ResourceExposure(ResourceScope.None)]
  189.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  190.         internal bool EnsureWriter()
  191.         {
  192.             bool ret = true;
  193.            
  194.             if (writer == null) {
  195.                 ret = false;
  196.                
  197.                 if (fileName == null)
  198.                     return ret;
  199.                
  200.                 // StreamWriter by default uses UTF8Encoding which will throw on invalid encoding errors.
  201.                 // This can cause the internal StreamWriter's state to be irrecoverable. It is bad for tracing
  202.                 // APIs to throw on encoding errors. Instead, we should provide a "?" replacement fallback
  203.                 // encoding to substitute illegal chars. For ex, In case of high surrogate character
  204.                 // D800-DBFF without a following low surrogate character DC00-DFFF
  205.                 // NOTE: We also need to use an encoding that does't emit BOM whic is StreamWriter's default
  206.                 Encoding noBOMwithFallback = GetEncodingWithFallback(new UTF8Encoding(false));
  207.                
  208.                
  209.                 // To support multiple appdomains/instances tracing to the same file,
  210.                 // we will try to open the given file for append but if we encounter
  211.                 // IO errors, we will prefix the file name with a unique GUID value
  212.                 // and try one more time
  213.                 string fullPath = Path.GetFullPath(fileName);
  214.                 string dirPath = Path.GetDirectoryName(fullPath);
  215.                 string fileNameOnly = Path.GetFileName(fullPath);
  216.                
  217.                 for (int i = 0; i < 2; i++) {
  218.                     try {
  219.                         writer = new StreamWriter(fullPath, true, noBOMwithFallback, 4096);
  220.                         ret = true;
  221.                         break;
  222.                     }
  223.                     catch (IOException) {
  224.                        
  225.                         // Should we do this only for ERROR_SHARING_VIOLATION?
  226.                         //if (InternalResources.MakeErrorCodeFromHR(Marshal.GetHRForException(ioexc)) == InternalResources.ERROR_SHARING_VIOLATION) {
  227.                        
  228.                         fileNameOnly = Guid.NewGuid().ToString() + fileNameOnly;
  229.                         fullPath = Path.Combine(dirPath, fileNameOnly);
  230.                         continue;
  231.                     }
  232.                     catch (UnauthorizedAccessException) {
  233.                         //ERROR_ACCESS_DENIED, mostly ACL issues
  234.                         break;
  235.                     }
  236.                     catch (Exception) {
  237.                         break;
  238.                     }
  239.                 }
  240.                
  241.                 if (!ret) {
  242.                     // Disable tracing to this listener. Every Write will be nop.
  243.                     // We need to think of a central way to deal with the listener
  244.                     // init errors in the future. The default should be that we eat
  245.                     // up any errors from listener and optionally notify the user
  246.                     fileName = null;
  247.                 }
  248.             }
  249.             return ret;
  250.         }
  251.        
  252.     }
  253. }

Developer Fusion