    // Create the root namespace as an object if it doesn't already exist (i.e. from another script include)
    var SDL = SDL || {};

    // Add a logging namespace with all the logging functions
    SDL.Logging = new function ()
    {
        // Valid values for message types
        this.msgTypeError = 1;
        this.msgTypeWarning = 2;
        this.msgTypeInfo = 4;
        this.msgTypeAuditSuccess = 8;
        this.msgTypeAuditFail = 16;

        // Constants used for logging the messages
        this.scriptErrorCode = 599;     // Error Status code, wanted distinct code for Script Errors, HttpCodes use 500s for errors
        this.loggingUrl = 'http://api.sdl.com/Log/Report.asmx/LogMessage?';


        /**
        * Function to return the flash plugin version number for the current browser
        * @return string containing flash version (e.g. 10.1.53)
        */
        function getFlashVersion()
        {
            var flashVersion = '';

            // For IE we need to create an ActiveX object
            try
            {
                flashVersion = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
            }
            catch (errorObjectIE) { }

            // For other browsers we can access the plugins collection
            try
            {
                if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin)
                {
                    flashVersion = navigator.plugins["Shockwave Flash"].description;
                }
            }
            catch (errorObject) { }

            // Replace anything that is not a digit with a dot (i.e. convert "Version 10, release 1 build 75" into "10.1.75"
            flashVersion = flashVersion.replace(/\D+/g, ".").substring(1);

            return flashVersion;
        }

        /**
        * Standard object for messages. The property names correspond to the web service parameters
        * @param {string} msg			Brief text of the error message
        * @param {string} detail		Full detail of the message (e.g. including a call stack) 
        * @param {string} msgData		Any system data to accompany the message
        * @param {string} customData	Any custom data set in the calling code
        * @param {int} status		    The status code to report
        * @param {int} messageType	    One of the above message types (Error, Warning etc.)
        */
        this.messageDetails = function (msg, detail, msgData, customData, status, messageType)
        {
            this.Message = msg;
            this.MessageDetail = detail;
            this.MessageData = "Flash: " + getFlashVersion();
            if (navigator.browserLanguage)
            {
                this.MessageData += "\r\nLanguage: " + navigator.browserLanguage;
            }
            if (navigator.language)
            {
                this.MessageData += "\r\nLanguage: " + navigator.language;
            }
            if (msgData !== '')
            {
                this.MessageData += "\r\n" + msgData;
            }
            this.Site = location.host;
            this.Url = location.href;

            // Browser will be set automatically from Request
            this.Browser = '';
            if (typeof (customData) === 'undefined')
            {
                this.CustomData = '';
            }
            else
            {
                this.CustomData = customData;
            }
            this.Status = status;
            this.MessageType = messageType;

            return this;
        };

        /**
        * Function to return a message details object from a try/catch exception object
        * @param {Object} ex			The exception object
        * @param {string} customData	Any additional data to be reported
        */
        this.exceptionMessage = function (ex, customData)
        {
            var messageData = '', messageDetail = '', statusCode = this.scriptErrorCode, exProp;

            if (typeof (ex.lineNumber) !== 'undefined')
            {
                messageData = 'Line: ' + ex.lineNumber;
            }

            if (typeof (ex.stack) !== 'undefined')
            {
                messageDetail = ex.stack;
            }

            if (typeof (ex.Number) !== 'undefined')
            {
                statusCode = ex.Number;
            }

            for (exProp in ex)
            {
                if (ex.hasOwnProperty(exProp))
                {
                    messageData += exProp + ': ' + ex[exProp] + '\r\n';
                }
            }

            return new this.messageDetails(ex.message, messageDetail, messageData, customData, statusCode, this.msgTypeError);
        };


        /**
        * Function to return a message details object based on the parameters from a window.onerror event
        * @param {string} msg			The error message
        * @param {string} url			The url the error occurred on
        * @param {int} line			The line number of the page the error occurred on 
        * @param {string} [customData]	Any additional data that should be reported with the error
        */
        this.errorMessage = function (msg, url, line, customData)
        {
            var callerText = '', caller = arguments.callee.caller, nameRegEx = new RegExp(/\s*function\s+([\S]+)\s*\(/), fullMatch = false,
                functionText, internalMatch, errorMethod, propName,
                loadScriptRegEx = new RegExp(/\("Error loading script","(http(s)?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)"/);

            // IE allows access to the caller to determine the stack trace
            if (caller && navigator.userAgent.indexOf('MSIE') > 0)
            {
                // This regex will extract the name of a function from a block of code
                while (caller !== null)
                {
                    // The caller is the full definition of the functions
                    //  Need to extract the name from it
                    functionText = caller.toString();

                    if (functionText.match(nameRegEx))
                    {
                        callerText += RegExp.$1 + '\r\n';
                        fullMatch = true;
                    }
                    else
                    {
                        // Come in here if we cannot find a function definition using the regex
                        //  The call may still match one of the function definitions in this file
                        //  These are of a different format this.functionname = function(...
                        //  Check each definition in "this" to see if it matches the caller
                        internalMatch = false;
                        if (!fullMatch)
                        {
                            for (errorMethod in this)
                            {
                                if (this[errorMethod] === functionText)
                                {
                                    callerText += errorMethod + '\r\n';
                                    internalMatch = true;
                                }
                            }
                        }
                        if (!internalMatch)
                        {
                            callerText += 'anonymous\r\n';
                        }
                    }

                    // Go higher up the caller stack
                    caller = caller.caller;
                }

            }
            else if (msg.toString() !== '[object Event]')
            {
                // Force an error via try/catch to get a call stack
                try
                {
                    this.will.fail += 0;
                }
                catch (errorObject)
                {
                    // Stack trace for FireFox
                    if (errorObject.stack)
                    {
                        callerText = errorObject.stack;
                        if (errorObject.stack.indexOf('\n') > 0 && errorObject.stack.indexOf('\r') === 0)
                        {
                            callerText = errorObject.replace('\n', '\r\n');
                        }

                        // For script loading errors, attempt to extract the script file from the stack
                        if (loadScriptRegEx.test(callerText))
                        {
                            msg = msg + ' ' + callerText.match(loadScriptRegEx)[1];
                        }
                    }
                }
            }
            else
            {
                // Unknown caller!! Check for properties on the msg
                callerText = '';
                if (msg.target)
                {
                    for (propName in msg.target)
                    {
                        if (propName !== 'sessionStorage' && msg.target.hasOwnProperty(propName))
                        {
                            if (typeof (msg.target[propName]) !== 'function' && msg.target[propName] !== '' && msg.target[propName] !== 0)
                            {
                                callerText += propName + ' = ' + msg.target[propName] + '\r\n';
                            }
                        }
                    }
                }
            }

            return new this.messageDetails(msg, callerText, 'Line: ' + line, customData, this.scriptErrorCode, this.msgTypeError);
        };

        /**
        * Standard handler to log an error message for window.onerror
        * @param {String} msg       The msg for the error (provided by the onerror event) 
        * @param {String} url	    The url for the error (provided by the onerror event)
        * @param {int} line	        The line of the error (provided by the onerror event)
        * @param {string} [customData]	Developer added custom data to be reported
        * @return {Object} messageDetails object to be used for reporting the error to the user
        */
        this.logError = function (msg, url, line, customData)
        {
            var errorObj = this.errorMessage(msg, url, line, customData);
            this.logMessage(errorObj);
            return errorObj;
        };

        /** 
        * Standard handler to log a try/catch exception
        * @param {Object} ex				The exception object
        * @param {string} [customData]		Any custom data that should also be reported
        * @return {Object} messageDetails object to be used for reporting the error to the user
        */
        this.logException = function (ex, customData)
        {
            var errorObj = this.exceptionMessage(ex, customData);
            this.logMessage(errorObj);
            return errorObj;

        };

        /**
        * Log the message by creating a new script element that uses the logging Url
        * This allows the javascript to send data across domains
        * The sent data is all relevant to the error message
        * There is no data received 
        * @param {Object} messageObj	A message details object containing all the information about the error
        */
        this.logMessage = function (messageObj)
        {
            var logScript, postData = '', propName;

            // Call the SDL logging API, this does not return anything but logs centrally
            if (location.protocol !== 'file:')
            {
                logScript = document.createElement('script');
                logScript.type = "text/javascript";
                for (propName in messageObj)
                {
                    if (messageObj.hasOwnProperty(propName))
                    {
                        postData += propName + '=' + encodeURIComponent(messageObj[propName]) + '&';
                    }
                }
                logScript.src = this.loggingUrl + postData;
                logScript = document.getElementsByTagName('head')[0].appendChild(logScript);
            }
        };

    };

