Thursday, December 31, 2009

Scary Framework

I don't really want to bag on another logging framework, but I've come across many posts that are downright scary. Here's an example of a situation with one of the most popular logging frameworks. Read down a bit and you'll hear about deadlocks that happened every day--until they removed that logging framework.

I've also read about other serious deadlock issues with that framework. But posting more links would be just piling on.

Try this one for yourself (if you're masochistic enough to be using that other framework). Delete the log file while your application is running. Oops. You can't do that, can you? Because the file is locked. I sure hope you don't have another application writing to the same file--or another logger in the same application.

For me, I think I'll stick with a framework that doesn't require a Ph.D. to configure--and one that is thread-safe, and yet I can be sure won't put my application into any deadlock situations. Like this one.

Exceptional Gotchas!

Yes, the pun was intended.

Many ASP.NET developers think that having code in the Application_Error method in Global.asax.cs is adequate for dealing with any unhandled exceptions within their web application. While it usually is adequate--for most basic web applications, there are particular cases where this simply won't catch all errors.

Specifically, if you have code running on the server that is in a thread pool, or you've spawned a separate thread, or you have callback code for a timer, if an unhandled exception occurs, you just might be out of luck.

You can easily test this for yourself by creating a web page with two buttons, one that throws an exception when you click it, and the other that uses a thread from the thread pool and throws an exception within it. Something like this:

public partial class _Default : System.Web.UI.Page
    {
        private void ThrowException()
        {
            throw new ApplicationException("Ouch!");
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            ThrowException();
        }

        protected void Button2_Click(object sender, EventArgs e)
        {
            System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback(DoWork));
        }

        private void DoWork(object dummy)
        {
            ThrowException();
        }
    }

In addition to the HttpUnhandledException that gets sent to the Application_Error method, you need to handle the UnhandledException event on the AppDomain.

You can set up a handler in the Application_Start method in Global.asax.cs. But if you go a step further, you can create something a little more reusable. Here's an HttpModule that will demonstrate handling both kinds of web exeptions--those occurring during a normal web request, and those occurring on separate threads. Perhaps it should be part of the Logging Framework.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using BitFactory.Logging;

namespace WebAppA
{
    public class MyExceptionModule : IHttpModule
    {
        private HttpServerUtility Server { get; set; }

        #region IHttpModule Members

        public void Dispose()
        {            
        }

        public void Init(HttpApplication context)
        {
            // deal with any non-main thread exceptions
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

            // deal with any HttpUnhandledExceptions 
            context.Error += new EventHandler(context_Error);

            Server = context.Server;
        }

        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            // If we are here, the process is about to die
            ConfigLogger.Instance.LogFatal(((Exception)e.ExceptionObject).Message);
        }

        void context_Error(object sender, EventArgs e)
        {
            // The outer exception is usually an HttpUnhandledException, so use the InnerException if it exists.
            // Even more detail can be retrieved by iterateing over each InnerException, if desired.
            Exception ex = Server.GetLastError();
            ex = ex.InnerException ?? ex;
            ConfigLogger.Instance.LogError(ex.Message);
        }

        #endregion
    }
}


Modify the above to suit your needs. In addition, the following would go into your web.config file.

<httpModules>
  <add name="MyExceptionModule" type="WebAppA.MyExceptionModule, WebAppA" />
</httpModules>


You might consider putting something like this into a separate utility assembly that you can reuse in all your web projects.

Tuesday, December 29, 2009

The Overabundance of Logging Frameworks

An interesting question in a tweet caught my eye, and I thought I would attempt to provide an answer. To paraphrase, why are there so many logging frameworks for .NET, especially when there are perfectly capable facilities built-in?

Ill start off by saying that I think there are probably more logging frameworks than are necessary, simply because some have come along more recently which add very little value to what already exists. However, the simple fact is that not every logging solution is going to feel right for everyone. People have very different needs--and different values.

Different values?

Yes, such as: flexibility vs. simplicity, or configurability vs. maintainability, etc.

With any tool you choose, typically you'll try to pick one that maximizes the benefits (that are important to you) and minimize the disadvantages.

I happen to think my logging framework maximizes the benefits most people look for in a logging framework, with very little downside. Incidentally, a lot of people agree. Here is just one example.

Watch this short video that demos my logging framework and tell me what you think.

Sunday, December 27, 2009

Making Sandwiches

There are far too many managers who think every software developer is a round peg--a "resource"--who can simply be replaced by another.

Ever been served by a worker at a Mexican fast food restaurant who didn't know how to roll a burrito? How about a sandwich shop where the person behind the counter couldn't slice the foot-long sub without tearing it up?

Okay, it isn't the best analogy. And maybe your experience has been better than mine during lunch hour. But the real point is that software development isn't like making sandwiches. And if you can't even count on consistency in sandwich-making by different people, why would you expect something different in the software development process?

Friday, December 25, 2009

A Simple Twitter Logger

[UPDATE! Look here for the OAuth version.]



Yes, using The Object Guy's Logging Framework it is incredibly easy to create a logger that will write to Twitter.

Here's a sample:

public class TwitterLogger : Logger
{  
  protected override bool DoLog(LogEntry aLogEntry)
  {
    // without this, you may receive a '417 Expectation Failed' error
    ServicePointManager.Expect100Continue = false;

    using (WebClient webClient = new WebClient())
    {
       webClient.Credentials = new NetworkCredential("[YOUR USER NAME]", "[YOUR PASSWORD]");

       var nvc = new NameValueCollection();
       nvc["status"] = aLogEntry.Message;

       webClient.UploadValues("http://twitter.com/statuses/update.xml", nvc);
    }
    return true;
  }
}

Remember that Twitter may limit your status updates to 150 per hour and a thousand or so per day.

Thursday, December 24, 2009

Session with Style

Sloppy code, unfortunately, is ubiquitous. Sometimes it's due to laziness; other times to ignorance, as in: "it never even occurred to me to do it otherwise." I'd put the example I use in this post as one of the latter for most people.

So you want to store something in Session? Easy.

Session["MyString"] = "this is a string I'm storing in session."

Then, somewhere else in your application you do:

MyLabel.Text = (string)Session["MyString"]; 

Maybe "sloppy" is too strong a word. But it certainly isn't slick--for a number of reasons: casting, remembering keys, key typos, overwriting existing keys, etc.

An incrementally better solution might be to use an enumeration or constants for the keys. A step beyond that would be to put all of your Session-related information into a single class and store that in Session.

Let's bump it up a level. Let's take this to 11. Here's a much better way.

Start with a base class you can reuse in any of your ASP.NET applications.

using System;
using System.Web;

namespace MyNamespace
{
    public class SessionInfo<Subclass>
        where Subclass : SessionInfo<Subclass>, new()
    {
        private static string Key
        {
            get { return typeof(SessionInfo<Subclass>).FullName; }
        }

        private static Subclass Value
        {
            get { return (Subclass) HttpContext.Current.Session[Key]; }
            set { HttpContext.Current.Session[Key] = value; }
        }

        public static Subclass Current
        {
            get
            {
                var instance = Value;
                if (instance == null)
                    lock (typeof(Subclass)) // not ideal to lock on a type -- but it'll work
                    {
                        // standard lock double-check
                        instance = Value;
                        if (instance == null)
                            Value = instance = new Subclass();
                    }
                return instance;
            }
        }
    }
}

Now, create a class specific to your current application.

public class MySessionInfo : SessionInfo<MySessionInfo>
{
   public string SomeString { get; set; }
}

To use it in your application, simply do:

// set the value
MySessionInfo.Current.SomeString = "this is a string I'm storing in session.";

// get the value
MyLabel.Text = MySessionInfo.Current.SomeString;

Of course, you can put more than just strings into your subclass. You can create separate subclasses for use in unrelated parts of your application, or you can create a single class (if the design calls for it) to store everything you'll need to have in Session. Perhaps something like:

public class MySessionInfo : SessionInfo<MySessionInfo>
{
   public WebSiteMember WebSiteMember { get; set; }
   public ShoppingCart ShoppingCart { get; set; }
   public BrowseHistory BrowseHistory { get; set; }
}

Just remember that if you'll be using anything other than in-process session state, you'll have to make your class serializable.