Thu, 15 Apr 2010

Printf(“HERE”) in the Cloud

I don’t know about you, but when things are going wrong in my code, I like to put little printf() statements (or Console.WriteLine or Response.Write or whatever) to let me know what’s going on.

Windows Azure Diagnostics gives us a way to upload trace messages, log files, performance counters, and more to Windows Azure storage. That’s a good way to monitor and debug issues in your production cloud application.

However, this method has some drawbacks as compared to printf("HERE") for rapid development and iteration. It’s asynchronous, it’s complex enough that it might be the source of an error (like incorrect settings or a bad connection string), and it’s no good for errors that cause your role instance to reset before the messages are transferred to storage.

Another option is the Service Bus sample trace listener, which Ryan covered in Cloud Cover Episode 6. This is more immediate than Windows Azure Diagnostics, but it still feels a bit too cumbersome and doesn’t persist messages (so only useful if you’re running the client while the error happens).

A Lower-Tech Solution

Recently, I’ve been using the following one-liner to record errors that I’m trying to track down in my Windows Azure application (spread out across multiple lines here for space reasons):


catch (Exception e) {
    CloudStorageAccount.Parse("<your connection string here>").CreateCloudBlobClient()
        .GetBlobReference("error/error.txt").UploadText(e.ToString()); }

Basically, it puts whatever exception I caught into a blob called error.txt in a container called error. Pretty handy.

Here’s a more complete and robust example:

try
{
    throw new Exception("Here's an exception.");
}
catch (Exception e)
{
    var container = CloudStorageAccount.Parse("<your connection string here>")
        .CreateCloudBlobClient().GetContainerReference("errors");
    container.CreateIfNotExist();
    container.GetBlobReference(string.Format("error-{0}-{1}",
        RoleEnvironment.CurrentRoleInstance.Id, DateTime.UtcNow.Ticks)).UploadText(e.ToString());
    throw;
}

This version of the code creates the container as needed and includes the instance ID and a timestamp in the blob name, so you don’t overwrite older messages. It also rethrows the exception instead of swallowing it.

Only Try This At Home

Note that I’m suggesting hardcoding your storage credentials, the name of the container, etc. That may all strike you as bad coding practices, and I agree. That’s because this is not for production use. This is just for my debugging purposes, like my good ol’ printf("HERE") statements.