开发者

How can I capture, log and display the console output of any arbitrary process with my own c# app?

开发者 https://www.devze.com 2023-04-12 12:36 出处:网络
I want to run on a C# program a specific running file and during it display the output o开发者_如何学编程n the screen and also saving it in the file.

I want to run on a C# program a specific running file and during it display the output o开发者_如何学编程n the screen and also saving it in the file.

I don't want to save the output in the file and later display it on screen.

I want them both to happen together.

I know a way to do it by "tee" but I failed each time I tried doing so.

Can anyone give me an example (that works) by using "tee"?


The main question is, do you have control over the source code of the program whose output you want logged and displayed?

If so, then you have a couple options. Probably the easiest would be to "hijack" the Console's output stream with a compound TextWriter:

public class CompoundWriter:TextWriter
{
    public readonly List<TextWriter> Writers = new List<TextWriter>();

    public override void WriteLine(string line)
    {
        if(Writers.Any())
            foreach(var writer in Writers)
                writer.WriteLine(line);
    }

    //override other TextWriter methods as necessary
}

...

//When the program starts, get the default Console output stream
var consoleOut = Console.Out;

//Then replace it with a Compound writer set up with a file writer and the normal Console out.
var compoundWriter = new CompoundWriter();
compoundWriter.Writers.Add(consoleOut);
compoundWriter.Writers.Add(new TextWriter("c:\temp\myLogFile.txt");

Console.SetOut(compoundWriter);

//From now on, any calls to Console's Write methods will go to your CompoundWriter,
//which will send them to the console and the file.

You can also use the Trace listeners to handle any output you want to go to both places:

Trace.Listeners.Clear();
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
Trace.Listeners.Add(new TextWriterTraceListener(File.Open("C:\temp\myLogFile.txt");

//replace any call to Console.WriteLine() with Trace.WriteLine()

if you do NOT have control over the source code of the console app you want to "tee", and the console app does not require any input in the middle of its execution, then you can use a named pipe to get the output of the app and redirect it.

var appLocation = @"C:\temp\myApp.exe";
 var pipeName = "ConsoleNamedPipe";

 using(var namedPipe = new NamedPipeServerStream(pipeName, PipeDirection.In))
 {

   var info = new ProcessStartInfo
                   {
                       FileName = "cmd.exe",
                       Arguments = String.Format(@"/C {1} >>\\.\pipe\{0}",
                                                 pipeName, appLocation),                                                             
                       WindowStyle = ProcessWindowStyle.Hidden
                   };
    ConsoleProcess = Process.Start(info);
    pipe.WaitForConnection();
    using (var reader = new StreamReader(pipe))
    {
        while (!reader.EndOfStream)
        {
            var line = reader.ReadLine();
            Console.WriteLine(line);
            myFileWriter.WriteLine(line);                        
        }
    }
 }

This is a very simple example; you can provide more interaction by using a two-way pipe but it will require quite a bit more code on your end.


You can try the following:

C:\you_csharp_program.exe arg1 arg2 arg3 |tee filename


I'm not going to write out specific code examples but I will tell you that you can look at logging frameworks like log4net which has console and file appenders which will do what you want. You cant wrong log statements in your code Log.Debug("some message") setup the log4net config to use any number of appenders you want and have it write the message to all of the sources at once, so for example screen, file, db, and email you all at the same time.

I seem to have missed the last sentence of the question about making it work with Tee so my answer may not be valid.


Elaborating on KeithS's answer, this is a working implementation. It should work for all Write/WriteLine calls without having to override every overload because the current (4.0, and I assume earlier) implementation of TextWriter directs all writes through Write(char).

public class TeeTextWriter : TextWriter
{
    readonly TextWriter[] _redirectTo;

    public override Encoding Encoding { get { return Encoding.UTF8; } }

    public TeeTextWriter(params TextWriter[] redirectTo)
    {
        _redirectTo = redirectTo ?? new TextWriter[0];
    }

    public override void Write(char value)
    {
        foreach (var textWriter in _redirectTo)
        {
            textWriter.Write(value);
        }
    }
}

Usage:

var realConsoleStream = Console.Out;
using (var fileOut = new StreamWriter(outFileName, false))
{
    Console.SetOut(new TeeTextWriter(fileOut, realConsoleStream));
    try
    {
        Console.WriteLine("Test");
    }
    finally
    {
        Console.SetOut(realConsoleStream);
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消