I have a C# application which in turn loads a C or C++ dll (which in turn loads other C/C++ dlls). Within the C# application I use a log4net logger to capture all the output into a series of log files. My application runs as a windows service so there is no console/output window for normal printfs or output written into stdout/stderr to go to.
Is there a way to setup the C# application to direct stdout/stderr (from the DLLs) and turn each line into a log4net output. Or is there some way within the C/C++ DLL to connect the stdout/stderr streams to the log4net output?
I found some solution (here : http://bytes.com/topic/c-sharp/answers/822341-dllimport-stdout-gets-eaten) that indicated I needed to put a call into my C DLL like this : setvbuf(stdout, NULL, _IONBF, 0);
Though, I don't know what that does, it doesn't do what I want. I assume I'd also need a similar line for stderr. In either case, google seemed to think those lines simply take care of buffering and not redirection into log4net.
I assume I need some sort of function override which snags the console writes (from a loaded DLL in another language) and converts them into mLog.InfoFormat("{0}", consoleString);
sorts of calls. I'm new to c# and not even sure what terms to google in order to find such an override (if its even possible).
Not sure if this complicates the problem, but my C# application is multithreaded and some of the DLLs have multiple threads as well. I assume that just means I need a lock of some sort inside the method that handles the console output and writes it into the log4net framework(maybe) or maybe the normal serialization of log4net will handle it for me.
Turns out those did the trick once I figured out how to use them. I setup two named pipes(or two ends of the same pipe?). One I connected to stdout and had it do a log message in log4net of whatever came through the pipe.
internal static void InfoLogWriter(Object threadContext)
{
mLog.Info("InfoLogWriterthread started");
int id = Process.GetCurrentProcess().Id; // make this instance unique
var serverPipe = new NamedPipeServerStream("consoleRedirect" + id, PipeDirection.In, 1);
NamedPipeClientStream clientPipe = new NamedPipeClientStream(".", "consoleRedirect" + id, PipeDirection.Out, PipeOptions.WriteThrough);
mLog.Info("Connecting Client Pipe.");
clientPipe.Connect();
mLog.Info("Connected Client Pipe, redirecting stdout");
HandleRef hr11 = new HandleRef(clientPipe, clientPipe.SafePipeHandle.DangerousGetHandle());
SetStdHandle(-11, hr11.Handle); // redirect stdout to my pipe
mLog.Info("Redirection of stdout complete.");
mLog.Info("Waiting for console connection");
serverPipe.WaitForConnection(); //blocking
mLog.Info("Console connection made.");
using (var stm = new StreamReader(serverPipe))
{
while (serverPipe.IsConnected)
{
try
{
string txt = stm.ReadLine();
if (!string.IsNullOrEmpty(txt))
mLog.InfoFormat("DLL MESSAGE : {0}", txt);
}
catch (IOException)
{
break; // normal disconnect
}
}
}
mLog.Info("Console connection broken. Thread Stopping.");
}
Also have a function to push all that to another thread so it doesn't block my main thread when it hits the various blocking calls.
internal static void RedirectConsole()
{
mLog.Info("RedirectConsole called.");
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(InfoLogWriter));
// TODO enqueue and item for error messages too.
}
I'm having trouble with it disconnecting and have to reconnect the pipes, but I'll figure out a reconnect solution. I'm guessing that happens when DLLs get swapped back out of memory or perhaps when I need to read but there isn't anything currently ready to be read? I've also got to setup another pair to snag stderr and redirect it as well, using Error logs for that one. Probably want to get rid of the magic number (-11) and use the normal enums as well (STD_ERROR_HANDLE, etc)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With