// // Copyright (c) 2009 by Skiviez, Inc. All rights reserved. // namespace Skiviez.Hedgehog.Model { using System; using System.Configuration; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Security.Permissions; using System.Text; /// /// Our implementation of . The reason we /// can't use Microsoft's is because they hard-coded how the initializeData /// filename is loaded for that class in TraceUtils. They do some magic such that /// it can be relative for ASP.NET Web sites, relative to the configuration file /// (which is what you want) and not the current working directory of the ASP.NET /// process (which is %SYSTEMDIR%, typically). So we do the mangling here ourselves. /// [HostProtection(SecurityAction.LinkDemand, Synchronization = true)] public class TextWriterTraceListener : TraceListener { /// /// The file name that we are writing to. /// private string fileName; /// /// Our text writer. /// private TextWriter writer; /// /// Initializes a new instance of the TextWriterTraceListener class /// with TextWriter as the output recipient. /// public TextWriterTraceListener() { } /// /// Initializes a new instance of the TextWriterTraceListener class, /// using the stream as the recipient of the debugging and tracing output. /// /// Stream that represents the stream the /// TextWriterTraceListener writes to. public TextWriterTraceListener(Stream stream) : this(stream, string.Empty) { } /// /// Initializes a new instance of the TextWriterTraceListener class /// using the specified writer as recipient of the tracing or debugging /// output. /// /// A TextWriter that receives the output from /// the TextWriterTraceListener. public TextWriterTraceListener(TextWriter writer) : this(writer, string.Empty) { } /// /// Initializes a new instance of the TextWriterTraceListener class, /// using the file as the recipient of the debugging and tracing output. /// /// The name of the file the /// TextWriterTraceListener writes to. public TextWriterTraceListener(string fileName) { this.fileName = MungeFileName(fileName); } /// /// Initializes a new instance of the TextWriterTraceListener class with /// the specified name, using the stream as the recipient of the /// debugging and tracing output. /// /// Stream that represents the stream the /// TextWriterTraceListener writes to. /// The name of the new instance. public TextWriterTraceListener(Stream stream, string name) : base(name) { if (stream == null) { throw new ArgumentNullException("stream"); } this.writer = new StreamWriter(stream); } /// /// Initializes a new instance of the TextWriterTraceListener class /// with the specified name, using the specified writer as recipient of /// the tracing or debugging output. /// /// A TextWriter that receives the output from the /// TextWriterTraceListener. /// The name of the new instance. public TextWriterTraceListener(TextWriter writer, string name) : base(name) { if (writer == null) { throw new ArgumentNullException("writer"); } this.writer = writer; } /// /// Initializes a new instance of the TextWriterTraceListener class /// with the specified name, using the file as the recipient of the /// debugging and tracing output. /// /// the fileName to output to /// the name of the trace listener public TextWriterTraceListener(string fileName, string name) : base(name) { this.fileName = MungeFileName(fileName); } /// /// Gets or sets the text writer that receives the tracing or /// debugging output. /// public TextWriter Writer { get { this.EnsureWriter(); return this.writer; } set { this.writer = value; } } /// /// Closes the Writer so that it no longer receives tracing or /// debugging output. /// public override void Close() { if (this.writer != null) { this.writer.Close(); } this.writer = null; } /// /// Flushes the output buffer for the Writer. /// public override void Flush() { if (this.EnsureWriter()) { this.writer.Flush(); } } /// /// Writes a message to this instance's Writer. /// /// The message to write. public override void Write(string message) { if (this.EnsureWriter()) { if (this.NeedIndent) { this.WriteIndent(); } this.writer.Write(message); } } /// /// Writes a message to this instance's Writer followed by a line terminator. /// /// The message to write. public override void WriteLine(string message) { if (this.EnsureWriter()) { if (this.NeedIndent) { this.WriteIndent(); } this.writer.WriteLine(message); this.NeedIndent = true; } } /// /// Disposes this TextWriterTraceListener object. /// /// whether or not to release managed resources protected override void Dispose(bool disposing) { if (disposing) { if (this.writer != null) { this.writer.Dispose(); } this.Close(); } } /// /// Gets an encoding with the given fallback encoding. /// /// the fallback encoding /// the cloned encoding. private static Encoding GetEncodingWithFallback(Encoding encoding) { Encoding encoding2 = (Encoding)encoding.Clone(); encoding2.EncoderFallback = EncoderFallback.ReplacementFallback; encoding2.DecoderFallback = DecoderFallback.ReplacementFallback; return encoding2; } /// /// Munges the file name such that if it is a relative path, we go /// relative from the configuration file and not from the current working /// directory. This makes things work as expected on ASP.NET sites and /// makes other applications similarly work with non-astonishing /// behavior. /// /// the file name to munge /// the munged filename private static string MungeFileName(string fileName) { string configPath; string mungedFileName; mungedFileName = fileName; if (fileName[0] != Path.DirectorySeparatorChar && fileName[0] != Path.AltDirectorySeparatorChar && !Path.IsPathRooted(fileName)) { ConfigurationSection configSection; configSection = (ConfigurationSection)ConfigurationManager.GetSection("system.diagnostics"); if (configSection != null) { configPath = configSection.ElementInformation.Source; if (!string.IsNullOrEmpty(configPath)) { string directoryName; directoryName = Path.GetDirectoryName(configPath); if (directoryName != null) { mungedFileName = Path.Combine(directoryName, fileName); } } } } return mungedFileName; } /// /// Ensures that a text writer exists. /// /// whether or not a text writer was created [SuppressMessage( "Microsoft.Design", "CA1031", Justification = "Generally, we don't trace config errors to bring down the app.")] private bool EnsureWriter() { bool flag = true; if (this.writer == null) { flag = false; if (this.fileName == null) { return flag; } Encoding encodingWithFallback = GetEncodingWithFallback(new UTF8Encoding(false)); string fullPath = Path.GetFullPath(this.fileName); string directoryName = Path.GetDirectoryName(fullPath); string fileName = Path.GetFileName(fullPath); for (int i = 0; i < 2; i++) { try { this.writer = new StreamWriter(fullPath, true, encodingWithFallback, 0x1000); flag = true; break; } catch (IOException) { fileName = Guid.NewGuid().ToString() + fileName; fullPath = Path.Combine(directoryName, fileName); } catch (UnauthorizedAccessException) { break; } catch (Exception) { break; } } if (!flag) { this.fileName = null; } } return flag; } } }