Reputation: 470
I call an API from my C# code that has a method with the following header
public void ExternalFactory.SetOut(TextWriter outputStream)
I typically call this method like
ExternalFactory.SetOut(Console.Out)
to just have the API write all of its information to the Console. However, I would also like to store this information in a more permanent place such as a text file in addition to being written to the Console.
My first guess is that I need to create some sort of customer TextWriter that splits the stream and sends one to the Console and one to a StreamWriter. What is the correct approach here?
Upvotes: 1
Views: 2143
Reputation: 74365
Rather than standard I/O facilities, I use log4net for just this sort of thing. I configure log4net to simultaneously log to both the console and a log file, using the ConsoleAppender and either a FileAppender or RollingFileAppender.
The nice thing is that you can set the log message template to trap all sorts of useful information (time-of-day, thread/process id, machine name, etc.) in addition to just the logged message.
You can also log to SQL Server, the event log, or to a remoting sink.
Easy!
Here's a sample TextWriter implementation that routes everything through log4net:
using System;
using System.IO;
using System.Text;
using log4net ;
namespace ConsoleApplication22
{
public class Log4NetTextWriter : TextWriter, IDisposable
{
private static ILog log = log4net.LogManager.GetLogger( typeof(Log4NetTextWriter) ) ;
#region properties
private StringBuilder buffer { get ; set ; }
public override Encoding Encoding
{
get
{
// since this TextWrite is writing to log4net, we have no idea what the final encoding might be.
// It all depends on the log4net configuration: tthe appender or appenders that wind up handling the logged message
// determine the final encoding.
//
// Might make more sense to return Encoding.UTF8 though, just to return something.
throw new NotImplementedException() ;
}
}
#endregion properties ;
public override void Flush()
{
if ( this.buffer != null && this.buffer.Length > 0 )
{
this.WriteLine() ;
}
return ;
}
public override void Close()
{
base.Close();
}
protected override void Dispose( bool disposing )
{
this.Flush() ;
base.Dispose( disposing );
}
#region public constructors
public Log4NetTextWriter() : this( null )
{
return ;
}
public Log4NetTextWriter( IFormatProvider formatProvider ) : base( formatProvider )
{
this.buffer = new StringBuilder() ;
}
#endregion public constructors
#region public Write() overloads
public override void Write( bool value )
{
this.buffer.Append( value ) ;
return ;
}
public override void Write( char value )
{
this.buffer.Append( value ) ;
return ;
}
public override void Write( char[] buffer )
{
this.buffer.Append( buffer ) ;
return ;
}
public override void Write( char[] buffer , int index , int count )
{
this.buffer.Append( buffer , index , count ) ;
return ;
}
public override void Write( decimal value )
{
this.buffer.Append( value ) ;
return ;
}
public override void Write( double value )
{
this.buffer.Append( value ) ;
return ;
}
public override void Write( float value )
{
this.buffer.Append( value ) ;
return ;
}
public override void Write( int value )
{
this.buffer.Append( value ) ;
return ;
}
public override void Write( long value )
{
this.buffer.Append( value ) ;
return ;
}
public override void Write(object value)
{
this.buffer.Append( value ) ;
return ;
}
public override void Write( string format , object arg0 )
{
this.buffer.AppendFormat( this.FormatProvider , format , arg0 ) ;
return ;
}
public override void Write( string format , object arg0 , object arg1 )
{
this.buffer.AppendFormat( this.FormatProvider , format , arg0 , arg1 ) ;
return ;
}
public override void Write( string format , object arg0 , object arg1 , object arg2 )
{
this.buffer.AppendFormat( this.FormatProvider , format , arg0 , arg1 , arg2 );
return ;
}
public override void Write( string format , params object[] arg )
{
this.buffer.AppendFormat( this.FormatProvider , format , arg ) ;
return ;
}
public override void Write( string value )
{
this.buffer.Append( value );
return ;
}
public override void Write( uint value )
{
this.buffer.Append( value );
return ;
}
public override void Write( ulong value )
{
this.buffer.Append( value );
return ;
}
public override void WriteLine()
{
string logMessage = this.buffer.ToString() ;
this.buffer.Length = 0 ;
log.Info( logMessage ) ;
return ;
}
#endregion public Write() overloads
#region public WriteLine() overloads
public override void WriteLine( bool value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( char value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( char[] buffer )
{
this.Write( buffer ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( char[] buffer , int index , int count )
{
this.Write( buffer , index , count ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( decimal value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( double value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( float value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( int value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( long value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( object value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( string format , object arg0 )
{
this.Write( format , arg0 ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( string format , object arg0 , object arg1 )
{
this.Write( format , arg0 , arg1 ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( string format , object arg0 , object arg1 , object arg2 )
{
this.Write( format , arg0 , arg1 , arg2 ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( string format , params object[] arg )
{
this.Write( format , arg ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( string value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( uint value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
public override void WriteLine( ulong value )
{
this.Write( value ) ;
this.WriteLine() ;
return ;
}
#endregion public WriteLine() overloads
}
}
Here's a sample log4net config file. This one logs to both the console and to log file, with automatic rollover based on size (it can also roll based on date/time or per-execution).
<log4net>
<!-- Log to the console -->
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<!-- Pattern to output the caller's file name and line number -->
<conversionPattern value="%5level [%thread] (%file:%line) - %message%newline" />
</layout>
</appender>
<!-- Log to a log file. This particular setup should log to a static file name 'log.txt' -->
<!-- When it hits 100KB in size, it rolls, keeping up to 10 archived files. The archived -->
<!-- files are named 'log.text.1', 'log.txt.2', ... , 'log.txt.10' -->
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="100KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="Console" />
<appender-ref ref="RollingFile" />
</root>
</log4net>
To configure log4net, the easiest way is by putting an XmlConfigurator attribute in AssemblyInfo.cs, thus:
// Configure log4net using the .log4net file
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="log4net",Watch=true)]
// This will cause log4net to look for a configuration file
// called TestApp.exe.log4net in the application base
// directory (i.e. the directory containing TestApp.exe)
// The config file will be watched for changes.
While your program is running, you can turn logging on or off, or change its configuration just by editing and saving the config file. log4net watches for config file changes and reconfigures itself on the fly.
Upvotes: 4
Reputation: 3372
Not tested beyond compiling and first-use, but might save someone some typing. Annoying it doesn't exist already.
/// <summary>
/// Spreads data out to multiple text writers.
/// </summary>
class TextWriterMulti : System.IO.TextWriter
{
private System.Collections.Generic.List<System.IO.TextWriter> writers = new System.Collections.Generic.List<System.IO.TextWriter>();
private System.IFormatProvider formatProvider = null;
private System.Text.Encoding encoding = null;
#region TextWriter Properties
public override System.IFormatProvider FormatProvider
{
get
{
System.IFormatProvider formatProvider = this.formatProvider;
if (formatProvider == null)
{
formatProvider = base.FormatProvider;
}
return formatProvider;
}
}
public override string NewLine
{
get { return base.NewLine; }
set
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.NewLine = value;
}
base.NewLine = value;
}
}
public override System.Text.Encoding Encoding
{
get
{
System.Text.Encoding encoding = this.encoding;
if (encoding == null)
{
encoding = System.Text.Encoding.Default;
}
return encoding;
}
}
#region TextWriter Property Setters
TextWriterMulti SetFormatProvider(System.IFormatProvider value)
{
this.formatProvider = value;
return this;
}
TextWriterMulti SetEncoding(System.Text.Encoding value)
{
this.encoding = value;
return this;
}
#endregion // TextWriter Property Setters
#endregion // TextWriter Properties
#region Construction/Destruction
public TextWriterMulti(System.Collections.Generic.IEnumerable<System.IO.TextWriter> writers)
{
this.Clear();
this.AddWriters(writers);
}
#endregion // Construction/Destruction
#region Public interface
public TextWriterMulti Clear()
{
this.writers.Clear();
return this;
}
public TextWriterMulti AddWriter(System.IO.TextWriter writer)
{
this.writers.Add(writer);
return this;
}
public TextWriterMulti AddWriters(System.Collections.Generic.IEnumerable<System.IO.TextWriter> writers)
{
this.writers.AddRange(writers);
return this;
}
#endregion // Public interface
#region TextWriter methods
public override void Close()
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Close();
}
base.Close();
}
protected override void Dispose(bool disposing)
{
foreach (System.IO.TextWriter writer in this.writers)
{
if (disposing)
{
writer.Dispose();
}
}
base.Dispose(disposing);
}
public override void Flush()
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Flush();
}
base.Flush();
}
//foreach (System.IO.TextWriter writer in this.writers)
//{
// writer;
//}
public override void Write(bool value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(char value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(char[] buffer)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(buffer);
}
}
public override void Write(decimal value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(double value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(float value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(int value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(long value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(object value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(string value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(uint value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(ulong value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(value);
}
}
public override void Write(string format, object arg0)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(format, arg0);
}
}
public override void Write(string format, params object[] arg)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(format, arg);
}
}
public override void Write(char[] buffer, int index, int count)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(buffer, index, count);
}
}
public override void Write(string format, object arg0, object arg1)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(format, arg0, arg1);
}
}
public override void Write(string format, object arg0, object arg1, object arg2)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.Write(format, arg0, arg1, arg2);
}
}
public override void WriteLine()
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine();
}
}
public override void WriteLine(bool value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(char value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(char[] buffer)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(buffer);
}
}
public override void WriteLine(decimal value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(double value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(float value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(int value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(long value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(object value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(string value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(uint value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(ulong value)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(value);
}
}
public override void WriteLine(string format, object arg0)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(format, arg0);
}
}
public override void WriteLine(string format, params object[] arg)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(format, arg);
}
}
public override void WriteLine(char[] buffer, int index, int count)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(buffer, index, count);
}
}
public override void WriteLine(string format, object arg0, object arg1)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(format, arg0, arg1);
}
}
public override void WriteLine(string format, object arg0, object arg1, object arg2)
{
foreach (System.IO.TextWriter writer in this.writers)
{
writer.WriteLine(format, arg0, arg1, arg2);
}
}
#endregion // TextWriter methods
}
Upvotes: 7
Reputation: 250
you can just send a copy of data to both stream(textfile) and display(console).
Upvotes: -3
Reputation: 62129
Your first guess is correct. You can generalize it and write a 'distributing' textwriter which accepts a list of textwriters as construction-time parameter and distributes all input that it receives to every single one of the textwriters.
Upvotes: 3