Enabling log4net logging in SLX 72
Posted By: nicocrm on January 20th, 2008 in Programming, Saleslogix
No Gravatar

The Saleslogix web client comes packaged with a wonderful logging system called log4net. This enables us to receive very detailed information about what is going on in every request, how the data is being read from the database, what business rules or events are being executed, etc. Unfortunately it is all disabled in the default installation. While I hope this changes in the near future to a configuration option, here is how you can turn it on for now. Warning: there is a significant performance impact to logging the debugging information – make sure you tweak this before deploying on production (you can restrict it to log only warning or errors).

  • Edit the web.config file, you need to add a line in the top (after the <configSections> tag) that reads:

    <section name="log4net"
        Version=, Culture=neutral, PublicKeyToken=1b44e1d426115821" />
  • At the end of your web.config file (right before the closing </configuration> tag) you will need to paste the log4net configuration proper. I won’t lie, this is a pretty decent chunk of XML and I usually just carry the same one around. But in truth it is pretty simple: we are defining Appenders which is where you want to send the log output (eg to files, or to the event viewers), Loggers which are settings for where the log comes from (for example you can specify that you only want log messages of a certain severity for a given logger, or decide to send messages from some loggers to emails and not others), and finally a root setting which is simply the default settings for all loggers. These are the settings that I use for development – they configure the logger to show only warnings from NHibernate (you can change that to DEBUG but be warned that there is a lot of info and it does have a significant effect on performance at that level), and all messages from any other part of the system. Everything is sent to a text file under the Log subdirectory:
    <!-- This section contains the log4net configuration settings -->
    <log4net debug="false">
      <appender name="rollingFile" type="log4net.Appender.RollingFileAppender" >
        <param name="File" value="Log/log.txt" />
        <param name="AppendToFile" value="false" />
        <param name="RollingStyle" value="Date" />
        <param name="DatePattern" value="yyyy.MM.dd" />
        <param name="StaticLogFileName" value="true" />
        <layout type="log4net.Layout.PatternLayout">
          <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
      <!-- Setup the root category, add the appenders and set the
         default priority -->
        <priority value="DEBUG" />
        <appender-ref ref="rollingFile"/>
      <logger name="NHibernate">
        <level value="WARN" />
      <logger name="NHibernate.SQL">
        <level value="ALL" />
  • Create a Log directory under the folder and set the permission to be modifiable by Everyone – this is where log4net will send the output.
  • You are almost done, the last thing is you need to create a file called Global.asax in the root of the web client with the following code to initialize the logging system:

    <%@ Application Language="C#" %>
    <script runat="server">
        private static log4net.ILog _log;
        /// <summary>
        /// Initialize web leads library.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>   
        void Application_Start(object sender, EventArgs e)
            _log = log4net.LogManager.GetLogger(typeof(global_asax));
            _log.Info("Application Started");
        void Application_End(object sender, EventArgs e)
            //  Code that runs on application shutdown
            _log.Info("Application Ended");
        /// <summary>
        /// Redirect to generic error page.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>    
        void Application_Error(object sender, EventArgs e)
        void Session_Start(object sender, EventArgs e)
            // Code that runs when a new session is started
        void Session_End(object sender, EventArgs e)
            // Code that runs when a session ends. 
            // Note: The Session_End event is raised only when the sessionstate mode
            // is set to InProc in the Web.config file. If session mode is set to StateServer 
            // or SQLServer, the event is not raised.

That’s it! Browse to the client and make sure the “log.txt” file gets created.

There is one more thing I like to do, add this code in the Page_Load of SmartParts/General/GeneralException.ascx, it will give you a traceback in the log whenever you hit an exception (the ones that are reported with the “Saleslogix has encountered an error” screen, not the ones that give you a Yellow Page Of Death):

if (Page.Request.QueryString["exceptionid"] != null)
  Exception ex = Sage.Platform.Application.ApplicationContext.Current.State[
     Page.Request.QueryString["exceptionid"]] as Exception;
  if (ex != null)
    ExceptionID.Text = ex.Message;
    log4net.LogManager.GetLogger(typeof(GeneralException)).Warn("Exception Reported", ex);

Now in your code, if you want to output some log messages, put code similar to this at the top of your class to declare a logger:

private static readonly log4net.ILog LOG =

and in your code add calls to LOG.Info, LOG.Debug, etc.

See the log4net home page for more information about this excellent tool.

Using Source Control with SLX 7.2
Posted By: nicocrm on January 15th, 2008 in Experiments, Saleslogix
No Gravatar

One of the new possibilities I was pretty excited about with the 7.2 web client is that we can finally start using our standard source control tools (we use Subversion – very simple yet powerful) to manage revisions. It is still not perfect because there are a few tidbits that are hard to get out of the database but in practice I have not found that to be much of a problem (UPDATE – see below). What source control gives us is a nice, easy to use way of:

  • tracking changes between revisions (i.e. what the heck did I break between last Wednesday and today). Also makes it a bit easier to apply the Sage bundle since I can apply the bundle and then use the source control to tell me what changed.
  • archiving your source code (other than backing up the whole database, which can be a bit of a hassle to restore).
  • collaborating on changes between users (i.e. if I am messing with something and Joe is messing with an unrelated feature, we don’t have to worry about temporarily breaking the website, since we are working on separate copies – though obviously we have to watch out when merging the changes back together).
  • it also lets you have several projects against the same database (eg for experimenting with a new feature without having to set up a new database). And when you are done experimenting, you can toss it out, or automatically merge into the production copy. Anyone who has ever used the Bundler will recognize how wonderful that is!

Here is what I had to do to make it happen, like I said it was pretty easy once I found where everything was:

  • In your “Project Workspace Manager” right click and select “Add”:
    Project Workspace Manager
  • Enter a meaningful name: the project workspace manager does not filter the list so you will have to be able to recognize it among all the other projects you might be working on!
  • Enter the path to export, make sure it is on the local drive and not too long: some of the files in AA are pretty long and combined with something like C:\Documents And Settings\domain.user\Documents\Visual Studio 2008 Projects… well it gets past the limit pretty quickly as I found out first-hand.
  • And finally, make sure you check the box to “Export Files Upon Creation”, and click “Create”:
  • Now double click on that shiny new project to open it. Any change you make to the entity model or the quick forms will now be saved to the specified folder instead of into the database. There are still a few things pointed to the database, for example, the Sage.Entity.Interfaces.dll that is generated when you build the project, and the support files (which includes all the assemblies used by the web site). It is possible to get the support files out but not really worth the hassle – first of all it is a pretty big directory, a lot of it is binary files that will just end up cluttering your repository for no practical purpose, and secondly I prefer doing my changes by copying the files to a separate directory and modifying them there, this way I can see exactly what I am working on (I have a build script that automatically deploys them from there to the web site, but that is another story).
  • You can now browse to the folder and import it into your repository using whatever is appropriate… for example with TortoiseSVN I just right click and import.

That’s it! One more thing I have to say if you are not familiar with source control is that it is even more fantastic when it is used with actual source code (as opposed to the XML form used by AA which is pretty verbose and automatically generated thus a bit harder to use in comparisons). This is where it really shines enabling you to see who did what and when and decide how you want to combine it.

UPDATE (2008-01-19): In light of a recent misadventure where I accidentally overwrote the database-stored VFS I would indeed recommend storing the support files into the source control as well. There is about 26 megs of them, and most of it is binary file, and they are not going to change often, but you know what, I’d rather know where they are. To do that, expand the portal (“Sage Saleslogix” or “Sage Saleslogix Customer Portal”), right click on the “Support Files” item and select Copy File. Select an empty target directory. Next, right click, select “Set Source Path”, and point it to your new directory. Check it into source control. There is STILL going to be some stuff saved into the VFS (eg the Sage.Entity.Interfaces file), but we’ll have to let it be for now. And the good side is, the base is going to be the same on all databases, so what I do is store the “Vanilla” 7.2.1 files in a specific path and have all databases reference to that same path. Then, I store the “delta” in a separate folder. I am considering doing that with the model too now (by storing it as a branch in subversion) but not quite there yet.

UPDATE (2008-03-17): On Saleslogix 7.2.2 you may get an error when compiling the platform, something that looks like “Unable to read snippet file, ‘\Entity Model\SalesLogix Application Entities\LeadAddress\.svn\text-base\Sage.SnippetLibrary.CSharp.@.07ffd774-6142-4b8a-a48a-09616effccea.codesnippet.cs.svn-base’. Unknown extension. Supported extensions are: .cs, .vb“. Sage tries to compile every file that contains “codesnippet” in its name. A small update to the Sage.Platform.dll is necessary to get past this – I may write more fully on the subject of how to patch the Sage libraries later but you can download the patched dll here (copy the dll to your C:\Program Files\Saleslogix\Architect\Platform folder) or the patched source code here (compile it with ilasm.exe).

Happy New Year
Posted By: nicocrm on January 7th, 2008 in Uncategorized
No Gravatar

Here are a few new year resolutions (on the technical side, anyway), just so I can laugh at them next year:

  • Learn Django – I need a breath of fresh air from the ASP.NET sometimes, I settled on Django after hesitating between that ASP.NET MVC (too close to home… I prefer something not running on .NET for a change), Ruby on Rails (bad experience with Ruby in the past… don’t ask), and a few more esoteric ones like Seaside or YAWS
  • Learn F#, or maybe Haskell? Something that is different enough from C# to give me a different perspective. I am worried that F# is probably still very close since it runs on the CLR but it might be hard to find a practical use for something like Haskell or Lisp
  • Learn more about the CLR – I picked up the book CLR via C# and intend to give it a thorough reading

Obviously SLX 7.2, ASP.NET and MS Ajax will have a place there too but that is pretty much a given even without a resolution!

On the non-technical side I would like to resolve NOT to adopt another dog this year but that might be a tough one (gee I hope not, 4 is more than enough!!)