Wednesday, February 23, 2005

Using Enterprise Library Logging

To get logging working without pesky WMI/Performance counter errors on every logged event:

Per Tom Hollander's weblog

Go to the Logging project, Project Properties dialog for the Common project, and under Configuration Properties\Build, find the Conditional Compilation Properties property and remove ;USEWMI;USEPERFORMANCECOUNTER for the build type you're interested in (ReleaseFinal, in this case).

Ignore compile warnings about DB2 goop.

Delete any old project references and re-add reference to new version in C:\Program Files\Microsoft Enterprise Library\src\Logging\bin\ReleaseFinal.

Then add an appropriate using statement and use in code:

using System;

using System.Collections;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Web;

using System.Web.SessionState;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using Microsoft.Practices.EnterpriseLibrary.Logging;

using Microsoft.Practices.EnterpriseLibrary.Logging.Tracing;

 

namespace CAESDO

{

    /// <summary>

    /// Summary description for WebForm1.

    /// </summary>

    public class DefaultPage : System.Web.UI.Page

    {

        private void Page_Load(object sender, System.EventArgs e)

        {

            // Put user code to initialize the page here

            LogEntry logEntry = new LogEntry();

            logEntry.Message = "Starting up the application";

            Logger.Write(logEntry);

 

            // Now this is cool! Tracing flow of code through application

            // and it was simple to add an EmailAlert with an EmailSink

 

            using (new Tracer("Trace"))

            {

                Logger.Write("Hello world");

                Logger.Write("Hello by e-mail", "EmailAlerts",5,3000,Microsoft.Practices.EnterpriseLibrary.Logging.Severity.Information, "An e-mail message logging all kinds of stuff");

            }

 

        }

 

        #region Web Form Designer generated code

        override protected void OnInit(EventArgs e)

        {

            //

            // CODEGEN: This call is required by the ASP.NET Web Form Designer.

            //

            InitializeComponent();

            base.OnInit(e);

        }

 

        /// <summary>

        /// Required method for Designer support - do not modify

        /// the contents of this method with the code editor.

        /// </summary>

        private void InitializeComponent()

        {   

            this.Load += new System.EventHandler(this.Page_Load);

        }

        #endregion

    }

}



Voila!

It'd sure make it easier to post code to my weblog if VisualStudio 2005 included CopySourceAsHtml functionality. This is a great application, too bad it doesn't work for me. I seem to have uncovered the first interaction between CSAH and a trial VisualPerl installation that won't uninstall.

Par for the course.

Although, I've suggested to the Visual Studio 2005 guys that they add this feature.

P.S. Collin worked to fix CSAH, and I nuked and reinstalled my system, including Visual Studio 2003.NET. That seems to have done the trick.

Friday, February 18, 2005

Microsoft Security Awareness training

This is just too funny to pass up.

So far, malware's winning the war of attrition.

Well, at least Microsoft Anti-Spyware is going to be free.

Too bad there's already malware which targets it.

You do have a real hardware firewall, don't you? If not, there's no reason why you shouldn't -- here's my OpenBSD recipe that works beautifully on inexpensive hardware, and has big-bucks features like stateful filtering, source tracking, bandwidth queuing, NAT, OS detection, adaptive state table timeouts, MAC address tagging (with brconfig), macros and tables, and hardware failover capability. All for the price of an OpenBSD CD and whatever hardware you run it on.

(One of the firewalls I set up for a class C network was a Pentium 166 with 32MB of RAM, and it mostly sat at 99% idle filtering a 100MB full-duplex LAN. OpenBSD has a very efficient network stack. When I've gone around to help setup OpenBSD firewalls for departments at UC Davis, we mostly recycle leftover desktops that have been replaced.)

Of course, to help deal with malware you'll also have to do egress filtering (not just ingress filtering, where most rulesets stop), and as always, keep your systems patched.

But then, there's no such thing as a panacea.

Did I also mention that pf rules are nearly plain-language?

pf r0x0rs!

Thursday, February 17, 2005

Fun with Microsoft Enterprise Library, part 2

Okay, I can see that this is going to be a long series ....

Here's what I'm talking about on how to get Visual Studio to see Enterprise Library assemblies without browsing (taken from the GotDotNet workspace patterns & practices: Enterprise Library: Message Boards):

"Do you want to see EntLib assemblies in Add References message box?
Create a text file named entlib.reg, and add this content:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1\AssemblyFolders\Enterprise Library]
@="C:\\Program Files\\Microsoft Enterprise Library\\bin\\"

Double click on the file and you'll be asked whether to add this registry key. Click yes, restart vs.net and there you go.
(it is assumed that assemblies are in their default folder - otherwise, change the path above)."

Sigh. I really really try to avoid the Registry when possible.

There's another neat trick for sorting out the structure of the Enterprise Library: open one of the solutions in Visual Studio, select Project-> Visio UML -> Reverse Engineer.

Too bad all it actually does is generate a 75K blank Visio file, because Visio is unable to resolve all of the references.

I suppose this will work for code that's so simple that a UML diagram isn't needed.

Moving right along, I've also found how to sign all of the Enterprise Library Asemblies! You just generate your public/private key pair, and then reference them in the GlobalAssemblyInfo.cs file in:

C:\Program Files\Microsoft Enterprise Library\src
This file gets referenced by every project when it's compiled. Yay!

Except that every project's AssemblyInfo.cs contains blank references:

[assembly : AssemblyDelaySign(false)]
[assembly : AssemblyKeyFile("")]
[assembly : AssemblyKeyName("")]

Which overwrite what gets pulled in from GlobalAssemblyInfo.cs.

So you have to go through every project's AssemblyInfo.cs file and remove those 3 lines.

Sigh. There's 23 projects in the Security section alone, which is sort of the sine qua non for using the EL to begin with, for my purposes.

Well, Caching, Configuration, Data, ExceptionHandling, and Logging are also useful.

One step at a time.

I've gotten Logging to work. Unfortunately, every time it logs it throws three error messages:

"Failed to create instances of performance counter 'Distributor: # of Logs Distributed/Sec' - The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly..

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp."

"Failed to fire the WMI event 'LoggingLogWrittenEvent'. Exception: System.Exception: This schema for this assembly has not been registered with WMI.
at System.Management.Instrumentation.Instrumentation.Initialize(Assembly assembly)
at System.Management.Instrumentation.Instrumentation.GetInstrumentedAssembly(Assembly assembly)
at System.Management.Instrumentation.BaseEvent.Fire()
at Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation.InstrumentedEvent.FireWmiEventCore(BaseEvent baseEvent).

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp."

and

"Failed to create instances of performance counter 'Client: # of Logs Written/Sec' - The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly..

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp."

Presumably these counters are installed as part of the EL service installation, but going through the batch file yields for logging:

@ECHO.
@ECHO ---------------------------------------------------------------------------------
@ECHO Installing Services for the Logging and Instrumentation Application Block
@ECHO ---------------------------------------------------------------------------------
@ECHO.

if Exist Microsoft.Practices.EnterpriseLibrary.Logging.dll installutil %1 Microsoft.Practices.EnterpriseLibrary.Logging.dll
@if errorlevel 1 goto :error

So I'd like to break out whatever counters are needed so I can install them on the webserver, and ideally, be able to script this installation for production code on a production server with an Installer project.

Although I'm strongly tempted to just go back to using my ErrorHandling class, which doesn't need anything installed anywhere.

No, I'll persist in using the EL. I'm sure there will be a payoff -- like extending the Logging class to handle XML.

Of course, to extend the EL one should be cognizant of all the Unit Tests there built to ensure its continued functionality. So I need that book on Test Driven Development real soon now.

Well, at least I'm not bored.

Friday, February 11, 2005

Fun with Microsoft's Enterprise Library

Oh yeah, back to my day-job: I'm a programmer.

So we've got a new application to update the current stone-tablet process in the University that determines if students actually graduate.

Now, I like to write good code, and furthermore, if someone else writes it for me, that's even better. So if you are working on the .NET platform, you'd do well to look at the Enterprise Library.

Of course, this is several thousand lines of code to pore over, good stuff, but unfortunately we have these minor annoying things called deadlines which prevents us from taking the time to grok everything properly.

Come to think of it, this happens in Physics, too -- I never have enough time to actually understand all the details of the problems I'm supposed to be solving, but my advisor assures me that this is the proper state of things in research, as opposed to writing text books, and three guesses as to which one gets you tenure.

Back to the matter at hand -- the Enterprise Library. Looking around at some good working examples , hilarity and pandemonium ensues when you try to do something simple like write to the Event Log when your application barfs. (Did I mention this doesn't come up a lot because instrumenting software seems to be a ... novelty?) I'm pretty sure that developers should not be doing things like editing the Registry, installing Services, or setting accounts programs run under with full admin rights -- I was a system administrator in a previous job, and I hated letting programmers do those kind of things.

So I won't inflict the same damage on our own, long-suffering sysadmin.

Now, my 52-line class solution doesn't do all the bells and whistles the EL does, but it sure doesn't require all the nastiness above:

using System;

using System.Diagnostics;

 

namespace CAESDO

{

    /// <summary>

    /// Methods to handle error reporting

    /// </summary>

    public class ErrorHandler

    {

        public ErrorHandler()

        {

            // Register application as source for Application log

            if (!EventLog.SourceExists("FacultyStudentSurveys"))

                EventLog.CreateEventSource("FacultyStudentSurveys", "Application");

        }

 

        /// <summary>

        /// Writes an error message to the Application Event Log

        /// </summary>

        /// <param name="error">The thrown exception</param>

        internal void WriteToEventLog(Exception error)

        {

            const string source = "Commencement";

            const string logName = "Application";

            EventLogEntryType enumType = EventLogEntryType.Error;

 

            EventLog objectLog = new EventLog(logName);

            objectLog.Source = source;

            objectLog.WriteEntry(error.Message, enumType, 1 );

        }

 

        internal void WriteToEventLog(string message, bool success)

        {

            const string source = "Commencement";

            const string logName = "Application";

            EventLogEntryType enumType;

 

            if (success)

            {

                enumType = EventLogEntryType.Information;

            }

            else

            {

                enumType = EventLogEntryType.Error;

            }

            EventLog objectLog = new EventLog(logName);

            objectLog.Source = source;

            objectLog.WriteEntry(message, enumType);

        }

    }

}



I'm sure I've missed something obvious. Anyone?

I just got a reply from the Hisam Baz, author of the above weblog which says, "Why write 52 lines of code when you can write 1?".

To which I reply, "I'd be happy to write 1 line of code -- if it works."

Which brings us to the second problem:

Non-portable references to the Global Assembly Cache in ASP.NET

Once you actually try to use the Enterprise Library, you'll often come across this bit of advice:

"References

Then from your application, add references to Microsoft.Practices.EnterpriseLibrary.Configuration.dll
and Microsoft.Practices.EnterpriseLibrary.Logging.dll from the C:\Program Files\Microsoft Enterprise Library\bin\ directory. You should consider signing the assemblies and then adding them to the GAC. You should also add a copy of the assemblies to C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE. Once you do that, you can select the assemblies directly from the "Add Reference" dialog. One you've added the reference, then add the appropriate using statement - using Microsoft.Practices.EnterpriseLibrary.Logging - to your code."

There's only one problem -- it doesn't work.

1. When Visual Studio 2003 .NET looks for references in the Global Assembly Cache, it never updates its view of the GAC in response to what you've added -- that's done by the registry (bleah). Which is why you've got to add a copy where VS can find it.

2. When writing ASP.NET applications, references should be against assemblies in the webserver GAC. And naturally, installer projects written in VS 2003 do not install the files in the GAC automatically, as they do for the web application itself. So now you have to manually add assemblies to the GAC and write registry entries for each assembly to be resolved by the .NET runtime.

Wait, why are we using the GAC again?

Okay, looks like I'll have to wade through Richard Grime's Fusion Workshop. Except that it doesn't cover the case I'm interested in. Joy.

Well, I sure hope that the 10 lines of code I'll end up emitting in this exercise will exceed the several thousand I could be writing if I just wrote everything myself.

What's that again about the virtues of programmers? Laziness, impatience, and hubris. Oh, alright then.