I love to dabble in new things, about 3 months ago I started helping a friend fix up their Cold Fusion 7 website. I noticed a bunch of things I consider important missing from the site and was curious about the user demographics/stats (who are they, what browser are they using, what is the max resolution we can use on the site, etc…). After talking a bit, I decided to implement the following items to gather the data:

  1. Implement AWSTAT to monitor the website logs.
  2. Implement a custom internal stat tracker to gather info about users.

Here are the fields we required:

  1. User Name
  2. Browser Type & Version (e.g. IE5, IE6, IE7, FF3, Chrome ???)
  3. Operating System (e.g. Linux, Max, Windows)
  4. Default Language Supported (e.g. en, jp )
  5. Screen Resolution (e.g. monitor resolution 1024×768, 1280×1024, or 800×600)
  6. Screen Colors (e.g. 16bit, 24bit, 32bit)
  7. IP Address
  8. URL (with only the first query string parameter, since he uses FuseBox)
  9. Date
  10. Time

Some of the variables in our list are not available in Cold Fusion, they are browser based data that the DOM has available via JavaScript. To get the data to the server I created a a page called “count.cfm” to accept a query string parameters with the data from the client. This is just JS 101, as far as I know there is no other solution to getting client side information about the user to the server. There are a lot more stats you can obtain from the client, to find them look at the JavaScript “screen” and “navigator” objects . I started to stub out a little extra in my code below (e.g. Navigator.javaEnabled ), if you want to use this data you’ll need to add the logic to add the data in a GET request to “count.cfm”.

    var file = '/fusebox/count.cfm';
    w = screen.width;
    h = screen.height;
    v = navigator.appName;
    j = navigator.javaEnabled();

    if (v != 'Netscape') {
        c = screen.colorDepth;
    }
    else {
        c = screen.pixelDepth;
    }

    info = 'res=' + w + 'x' + h + '&js=' + j + '&col=' + c;

    document.write('<img style="display: none;" src="http://www.zachhunter.net/blog/' + file + '?' + info + '" alt="" />');

After the data is received in the “count.cfm” page, I normalize the data by replace some of the data with foreign keys. This keeps the stat log normalized and decreases the table size.

function LogStats()
{
if(isDefined("session.userid"))
{
    UserId = session.userid;
    UserName = session.username;
    AuthorityLevel = session.authoritylevel;
}
else
{
    UserName = "Anonymous";
    UserId = "";
    AuthorityLevel = "";
}

OS = getOs(CGI.HTTP_USER_AGENT);
BrowserType = getBrowser(CGI.HTTP_USER_AGENT);

if(isDefined("CGI.HTTP_ACCEPT_LANGUAGE"))
{
    Language = CGI.HTTP_ACCEPT_LANGUAGE;

    if(Language.IndexOf(";") GT 0)
    {
        Language = Mid(Source, 1, Source.IndexOf(";"));
    }
}
else
{
    Language = "(Unknown)";
}

IP = CGI.REMOTE_ADDR;
LogDate = DateFormat(Now(),"mm/dd/yyyy");
LogTime = TimeFormat(Now(), "hh:mm");

if(isDefined("CGI.HTTP_REFERER"))
{
    Source = CGI.HTTP_REFERER;

    if(Source.IndexOf("&amp;") GT 0)
    {
        Source = Mid(Source, 1, Source.IndexOf("&amp;"));
    }
}
else
{
    Source = "(Home Page)";
}

if(isDefined("CGI.HTTP_REFERER"))
{
    Referrer = CGI.HTTP_REFERER;
}
else
{
    Referrer = "";
}

if(isDefined("url.res"))
{
    Resolution = url.res;
}
else
{
    Resolution = "(Unknown)";
}

if(isDefined("url.js"))
{
    JavaScriptEnabled = url.js;
}
else
{
    JavaScriptEnabled = "(Unknown)";
}

if(isDefined("url.col"))
{
    Colors = url.col;
}
else
{
    Colors = "(Unknown)";
}

IdOs = GetIdOS(OS);
IdColors = GetIdColors(Colors);
IdRes = GetIdRes(Resolution);
IdBrowser = GetIdBrowser(BrowserType);
IdLang = GetIdLang(Language);
IdPath =  GetIdPath(Source);

Insert = "SET NOCOUNT ON;
          INSERT INTO tbl_Stats_Stats
          (username, [date], [time], ip, osid, colorid, browserid, resid, pathid,languageid)
          VALUES
          ('#UserName#', '#LogDate#', '#LogTime#', '#IP#', #IdOs#, #IdColors#, #IdBrowser#, #IdRes#, #IdPath#, #IdLang#);
          SELECT @@identity as LanguageId from tbl_stats_Languages";
InsertResults = cfquery(dsn="",sqlstring=Insert);
return InsertResults.LanguageId;
}

This probably took about 2 hours to throw together. I’m sure there is more than can be done to optimize the code but this way my first time programming with Cold Fusion. Additionally, since my script is targeted for an intranet application, I didn’t implement parameter validation which is a must for external websites! The largest amount of work was building the logic to parse the USER_AGENT so I could determine the browser and OS. These are very important, since our goal was to determine how we could redesign without negatively impacting the users.

This was deployed by putting the JS into the footer template (footer.cfm). The database script is included in the source code download, along with a data reset script.

Hopefully the logic or code can help you solve your problem.

Cold Fusion Web User Statistics