PITADEV
Curiosity killed the developer's project.

Setting an EventLogAppender’s Event Source at Runtime

Friday, 17 July 2009 09:37 by aj

I’m switching one of my big frameworks over from a homegrown logging solution to the beautifully simple and powerful log4net.  One of the many conveniences offered with log4net is the ability to log messages to many different message repositories with one call.  Our application requires three logging repositories: a table in our DB2 database, SMTP email, and the event log.  Using the built-in thresholds, the DB2 table receives all messages, while email and the event log only receive Error and Fatal messages.

This particular framework runs under a Windows Service, which uses a Factory to load multiple implementation assemblies that all execute on their own child thread.  Each implementation is responsible for its own set of activities, of course, and the service itself knows only to check the child threads every now and then to make sure they aren’t dead.  Using the default setup for the EventLogAppender, you can set the event source in the configuration by setting the EventLogAppender’s ApplicationName property.  In this case, all of the configuration for log4net is set in the service’s app.config file, which means that the settings for each appender are global to the service.  That’s fine, for most applications, but in this case it would be very convenient to change the event source to something that’s indicative of the specific implementation thread making the logging call, making it much easier to find messages related to a particular implementation.

The obvious way to do this would be to set the ApplicationName property at runtime before each logging call.  However, after much Googling, I wasn’t able to find an example of how to do this.  So I mused and ruminated and muddled about for a while, and eventually started fiddling in the Immediate Window while debugging my test application, attempting to figure out how to access specific appender properties at runtime.  Turns out the solution is simpler than I could have imagined.  Using the excellent tutorials provided by Sir Beefy, I set up my log object like this:

   1: //Console app, global
   2: private static log4net.ILog log;
   3:  
   4: //In void Main()
   5: log4net.Config.XmlConfigurator.Configure();
   6: log = log4net.LogManager.GetLogger(
   7:     System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

Then I provided a Log() method in the abstract class that is required for each implementation that looks something like this:

   1: protected void Log(string appName, 
   2:                    string message, 
   3:                    Exception ex, 
   4:                    log4net.Core.Level entryLevel)
   5: {
   6:     //Set the application name of the EventLogAppender
   7:     ((log4net.Appender.EventLogAppender)
   8:         log.Logger.Repository.GetAppenders().Single(
   9:         a => a.Name == "EventLogAppender")
  10:         ).ApplicationName = appName;
  11:     //do whatever else to log the entry
  12: }

That’s it.  It might be a better idea to use a GetType() call to find the Appender of type EventLogAppender and set the ApplicationName property that way, but I don’t think that’s necessary here.  So if you’re looking to change the Event Source at runtime using log4net to an event log, this is one way to do it without having to change the configuration file on the fly.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:  
Categories:   .Net | Tools
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

Raw Power

Wednesday, 27 May 2009 12:56 by aj

One of the problems that comes along with writing my own blog on software development is that I often expose my own ignorance.  I don’t pretend to know everything, by any stretch of the imagination, but writing stuff that anyone can read comes with a modicum of “assumed authority,” which I clearly don’t have.  However, I sometimes stumble across things that are so cool that I just have to write about them, no matter how behind-the-times they may make me look.  So, I’ll just come out and say it now: Powershell rocks my world.

Yesterday I was dinking around with a web-forms app that required some mildly tricky string manipulation.  When coding in Visual Studio, I’ve often found myself wishing that there were some way to use the Immediate Window to evaluate runtime behavior at design time.  For example, I can never remember string formats.  The .ToString(“format”) method is extremely common, but I’ve already got too much crap crammed in my skull to remember all of the possible values for “format.”  What I’ve always done in the past is fire up my project in Debug mode, put a breakpoint somewhere, and then use the Immediate Window to figure out which argument I need to pass to ToString().  There’s probably a better way to do this in general, but somehow my string of searches yesterday led to Powershell, and goodness am I happy about it.  It’s not that I didn’t know that Powershell existed.  Brad’s been singing its praises forever.  I just never took the time to understand how powerful it is. 

A known bug with the Immediate Window that I don’t believe has been solved yet is the “'RegularExpressions' is not a member of 'Text'” exception.  If you’ve ever tried to execute RegularExpressions in the Immediate Window, you’ve probably seen this.  After downloading and installing Powershell, I started out working on how to test the evaluation of a Regex.Replace() statement.  First, I had to figure out how to get the System.Text.RegularExpressions namespace loaded.  I found a nice blog post that taught me how to do that, and I’m pretty sure that you can have a Powershell script execute on launch as part of your profile.  This little guy will definitely be a part of it when I have time to figure it out.

   1: PS > $GacRootDir = Join-Path -Path $Env:SystemRoot -ChildPath "Assembly/Gac"
   2: PS > Get-Childitem -path $GacRootDir -recurse -include *.dll| Foreach-Object {$_.FullName} | Foreach-Object {([Reflection.Assembly]::LoadFrom($_))}

 

After that, most of what you can do is a matter of learning the syntax, and for a guy who spent the first six-odd years of his programming career using VIM in command prompts, I must admit that it’s strangely comforting to get back to a little command-line scripting.  Once my GAC assemblies were loaded, I set about solving my string manipulation problem.  Specifically, I needed to set a NavigateUrl property to a UNC path, which then got sent to a JavaScript method.  The code-behind is VB, but JavaScript, of course, likes it’s back-slashes to be escaped like C#.  For some reason my brain thought that I should use Regex.Replace() for this (maybe scripting reminded me enough of Perl that I wanted to do a little $foo =~ s/\\/\\\\/g; or something).  Testing this in Powershell was simple:

RegexReplacePS Lovely.  Really, it was.  I didn’t have to fire up a debugging session to find out that Regex.Replace() is going to be funny about back-slashes too, which, if I’d actually taken two seconds to think, I would have already known.  Then, I remembered String.Replace():

PSStringReplace I know, I know, this is elementary stuff.  But the beautiful thing to me here is that Powershell let me do this in a matter of seconds at design-time rather than having to write a separate application or fire up the debugger

Even more fun, I learned how to load unsigned .Net assemblies (and COM!) into Powershell so I can run methods for testing and support issues without having to write a test app.  Simply using System.Reflection.Assembly, same as I would to load an unreferenced assembly in code, I can initialize a new object and then view its members and execute its methods. 

So, maybe I just made myself look ignorant, but I needed to say something because I’m so excited to be finally learning to work with this powerful tool.  If you’re a .Net developer and you’re not already using Powershell, I highly recommend that you go and check it out.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:  
Categories:   .Net | Tools
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

A Necessary Evil

Tuesday, 19 May 2009 08:50 by aj

Creative Commons user Paulus Veltman

One of the most well-known code smells in .Net development is explicitly messing with the Garbage Collector.  In my coding travels, I’ve had at least a couple of situations where a performance/memory consumption issue was traced back to a GC.Collect() call.  In my own applications, I’ve generally followed Rico Mariani’s Rule #1, “don’t use it.”  I figure the .Net Framework team has a much better idea of how to handle that sort of stuff than I do, and for the most part, their ideas work just fine.

I’ve been working on a project for the last month or so that requires the use of an OCR API to read type-written words from TIFF images and then run them up against a set of regular expression patterns looking for useful data.  There are several OCR libraries available for purchase, but after significant testing and analysis, we decided that the Microsoft Office Document Imaging (MODI) library that comes with Office 2003 works about as well as anything else, and has the added benefit of being free, since our workstations haven’t been upgraded to Office XP.  I spent some time figuring out how to use MODI for OCR, then found a nice tutorial on Code Project that would have saved me some time.

Our TIFF images are stored in a homegrown database/file system setup that, after years of working with FileNet, is a cool breeze on a hot day.  Through more prototyping, I found that MODI OCR’s performance was best if I copied each multipage TIFF file to the local system, split them into single page files, and then ran each file through the OCR process, cleaning everything up afterward.  Here’s a chopped down version:

   1: MODI.Document modiDoc = null;
   2: MODI.Image modiImage = null;
   3: MODI.Word modiWord = null;
   4: List<String> filesToProcess = null;
   5: try
   6: {
   7:     filesToProcess = SplitTif(inputFile, workingFolder);
   8:     foreach (var fileToProcess in filesToProcess)
   9:     {
  10:         try
  11:         {
  12:             modiDoc = new MODI.Document();
  13:             modiDoc.Create(fileToProcess);
  14:             modiDoc.OCR(MODI.MiLANGUAGES.miLANG_ENGLISH, true, true);
  15:             for (var i = 0; i < modiDoc.Images.Count; i++)
  16:             {
  17:                 modiImage = (MODI.Image)modiDoc.Images[i];
  18:                 for (var j = 0; j < modiImage.Layout.Words.Count; j++)
  19:                 {
  20:                     modiWord = (MODI.Word)modiImage.Layout.Words[j];
  21:                     if (System.Text.RegularExpressions.Regex.IsMatch(modiWord.Text, regexPattern))
  22:                     {
  23:                         //Do stuff that needs to be done when there's a hit
  24:                     }
  25:                 }
  26:             }
  27:         }
  28:         finally
  29:         {
  30:             if (modiWord != null)
  31:                 DisposeCom(modiWord);
  32:             if (modiImage != null)
  33:                 DisposeCom(modiImage);
  34:             if (modiDoc != null)
  35:                 DisposeCom(modiDoc);
  36:             modiWord = null;        //This may be redundant...?
  37:             modiImage = null;
  38:             modiDoc = null;
  39:         }
  40:     }
  41: }
  42: catch (Exception ex)
  43: {
  44:     //handle it
  45: }
  46: finally
  47: {
  48:     //Cleanup temp files
  49:     if(filesToProcess != null && rdoNIS.Checked)
  50:         filesToProcess.ForEach(System.IO.File.Delete);
  51: }

Notice all of the cleanup.  I’ve learned the hard way that it’s never a bad idea to be very explicit about cleaning up COM objects, especially since a lot of COM objects don’t implement anything close to IDisposable.  So, with this code, I thought I was good to go.

And for the most part, I was.  My company has two main data centers: one in the building where I work, in a city I’ll call St. Small, and one that’s roughly 1,300 miles away, in a city I’ll call Sunville.  The TIFF images and the database I’m persisting data to are in Sunville.  The workstations that I started out running my client application for the OCR process are all in St. Small.  They worked fine, albeit slowly due to network latency and crappy hardware. But I wanted the process to run faster, since we have a lot of images to sift through, so I nabbed up the only workstation I could find in Sunville, set up my client, and started to run a batch.

I was quite surprised when I repeatedly and inconsistently received OutOfMemory exceptions from the MODI OCR method.  I checked all of the system resources, running programs, and RAM, and everything looked fine.  It’s running a dual-core processor at 2.4 GHz with 2 GB of RAM, which should be totally adequate for the MODI process, right?  Wrong.  No matter how hard I tried, I could not get these exceptions to go away.  What was even more interesting is that I wasn’t getting the errors on the workstations in St. Small.

So what’s the difference?  Duh.  Since the Sunville workstation doesn’t have nearly as much network latency to deal with, it does run quite a bit faster.  Since it’s able to run faster, it’s creating and destroying MODI COM instances much more frequently than the copies running on the St. Small systems.  So I did some more web research, more testing, and with a cringe, I added this line of code (and the comment) to my finally block: 

   1: //THIS IS VERY VERY BAD YOU BAD BOY
   2: GC.Collect();

I tested it on my workstation and performance didn’t seem to suffer too much.  So I dropped the new version of my application on the Sunville workstation and fired it up, thinking I’d solved the problem.

Nope.  The Sunville system still threw random OutOfMemory exceptions.

Creative Commons User Photos o' Randomness

What gives?  I thought GC.Collect() was the magic baseball bat that beat the crap out of everything?  If it isn’t, why is it so terrible to use it?  Well, the answer is, it is terrible to use.  Read Rico’s post and the many other articles on Garbage Collection, if you don't believe me.  But in my situation it seems necessary, since we have so many images to process and I don’t want to babysit every instance of the application.  I still had the problem, though.  Why wasn’t GC.Collect() working?

Because I wasn’t using it correctly, that’s why.  The client application I wrote was a quick and dirty Windows Forms app, so all of the MODI OCR calls were synchronous.  GC.Collect(), on the other hand, is not.  However, you can force it to be synchronous by adding one line of code, which I did, and now my application runs wherever I want it to.

   1: //THIS IS STILL VERY BAD AND YOU ARE STILL A BAD BOY
   2: GC.Collect();
   3: GC.WaitForPendingFinalizers();

It’s funny—I can actually see the points when the code execution is sitting on that line.  It doesn’t happen often, but it clears up all of my errors.  If anyone knows a better way, I would love to hear it.  I don’t want to use it, but for this project, I’ve come to believe that forcing garbage collection is a necessary evil.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   , ,
Categories:   .Net
Actions:   E-mail | del.icio.us | Permalink | Comments (2) | Comment RSSRSS comment feed

We All Laugh in the Same Language

Wednesday, 28 January 2009 15:09 by aj

As I've continued coding in VB.Net at my new(ish) job, the "WTF!!!" factor has reduced substantially, although I still do a lot of backtracking to insert Dim statements.  For the most part, I've realized I can do all the same stuff in VB that I did in C#, along with a couple of nice things like the great pile of VB snippets that ship with Visual Studio compared to the meager offerings for C#.  Don't get me wrong, I'd still rather be coding C#, but I've come to terms with the fact that they really are the same thing when it comes down to the CLR, and except for a few small differences, it's really just a matter of personal preference.

Or is it?

It's certainly not my personal preference, in this case.  So I guess it's enterprise preference.  But wait, who decides enterprise preference?  Someone obviously does, someone who probably makes a helluvalot more money than I do, and probably doesn't code any more if they ever wrote code in the first place.  What I've been wondering lately is this: if both VB and C# compile and run on the same .Net runtime, why do enterprises care which language its developers use to write .Net applications?  Just because I've been coding VB for the last three months doesn't mean that my C# skills have atrophied off like an unused extremity.  I can still read and write C#, and when I coded C# for a living ("enterprise standard" at my old job) I could still read and write VB.  Once folks understand the simple syntactic differences between the two, square brackets for arrays, angle brackets for generic type identifiers, "Dim thing as type," Function and Sub, etc., it's really not hard at all to read and understand both.

The common answer I hear to this question is that the enterprise wants everyone coding in the same language to...

  1. Promote code reuse.
  2. Make it so that if department A happens to be on a team building exercise at the archery range and they all get impaled on crazy boomerang arrows, department B can "seamlessly" pick up where the dead folks left off.

 

Superficially, these reasons make a lot of sense.  Code is easier to share and recycle if it's all in the same language.  Joe Developer coding VB in the Wonky Department on the 12th floor can read Jane Developer's code, even though she works on entirely different stuff in the Hinky Department on the 3rd floor.  And that's good, because Jane wrote a really cool snippet for connecting to the company mainframe and screen-scraping something or other.  But wait...why does Joe even need to read Jane's code?  It seems to me that if Jane wrote that functionality in C#, she can simply bundle it up into an assembly, compile it, and offer it to Joe as a referenceable DLL. 

OMG! But that DLL was written in C#!

All Joe sees are the members of Jane's object after he references, and Visual Studio translates that to VB for him anyway. 

IntelliSense even formats the tooltips in VB

 

Joe doesn't even see "public string CSharpMethod(string inVal)."  Pretty nifty runtime conversion there, if you ask me.  Another example of the inherent quality built into the Visual Studio IDE.

Can somebody please tell me why I can't write my applications in C# even though my boss wrote applications in VB?

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5