How do I log the Console output of a hosted scriptcs execution? - c#

I am using scriptcs.net to host dynamic scripts, and I would like to capture the output from Console.WriteLine() into my log as information, and Console.Error.WriteLine() as errors. Same as the way Octopus Deploy custom script logging.
The reason I would like to capture the Console.WriteLine() output is to make it easy for script writers to log information using a familiar tool.
Here is my script hosting function:
public static dynamic RunScript()
{
var logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
var scriptServicesBuilder = new ScriptServicesBuilder(new ScriptConsole(), logger).
LogLevel(LogLevel.Info).Cache(false).Repl(false).ScriptEngine<RoslynScriptEngine>();
var scriptcs = scriptServicesBuilder.Build();
scriptcs.Executor.Initialize(new[] { "System.Web" }, Enumerable.Empty<IScriptPack>());
scriptcs.Executor.AddReferences(Assembly.GetExecutingAssembly());
var result = scriptcs.Executor.Execute("Hello.csx");
scriptcs.Executor.Terminate();
if (result.CompileExceptionInfo != null)
return new { error = result.CompileExceptionInfo.SourceException.Message };
if (result.ExecuteExceptionInfo != null)
return new { error = result.ExecuteExceptionInfo.SourceException.Message };
return result.ReturnValue;
}
The contents of my Hello.csx file:
Console.WriteLine("This output is lost forever");
So my question is how can I capture the text written to Console.WriteLine() and save it?

Related

Log Appium testresults to console

Appium won't log the test results (of the UI-tests, executed with adb emulator) to the debug output (Deug.WriteLine).
According to the documentation, get test logs is possible with the following line
ILogs logs = driver.Manage().Logs;
Hower, Appium has different log types:
Browser
Client
Driver
Profiler
Server
I tried every single log type with the following code. But by executing I don't get any result and the test will (where I put the code) fail. Does anyone have a solution for this problem?
ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Browser);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Client);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Driver);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Profiler);
// ReadOnlyCollection<LogEntry> logs = _driver.Manage().Logs.GetLog(LogType.Server);
foreach (var log in logs)
{
Debug.WriteLine("Time: " + log.Timestamp);
Debug.WriteLine("Message: " + log.Message);
Debug.WriteLine("Level: " + log.Level);
}
I just figure out.
Check this article first
relaxed Security AppiumService
Get the log type
IReadOnlyCollection<string> logTypes = driver.Manage().Logs.AvailableLogTypes;
foreach (string logType in logTypes)
{
Console.WriteLine(logType);
//logcat
//bugreport
//server
}
Print logs
public static void PrintLogs(string logType)
{
try
{
ILogs _logs = driver.Manage().Logs;
var browserLogs = _logs.GetLog(logType);
if (browserLogs.Count > 0)
{
foreach (var log in browserLogs)
{
//log the message in a file
Console.WriteLine(log);
}
}
}
catch(Exception e)
{
//There are no log types present
Console.WriteLine(e.ToString());
}
Picture : C# Console Print appium log
In java I am doing it using the following code:
List<LogEntry> logEntries = driver.manage().logs().get("logcat").getAll();
for (LogEntry logEntry : logEntries) {
System.out.println(logEntry);
}
Not sure if this method works for C#. Please give it a try
List<LogEntry> logEntries = _driver.Manage().Logs().Get("logcat").GetAll();

log4net how do I get the output of Log errors?

I have a c# web application and I would like to be able to see errors being thrown in the Service.asmx.cs section. I.e. when the sql statement fails or I spelled a variable name wrong.. etc. I'm fairly new to .net but the template for the app that I have has log4net installed.
protected static log4net.ILog Log = EarthSoft.Common.log4net.GetLogger(typeof (Service));
Then my get method is :
[WebMethod(Description = "Get a list of facilities")]
public List<Facility> GetFacilities()
{
try
{
var context = EarthSoft.Server.Helpers.RequestContext.GetRequestContext(true);
if (context.Connection == null || context.User == null) return null;
var lst = new List<Facility>();
string sql =
string.Format("select analyte_full,conc from dt_biological");
var cmd = context.Connection.CreateCommand(sql);
context.Connection.PrepareTextCommand(cmd);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
lst.Add(new Facility()
{
name = (string)reader.GetValue(0),
distance = (decimal)reader.GetValue(1)
});
}
}
context.Connection.Close();
return lst;
}
catch (Exception ex)
{
Log.Error(ex.Message, ex);
return null;
}
}
So the error message is appended to the Log here but I have no idea how to
access it or where it writes the log? Is it possible the log output is saved onto a table into the sql database somewhere?
Log4Net is higly configurable, and to specify "where" to log, it uses the "appender" concepts. There is a lot of appender already done that write in files/database/queues and you can write your own, if needed ( normally not ).
Anyway start to look at the examples from here: https://logging.apache.org/log4net/release/config-examples.html
Somewher ein your code, typically in the application initialization, you have to call:
XmlConfigurator.Configure()
This will read the configuration from the application configuration file.
Alternatively you can configure log4net programmatically, but it can be sort of advanced if you never used before with manual configuration, so I suggest to start looking at the examples above and use configuration from file.
If no configuration is supplied, log4net work perfectly but... no log are produced at all, and I think this is the reason you can't find any log at the moment.

How to skip the code which produces error when dll is Missing in C#?

I have a Console Application in C# and a Class Library named AppManager.cs. The Method of this class is used in the console Application as given below.
try
{
AppManager mgr = new AppManager(); //Want to skip this line when dll is missing.
mgr.Method_Name(this, true); //Want to skip this line when dll is missing.
}
catch (Exception)
{
}
When I have published the Code and extracted only exe then application fails to run [I know as the exe try to find that dll and method present in dll won't available].
Now my Question is That Is there any way to skip the code which will produce error when it will not find the reference of dll.
I also tried this but it didn't worked:
String file = null;
String filePath = Path.GetDirectoryName(Application.ExecutablePath);
file = Directory.GetFiles(filePath, "myLibrary.dll", SearchOption.AllDirectories)
.FirstOrDefault();
if (file != null)
{
AppManager mgr = new AppManager();
mgr.Method_Name(this, true);
}
If you do that, it won't give the desired output too. Why don't you integrate the dll with your exe? You can do it by using this tool
Can you not simply check if the dll exists or not?
string filePath = #"SOME_PATH";
var exists = File.Exists(filePath);
if(exists)
{
AppManager mgr = new AppManager();
mgr.Method_Name(this, true);
}

Set Script Task code dynamically in SSIS 2012

In my application, a script task is created dynamically.
In SQL Server 2008's implementation of SSIS, the following method worked fine.
private void SetSourceCode(ScriptTask scriptTask, string code, string codeName)
{
string fileName = "ScriptMain.vb";
string language = "VisualBasic";
string proj = ".vbproj";
scriptTask.ScriptLanguage = VSTAScriptLanguages.GetDisplayName(language);
scriptTask.ScriptingEngine.InitNewScript(language,
scriptTask.ScriptProjectName, proj);
scriptTask.ScriptingEngine.ShowDesigner(false);
scriptTask.ScriptingEngine.AddCodeFile(fileName, code);
if (!scriptTask.ScriptingEngine.Build())
throw new Exception("Failed to build vb script code: " + codeName);
scriptTask.ScriptingEngine.SaveScriptToStorage();
if (!scriptTask.ScriptingEngine.CloseIDE(false))
{
throw new Exception("Unable to close Scripting engine.");
}
}
How do I migrate this code to SQL Server 2012, because following methods are removed from SQL Server 2012 dll-s (assemblies):
InitNewScript
AddProjectReference
AddCodeFile
SaveScriptToStorage
CloseIDE
Build
ShowDesigner
Generally, how do I dynamically set source code for script task in SQL Server 2012?
As you've noticed, the VSTA helper methods you could use in 2008 were moved/removed in 2012. It is still possible to do, but the code has changed.
The easiest thing to do is load an existing project using VstaHelper.LoadProjectFromFolder().
If you want to dynamically add script files, see the snippet below. There are two main things you need to keep in mind:
The ScriptingEngine and VstaHelper classes represent VSTA itself. This is where you’d create the project, and add new files. You cannot remove or replace an existing file directly here. When you call SaveProjecToStorage(), it's like closing the VSTA window … it saves the project and compiled binary to the ScriptTask.
ScriptTask.ScriptStorage allows you to directly manipulate the source file contents. From here, you can modify the content of a file.
The following code snippet should help you get started.
static void Main(string[] args)
{
// 1. Create new package, and add a script task
var pkg = new Package();
var exec = pkg.Executables.Add("STOCK:ScriptTask");
var th = (TaskHost)exec;
th.Name = "Script Task";
th.Description = "This is a Script Task";
var task = (ScriptTask)th.InnerObject;
// 2. Set the script language - "CSharp" or "VisualBasic"
task.ScriptLanguage = VSTAScriptLanguages.GetDisplayName("CSharp");
// 3. Set any variables used by the script
//task.ReadWriteVariables = "User::Var1, User::Var2";
// 4. Create a new project from the template located in the default path
task.ScriptingEngine.VstaHelper.LoadNewProject(task.ProjectTemplatePath, null, "MyScriptProject");
// 5. Initialize the designer project, add a new code file, and build
//task.ScriptingEngine.VstaHelper.Initalize("", true);
//task.ScriptingEngine.VstaHelper.AddFileToProject("XX.cs", "FileContents");
//task.ScriptingEngine.VstaHelper.Build("");
// 6. Persist the VSTA project + binary to the task
if (!task.ScriptingEngine.SaveProjectToStorage())
{
throw new Exception("Save failed");
}
// 7. Use the following code to replace the ScriptMain contents
var contents = File.ReadAllText("path to file");
var scriptFile =
task.ScriptStorage.ScriptFiles["ScriptMain.cs"] =
new VSTAScriptProjectStorage.VSTAScriptFile(VSTAScriptProjectStorage.Encoding.UTF8, contents);
// 8. Reload the script project, build and save
task.ScriptingEngine.LoadProjectFromStorage();
task.ScriptingEngine.VstaHelper.Build("");
// 9. Persist the VSTA project + binary to the task
if (!task.ScriptingEngine.SaveProjectToStorage())
{
throw new Exception("Save failed");
}
// 10. Cleanup
task.ScriptingEngine.DisposeVstaHelper();
// 11. Save
string xml;
pkg.SaveToXML(out xml, null);
File.WriteAllText(#"c:\temp\package.dtsx", xml);
}

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

I want to run on a C# program a specific running file and during it display the output on 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);
}
}

Categories