//
// 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;
}
}
}