I’m building a web UI to help automate our deployment process and am going to write a powershell script to do the deployment and would like it’s Write-Debug (or any statement to log, just let me know which to use 🙂 ) statements to be logged to the deployed package‘s database variable Log. I haven’t really used log4net before so please don’t laugh if I’m doing this completely wrong.
I figure since the location is dynamic, I’d have to code the log4net appenders, but would it be easier/better to do all of the log4net stuff inside of the powershell script? I read this and found I should use ps.Streams.Debug.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e) to get the write-debug information.
Here is what I have so far:
public static void Test(Package pkg)
{
//Do roll_out
//Creates a cmd prompt
PowerShell ps = PowerShell.Create();
string myCommand = @"C:\Users\evan.layman\Desktop\test.ps1";
ps.AddCommand(myCommand);
ps.Streams.Debug.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e)
{
PSDataCollection<DebugRecord> debugStream = (PSDataCollection<DebugRecord>)sender;
DebugRecord record = debugStream[e.Index];
Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
hierarchy.Root.RemoveAllAppenders(); /*Remove any other appenders*/
AdoNetAppender appender = new AdoNetAppender();
appender.ConnectionString = ConfigurationManager.ConnectionStrings["DeploymentConnectionString"].ConnectionString;
appender.CommandText = "with cte as (SELECT * FROM Package PackageID =" + pkg.PackageID + ") UPDATE cte SET (Log) VALUES (?logText)";
AdoNetAppenderParameter param = new AdoNetAppenderParameter();
param.DbType = System.Data.DbType.String;
param.ParameterName = "logText";
param.Layout = new log4net.Layout.RawTimeStampLayout();
appender.AddParameter(param);
BasicConfigurator.Configure(appender);
ILog log = LogManager.GetLogger("PowerShell");
log.Debug(record.Message);
//log.DebugFormat("{0}:{1}", DateTime.UtcNow, record);
//log.Warn(record, new Exception("Log failed"));
});
Collection<PSObject> commandResults = ps.Invoke();
Hopefully I can get this working 🙂
I would keep as much log4net config out of your code as possible. In your code, the config is being recreated on each debug statement, which is inefficient.
It’s possible to do what you want using event context properties in log4net. I’ve blogged about log4net event context a bit on my blog.
Here’s a quick example that’s close to your existing codebase….
This C# code shows how to use log4net global properties to store custom event context data; note the setting of the “PackageID” global property value before the pipeline is executed…
And here is the app.config that drops the logs into the database; note the custom PackageID parameter in the SQL, and how the value is pulled from the log4net property stack:
Hope this helps.