log4net using both BufferedAppender and RollingFileAppender - c#

I have a problem similar to several other already discussed here and also on log4net manual, but I need a bit more complex behaviour.
I would like to log Info, Warn, as well as Error events.
I also want to "dump" latest 50 events (Debug, Info and Warn) when I get an error
I would like to write both this logs to the same logfile file.
I know something "similar" is possible with BufferedAppender, but I tried and I only got the dump when there is an error, so I loose all the Info and Warn events if I do not get any error (If I lower the threshold of BufferedAppender I get all the logs every event).
I think I need to create a ForwardingAppender and a BufferedAppender that both forward to the same RollingFileAppender, but I do not know how to correctly do it.
Is it possible with versione 1.2.10 for Fx 2.0 ? I need this for customer request...
Moreover, can you please write the solution both in XML .config file and also in C# programmatically configuration?
Thanks

After 2 days of trying I finally found the solution.
(I configure it programmatically, if you need the xml configuration I'm sorry you have to figure it out)
log4net.Appender.ForwardingAppender direct; // Global variable to enable threshold change
public Form1()
{
InitializeComponent();
// configuring Log4net
log4net.Appender.RollingFileAppender roller = new log4net.Appender.RollingFileAppender();
roller.AppendToFile = true;
roller.File = "TestLog.log";
roller.MaxSizeRollBackups = 10;
roller.DatePattern = "yyyyMMdd";
roller.Layout = new log4net.Layout.PatternLayout("%date %-5level - %message%newline%exception");
roller.RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Date;
roller.StaticLogFileName = true;
roller.ActivateOptions();
//log4net.Config.BasicConfigurator.Configure(roller); Do not activate logger, otherwise you will have all events twice
direct = new log4net.Appender.ForwardingAppender();
direct.Name = "direct";
direct.Threshold = log4net.Core.Level.Warn;
direct.AddAppender(roller);
direct.ActivateOptions();
log4net.Config.BasicConfigurator.Configure(direct);
log4net.Appender.BufferingForwardingAppender buffer = new log4net.Appender.BufferingForwardingAppender();
buffer.Name = "Buffer";
buffer.BufferSize = 50;
buffer.Lossy = true;
buffer.Evaluator = new log4net.Core.LevelEvaluator(log4net.Core.Level.Error);
buffer.AddAppender(roller);
buffer.ActivateOptions();
log4net.Config.BasicConfigurator.Configure(buffer);
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (numericUpDown1.Value == 0)
direct.Threshold = log4net.Core.Level.Debug;
if (numericUpDown1.Value == 1)
direct.Threshold = log4net.Core.Level.Info;
if (numericUpDown1.Value == 2)
direct.Threshold = log4net.Core.Level.Warn;
if (numericUpDown1.Value == 3)
direct.Threshold = log4net.Core.Level.Error;
}
If you have a cleaner way to do it, please let me know.
Also I was not able to log in different layouts nor to add
"**** Dump start ****" and "**** Dump end ****" at the beginning and ending of the dump done by BufferingForwardingAppender.
Let me know if you know how to do it.

Related

How to point to the correct Store in Outlook automation by C#?

I have a lot of VBA automation that interlinks an Outlook and Word solution; it is fine, but time is inexorable... so, I'm start to decorating and extending that old solution, wraping it with C#/VS2017.
Through a conventional Winform I can choose my patients, and from this action I do a lot of actions, including open the correct Outlook contact; that's the problem, because I can't get the correct Store; the patients.pst, depending on the machine, may be the 1st, 2nd, 3rd...
In VBA I do this:
WhichStoreNameToPointAt="patients"
Set myNamespace = myolApp.GetNamespace("MAPI")
For i = 1 To myNamespace.Stores.Count Step 1
If myNamespace.Stores.item(i).DisplayName = WhichStoreNameToPointAt Then
intOutlookItemStore = i
End if
End If
Set myFolderPatients = myNamespace.Stores.item(intOutlookItemStore).GetDefaultFolder(olFolderContacts)
And it always functions like a charm.
In C# I tried a lot of variations, and could not point to the correct store:
public void OpenPatientContact(string patientName)
{
Outlook.Store whichStore = null;
Outlook.NameSpace nameSpace = OlkApp.Session;
int i = 1;
foreach (Outlook.Folder folder in nameSpace.Folders)
{
bool p = false;
if (whichStoreNameToPointAt == folder.Name)
{
p = true;
whichStore = folder.Store;
//Correct Store selected; I can tell because of this watch:
//whichStore.displayname == whichStoreNameToPointAt
}
i++;
if (p)
break;
}
var contactItemsOlk = whichStore.Session.GetDefaultFolder
(Outlook.OlDefaultFolders.olFolderContacts).Items;
// The problem is below; always the first Store
Outlook.ContactItem contact = (Outlook.ContactItem)contactItemsOlk
.Find(string.Format("[FullName]='{0}'", patientName)); //[1];
if (contact != null)
{
contact.Display(true);
}
else
{
MessageBox.Show("The contact information was not found.");
}
}
Unfortunately, it keeps pointing ever to the same first Store, the one that has no patients...
If I change the Store order I can get past this and test other stuff, but of course it is not the right way.
Any other heads/eyes to see the light?
TIA
While seated writing the question, looking at a yellow rubber duck - and a lot of other stuff that belongs to my 1 yo daughter ;), I realized that whichStore.Session.GetDefaultFolder is a little strange in this context. I only changed this
var contactItemsOlk = whichStore.Session.GetDefaultFolder
(Outlook.OlDefaultFolders.olFolderContacts).Items;
To that:
var contactItemsOlk = whichStore.GetDefaultFolder
(Outlook.OlDefaultFolders.olFolderContacts).Items;
Voilá! Magic happens with C# too!
Session returns the default NameSpace object for the current session.
PS: yellow rubber duck; guys of The Pragmatic Programmer really knows some secrets and tricks ;)
Thanks Thomas and Hunt!

C# - Getting the full picture from WUAPI

I am trying to collect an accurate picture of Windows Updates, specifically KB installations, on a number of different machines. I've tried a number of different pieces of code that I've found scattered about, but I still cannot seem to create an accurate picture of what is installed. By accurate, I mean that whatever I gather seems to be a subset of what is shown when I check the Windows Update History on the machine using the Windows UI! Can't seem to figure this out!
Here are a few things I've tried;
UpdateSession uSession = new UpdateSession();
IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher();
uSearcher.Online = false;
ISearchResult sResult = uSearcher.Search("IsInstalled=1");
foreach (IUpdate update in sResult.Updates)
{
foreach (string kbaid in update.KBArticleIDs)
{
txtAllUpdates.AppendText(kbaid + Environment.NewLine);
}
}
I also tried adding code within this same routine to gather all of the updates within the Bundled Updates field, like so;
foreach (IUpdate update2 in update.BundledUpdates)
{
txtAllUpdates.AppendText("\t--> " + update2.Title + Environment.NewLine);
foreach (string kbaid2 in update2.BundledUpdates)
{
string kbNo = GetKBNo(update2.Title.ToLower());
txtAllUpdates.AppendText("\t\t" + kbNo);
}
}
I also tried looking at the Update History, but that provided me with yet another set of data - still not complete!
UpdateSession updateSession = new UpdateSession();
IUpdateSearcher updateSearcher = updateSession.CreateUpdateSearcher();
int count = updateSearcher.GetTotalHistoryCount();
MessageBox.Show("Total Count = " + count);
IUpdateHistoryEntryCollection history = updateSearcher.QueryHistory(0, count);
for (int i = 0; i < count; ++i)
{
txtAllUpdates.AppendText("\t\t\t" + history[i].Title);
}
I also checked into some code that leverages the registry, but from what I've read, that's not the right way to do things. At this point, I'm performing a number of different queries, searching entries for "KB" references and building a list and removing duplicates, but I'm still not getting the same list I see on the screen! Even if this did work, it can't possibly be the right way to go - I feel like I must be missing something.
Finally, I tried to just get information on when updates were last checked for and installed - even that doesn't match up with what is displayed. I did this with the following code;
var auc = new AutomaticUpdatesClass();
DateTime? lastInstallationSuccessDateUtc = null;
if (auc.Results.LastInstallationSuccessDate is DateTime)
lastInstallationSuccessDateUtc = new DateTime(((DateTime)auc.Results.LastInstallationSuccessDate).Ticks, DateTimeKind.Utc);
DateTime? lastSearchSuccessDateUtc = null;
if (auc.Results.LastSearchSuccessDate is DateTime)
lastSearchSuccessDateUtc = new DateTime(((DateTime)auc.Results.LastSearchSuccessDate).Ticks, DateTimeKind.Utc);
lblInstall.Text += lastInstallationSuccessDateUtc.ToString();
lblSearch.Text += lastSearchSuccessDateUtc.ToString();
Does anyone have some expertise in this area? Really want to get this done right!
Thanks for taking the time to read!
Respectfully,
Marshall
All the various ways to find installed software is incomplete, so I used a variety of ways in Get-KbInstalledUpdate, which I describe as a:
Replacement for Get-Hotfix, Get-Package, searching the registry and searching CIM for updates
Though I haven't tried this way, which is essentially:
$session = New-Object -ComObject "Microsoft.Update.Session"
$updatesearcher = $session.CreateUpdateSearcher()
$count = $updatesearcher.GetTotalHistoryCount()
$updates = $updatesearcher.QueryHistory(0, $count)

Best way to check SVN revision changes of directory and return if changes are found

I have been looking for a while now about how to check the revision and return if there has been changes. What I have works but I am wondering if there is a better/ cleaner way:
bool needToUpdate = false;
Process process = new Process();
var info = new ProcessStartInfo("svn", string.Format(#"status -u {0}", directoryInto.FullName));
process.StartInfo = info;
process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
string data = e.Data;
//This if statement is what I have an issue with.
if (data.Contains("!"))
needToUpdate = true;
}
});
As you can see I am checking the string to find a (!) in the string. Its not great practice, so I am seeing if anyone would be able to help me out with a better solution or a better way to receive the standard output from the SVN (I am not asking how to do it, I am asking a better way than I already have) I would greatly appreciate it!
Thanks!
EDIT Using SharpSVN
I am now using SharpSVN and I having a problem with IsRemoteUpdate, it seems to always be returning true:
bool needToUpdate = false;
using (SvnClient client = new SvnClient())
{
SvnStatusArgs statusArgs = new SvnStatusArgs();
statusArgs.RetrieveAllEntries = true;
statusArgs.RetrieveRemoteStatus = true;
client.GetStatus(directoryInto.FullName, statusArgs, out statuses);
for (int i = 0; i < statuses.Count(); i++)
{
if (statuses[i].IsRemoteUpdated)
{
needToUpdate = true;
break;
}
}
}
Could you tell me why this is always returning true, even when my directory is updated?
Edit #2
I have since added:
if (statuses[i].LocalContentStatus == SvnStatus.Missing || statuses[i].LocalContentStatus == SvnStatus.Modified)
is there a better way to do this?
There is a discussion on the SharpSVN forum about this: How to check if working copy is latest version? There they use similar solution, like you: get status, iterate through items, if any has IsRemoteUpdated, the SVN directory needs to be updated.

Gstreamer: Link a bin with two sinks to playbin2

I want to read in an SDP file and encode the video stream into h.264 and the audio stream into aac. Then I want to multiplex those streams into an avi stream and then to a file. I don't know the contents of the SDP file ahead of time, so it seems easiest to use playbin2.
So, I thought I could do something like this:
RawToAviMux Bin
______________________________________
----- |ghostpad----x264enc
/ | \
playbin2------ | avimux--filesink
\ | /
-------| ghostpad----ffenc_aac
|_______________________________________
setting playbin2's videosink to an instance of RawToAviMux
and playbin2's audiosink to the same instance of RawToAviMux
However, I cannot get the pipeline into the playing state.
Here is the code:
recorder = new Gst.BasePlugins.PlayBin2();
recorder.PlayFlags &= ~((Gst.BasePlugins.PlayBin2.PlayFlagsType)(1 << 2));
recorder.Bus.AddSignalWatch();
recorder.Bus.EnableSyncMessageEmission();
RawToAviMuxer aviMuxer = new RawToAviMuxer(fileName);
recorder.VideoSink = aviMuxer;
recorder.AudioSink = aviMuxer;
recorder.SetState(Gst.State.Ready);
recorder.Uri = #"file:///" + filePath.Replace('\\', '/');
recorder.SetState(Gst.State.Paused);
recorder.SetState(Gst.State.Playing);
Gst.State currentState;
Gst.State playingState = Gst.State.Playing;
Gst.StateChangeReturn stateReturn = recorder.GetState(out currentState, out playingState, Gst.Clock.Second);
if (stateReturn != Gst.StateChangeReturn.Failure)
return true;
return false;
With RawToAviMuxer as
public class RawToAviMuxer : Gst.Bin
{
bool test = false;
public RawToAviMuxer(string outputFileName)
: base("rawToAviMux")
{
Gst.Element x264Enc = Gst.ElementFactory.Make("x264enc");
Gst.Element ffenc_aac = Gst.ElementFactory.Make("ffenc_aac");
x264Enc["bframes"] = (uint)0;
x264Enc["b-adapt"] = false;
x264Enc["bitrate"] = (uint)1024;
x264Enc["tune"] = 0x4;
x264Enc["speed-preset"] = 3;
x264Enc["sliced-threads"] = false;
x264Enc["profile"] = 0;
x264Enc["key-int-max"] = (uint)30;
Gst.GhostPad videoToX264Pad = new Gst.GhostPad("video_sink", x264Enc.GetStaticPad("sink"));
Gst.GhostPad audioToAACPad = new Gst.GhostPad("audio_sink", ffenc_aac.GetStaticPad("sink"));
test = this.AddPad(videoToX264Pad);
test = this.AddPad(audioToAACPad);
Gst.Element aviMux = Gst.ElementFactory.Make("avimux");
Gst.Element fileSink = Gst.ElementFactory.Make("filesink");
test = this.Add(new Gst.Element[]{x264Enc, ffenc_aac, aviMux, fileSink});
test = x264Enc.Link(aviMux);
test = ffenc_aac.Link(aviMux);
test = aviMux.Link(fileSink);
fileSink["location"] = outputFileName;
}
}
I have stepped through in the debugger and all of the links are successful.
update
Ok, so I tried the following pipeline with Gst.Parse.Launch:
uridecodebin uri=file:///C:/Users/Jonathan/AppData/Local/Temp/192.168.0.215_5000.sdp !
x264enc byte-stream=true bframes=0 b-adapt=0 tune=0x4 speed-preset=3 sliced-threads=false
profile=0 ! mux. ffenc_aac ! mux. avimux name=mux ! filesink location=C:\Users\Jonathan\Desktop\test.avi
I still can't get it out of paused.
I am using the windows build, so I am worried maybe there is something wrong with that?
I also can't attach a message handler to the bus so that I can figure out what is going on, which is really starting to get annoying.
I did just find this however,
If I directly grab the streams via an udpsrc element, knowing what the formats are ahead of time, it does not work with just an rtph264depay element. There must be an h264parse element in the pipeline. This may be the reason that uridecodebin isn't working for me?
solved
I ended up doing the following:
if (!gst_is_init)
{
Gst.Application.Init();
gst_is_init = true;
}
if(recorder != null)
{
recorder.SetState(Gst.State.Null);
recorder.Dispose();
}
string videoDepay, audioDepay, strExtension, strMuxer;
GetGstElements(stream.VideoCaps, out videoDepay, out strMuxer, out strExtension);
GetGstElements(stream.AudioCaps, out audioDepay, out strMuxer, out strExtension);
fileName = Path.ChangeExtension(fileName, strExtension);
//recorder = new Gst.Pipeline("recordingPipe");
string pipeString = String.Format("udpsrc port={0} ! {1} {2} ! {3} ! queue ! mux. udpsrc port={4} ! {1} {5} ! {6} ! mux. {7} name=mux ! filesink location={8}",
portToUse, "application/x-rtp,", stream.VideoCaps, videoDepay, (portToUse + 2), stream.AudioCaps, audioDepay, strMuxer, fileName.Replace("\\", "\\\\"));
recorder = (Gst.Pipeline)Gst.Parse.Launch(pipeString);
recordingFileName = fileName;
recorder.SetState(Gst.State.Ready);
recorder.SetState(Gst.State.Paused);
recorder.SetState(Gst.State.Playing);
Gst.State currentState;
Gst.StateChangeReturn stateReturn = recorder.GetState(out currentState, Gst.Clock.Second);
if (stateReturn != Gst.StateChangeReturn.Failure)
return true;
return false;
You have to have a parser for all streams, for the pipeline to go to playing. So, in the case of an incomming h264 stream, I would need to use rtph264depay ! h264parse. In addition the NALU and byte-stream must match for the timing to be right.
Also, in order for the file to be usable, you have to send an EOS downstream before disposing of the pipeline.
recorder.SendEvent(Gst.Event.NewEos());
System.Threading.Thread.Sleep(100);
recorder.SetState(Gst.State.Paused);
recorder.SetState(Gst.State.Ready);
recorder.SetState(Gst.State.Null);
recorder.Dispose();
Yes, this won't work. Here is how you can do it. Playbin2 is a modular component, it consists of an uridecodebin and a playsinkbin. You can just use and uridecodebin, set your media file uri, add signal handlers for pad-added and connect the newly created pads to the sink-pads of your rawtoavimux component.
One alternative for rawtoavimux would be to use encodebin. Using uridecodebin ! encodebin can potentially to smart transcoding which would avoid the decoding and re-encoding if the format of one or more stream is already in the correct format.

Converting VBS code to C#

I just have the below code that was provides as hMailServer's DCOM API at http://www.hmailserver.com/documentation/latest/?page=com_example_account_create The below script works fine. It has no reference nothing. Just after installing hMailServer, running the below code can create an account. Now, I need the same thing in C#. They didn't provide me with any library for C# I googled for it but no relevant results all I have is the below code but in according to hMailServer API they said you can convert the below script to any language that you want. But how? I can't even understand how to start to write even the first line. Anybody please help me.
Dim obApp
Set obApp = CreateObject("hMailServer.Application")
' Authenticate. Without doing this, we won't have permission
' to change any server settings or add any objects to the
' installation.
Call obApp.Authenticate("Administrator", "your-main-hmailserver-password")
' Locate the domain we want to add the account to
Dim obDomain
Set obDomain = obApp.Domains.ItemByName("example.com")
Dim obAccount
Set obAccount = obDomain.Accounts.Add
' Set the account properties
obAccount.Address = "account#example.com"
obAccount.Password = "secret"
obAccount.Active = True
obAccount.MaxSize = 100 ' Allow max 100 megabytes
obAccount.Save
Add the COM object (hMailServer) to your C# project as a reference and translate the rest of the code to C#.
It will look something like this:
var app = new hMailServer.Application();
// Authenticate. Without doing this, we won't have permission
// to change any server settings or add any objects to the
// installation.
app.Authenticate("Administrator", "your-main-hmailserver-password");
// Locate the domain we want to add the account to
var domain = app.Domains["example.com"];
var account = domain.Accounts.Add();
// Set the account properties
account.Address = "account#example.com";
account.Password = "secret";
account.Active = true;
account.MaxSize = 100; // Allow max 100 megabytes
account.Save();
I hope this is still relevant and can help someone. Here I simply used the get item by name property to pull the domain name configured in the hMailServer.
protected void Page_Load(object sender, EventArgs e)
{
var app = new hMailServer.Application();
// Authenticate. Without doing this, we won't have permission
// to change any server settings or add any objects to the
// installation.
app.Authenticate("Administrator", "your.admin.password.here");
// Locate the domain we want to add the account to
var domain = app.Domains.get_ItemByName("your.configured.domain.name.here");
var account = domain.Accounts.Add();
// Set the account properties
account.Address = "account.name.here";
account.Password = "pass.word.here";
account.Active = true;
account.MaxSize = 100; // Allow max 100 megabytes
account.Save();
}

Categories