Archive for January 2012

Drink Recipe – Martinez

It took me a couple of tries to get into this drink; the Ransom Old Tom gin takes a little bit of getting used to at first. This has turned into one of my favorites.  There’s a theme here: I love gin cocktails.

Here’s the recipe:

  • 1.5oz Ransom Old Tom gin
  • 0.5oz Luxardo Maraschino liqeur
  • 0.75oz sweet vermouth
  • Dash of orange bitters
  • Build the ingredients in an ice-filled glass
  • Stir with a bar spoon
  • Strain into a cocktail glass
Note: the recipe called for Carpano Antica Formula sweet vermouth, otherwise known as the best sweet vermouth ever. The problem is it only comes in a 1 liter bottle which tends to go flat before you use it all … I wish they made a smaller bottle!



Martinez

SharePoint – Configuring log4net in a SharePoint Application

Cross-posted from my Clarity blog

Configuring log4net in a SharePoint or ASP.NET application is pretty straightforward, but no matter how many times I do it, I always forget something small and waste time troubleshooting why logging isn’t working.

Here’s a guide to configuring logging using log4net in a SharePoint application. This post will cover:

  • Creating and deploying a log4net configuration file
  • Making the necessary web.config changes to support logging
log4net deployment is documented extensively so I’ll keep this at a high level.

Creating and Deploying a log4net Configuration File

It’s always best to put your log4net configuration in a dedicated config file, this way you get to keep your SharePoint web.config file clean and not have to deal with the SPWebConfigModification class.

The part I always forget is that when you have the log4net configuration in a separate file, you need to tell your assembly the name of the log4net configuration file. You do that by adding the following attribute to the AssemblyInfo class of the SharePoint project or class library where you have your logger.

According to the documentation from Apache, this attribute is read and processed after the first time you invoke log4net. Your logger will call LogManager.GetLogger and look for this attribute and then read the configuration information.

[assembly: log4net.Config.XmlConfigurator(
 ConfigFile = "log4Net.config", Watch = true)]

You can examine properties of the logger such as isDebugEnabled and isErrorEnabled to check if everything is working properly.

You can then use a SharePoint Timer job to deploy the log4net.config file to the root of your SharePoint web application.

In a Web Application scoped feature, you can run a SharePoint Timer job in the FeactureActivated handler:

var log4NetDeployJob = new log4NetConfigDeploymentJob(
    _jobName, webApplication);
var schedule = new SPOneTimeSchedule(DateTime.Now);
log4NetDeployJob.Schedule = schedule;
log4NetDeployJob.Update();
webApplication.JobDefinitions.Add(log4NetDeployJob);
webApplication.Update();
log4NetDeployJob.RunNow();

The Execute method of the Timer Job extracts the log4net.config file from where it was deployed with your feature and copies it to the virtual directory:

public override void Execute(Guid targetInstanceId)
{
    var webApp = this.Parent as SPWebApplication;
    foreach (KeyValuePair<SPUrlZone, SPIisSettings> setting in webApp.IisSettings)
    {
        var deployTo = setting.Value.Path.FullName + @"\log4Net.config";
        var filePath = SPUtility.GetGenericSetupPath(
            @"TEMPLATE\FEATURES\log4Net\Configuration\log4Net.config");
        File.Copy(filePath, deployTo, true);
    }

    base.Execute(targetInstanceId);
}

SPUtility.GetGenericSetupPath returns the full path to the 14 hive, and “TEMPLATE\FEATURES\log4Net\Configuration\log4Net.config” is the path to the log4Net.config file inside the feature.

Web.config Modifications

You now need to make some changes to the web application’s web.config file.

Add log4Net configuration section in configuration/configSections:

<section name="log4net"
  type="log4net.Config.Log4NetConfigurationSectionHandler, Log4net"/>

Add the following before the end of the configuration section:

<log4net configSource="log4Net.config" />

That’s it! Assuming you created a logger class in your application, you can start logging away. Check out the documentation from Apache for information about how to write a logger.

Drink Recipe – Corpse Reviver #2

I had this drink for the first time at The Drawing Room in Chicago and was instantly hooked.

The Corpse Reviver #2 is historically a breakfast drink (ha!). I have to say that I did have it with breakfast at Big Jones and it was pretty awesome …

Here’s the recipe:

  • 1 3/4oz New Holland Knickerbocker gin (I like to up the gin slightly)
  • 1oz Cointreau
  • 1oz Lillet Blanc
  • 1oz fresh squeezed lemon juice
  • A few drops of Lucid absinthe
  • Shake with ice
  • Double-strain into a cocktail glass
Enjoy!

Corpse Reviver #2

Swimming – World’s Smoothest Freestyle?

This is pretty amazing to watch …

SharePoint – Timing of Master Page JavaScript

Our base SharePoint 2010 Publishing master page calls a JavaScript function that does things such as:

  • Initialize jQuery plugins
  • Configure some top-navigation tweaks
  • Miscellaneous items such as setting the date in the copyright footer
  • Initialize Google Analytics

We call this function right before the end of the head section of the master page. The contents of the JavaScript function are enclosed in a jQuery $(document).ready block, meaning that the function will run after the DOM is ready.

A drawback to this approach is that JavaScript that manipulate elements on the screen only executes after the DOM is ready. This isn’t a great user experience because the user can see the “jumpiness” on screen as the script executes.

I ran this by our resident web ninja Jacob Gable and he suggested breaking up the JavaScript function into smaller chunks and calling those chunks directly after the HTML content that they affect; for example:

<div id="footer">
    <span id="footer-year"></span>&nbsp;All rights reserved.
    <script type="text/javascript">
        SetCopyrightDate();
    </script>
</div>

where SetCopyrightDate uses jQuery to set the date to the current year:

function SetCopyrightDate() 
{
    var today = new Date();
    var yr = today.getFullYear();
    $("#footer-year").text("© " + yr);
}

This is pretty obvious in retrospect; your JavaScript can be sprinkled throughout the page as needed. People are scared away by this, wanting to have a super-clean master page.

However, in this case, pulling out some JavaScript functionality into separate functions and calling them from the master page does the trick.

The remaining JavaScript can stay in a global function and run in a $(document).ready block; for us, all this does now is initialize jQuery plugins we’re using throughout the site.

Hello world!

I’ve had this domain for as long as I can remember but there’s never been anything on it. I’ve actually never even had a personal website, so I figure it’s about time I put some effort into building my own brand on the internet.

I plan to use this blog to post about interesting things I’m doing at work, and personal interests such as swimming, food, and mixology.

Welcome, and stay tuned!