docs: function standardLogAppender(level, exception, message, data) {
    // Attempt to get the Error stack if it isn't passed in.  Without the stack
    // the traceback is not as useful.  For more information on Error stack see:
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/stack
    var stack        = exception ? exception.stack : new Error().stack || null,
      scriptURL      =  Canadarm.constant.UNKNOWN_LOG,
      errorMessage   = message || exception.message || Canadarm.constant.UNKNOWN_LOG,
      // Want an ISO string because local time sent as a log is not helpful
      currentDate    = new Date(),
      pageURI        = window.location.href,
      lineNumber     = Canadarm.constant.UNKNOWN_LOG,
      columnNumber   = Canadarm.constant.UNKNOWN_LOG,
      language       = window.navigator.language || Canadarm.constant.UNKNOWN_LOG,
      characterSet   = window.document.characterSet ||
                      window.document.charset ||
                      window.document.defaultCharset ||
                      Canadarm.constant.UNKNOWN_LOG,
      logAttributes,
      dataKey,
      stackData,
      dateTime,
  
      // first position is the URL of the script,
      // second position is the line number,
      // third position is the column number,
      // last position is used to gobble down so we can get the data in all browsers.
      STACK_SCRIPT_COLUMN_LINE_FINDER = /(http\:\/\/.*\/.*\.js)\:(\d+)\:(\d+)(.*)$/;
  
    // Generates the url, lineNumber, and Column number of the error.
    function findStackData(stack) {
      // If the stack is not in the error we cannot get any information and
      // should return immediately.
      if (stack === undefined || stack === null) {
        return {
          'url': Canadarm.constant.UNKNOWN_LOG,
          'lineNumber': Canadarm.constant.UNKNOWN_LOG,
          'columnNumber': Canadarm.constant.UNKNOWN_LOG
        };
      }
  
      // Remove the newlines from all browsers so we can regex this easier
      var stackBits = stack.replace(/(\r\n|\n|\r)/gm,'').match(STACK_SCRIPT_COLUMN_LINE_FINDER),
        newStack, stackData = [],
        stackHasBits = (stackBits !== null && stackBits !== undefined);
  
      while (stackHasBits) {
        stackBits = stackBits[1].match(STACK_SCRIPT_COLUMN_LINE_FINDER);
        newStack = stackBits !== null ? stackBits[1] : null;
        stackData = stackBits !== null ? stackBits : stackData;
        stackHasBits = (stackBits !== null && stackBits !== undefined);
      }
  
      return {
        'url': stackData.length >= 1 ? stackData[1] : Canadarm.constant.UNKNOWN_LOG,
        'lineNumber': stackData.length >= 1 ? stackData[2] : Canadarm.constant.UNKNOWN_LOG,
        'columnNumber': stackData.length >= 1 ? stackData[3] : Canadarm.constant.UNKNOWN_LOG
      };
    }
  
    // Use an internal polyfill for Date.toISOString
    // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString#Polyfill
    // Not attaching it to prototype so we do not add implementation to consumers.
    function pad(number) {
      if (number < 10) {
        return '0' + number;
      }
      return number;
    }
  
    if (!currentDate.toISOString) {
      dateTime = (function() {
        return currentDate.getUTCFullYear() +
          '-' + pad(currentDate.getUTCMonth() + 1) +
          '-' + pad(currentDate.getUTCDate()) +
          'T' + pad(currentDate.getUTCHours()) +
          ':' + pad(currentDate.getUTCMinutes()) +
          ':' + pad(currentDate.getUTCSeconds()) +
          '.' + (currentDate.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) +
          'Z';
      }());
    } else {
      dateTime = currentDate.toISOString();
    }
  
    // If stack is not defined we are in a browser that does not fully support our logging.
    // Or one of our custom loggers was called, e.g. Logger.debug('message').
    if (exception === undefined || exception === null || exception.stack === null) {
      if (typeof window.document.getElementsByTagName === 'function') {
        var scripts = window.document.getElementsByTagName('script');
        scriptURL = (window.document.currentScript || scripts[scripts.length - 1]).src;
      } else {
        // Probably not in a browser, return unknown log.
        scriptURL = Canadarm.constant.UNKNOWN_LOG;
      }
  
    } else {
      stackData = findStackData(stack);
  
      scriptURL = stackData.url;
      lineNumber = stackData.lineNumber;
      columnNumber = stackData.columnNumber;
  
    }
  
    // Set base values for log attributes.
    logAttributes = {
      'characterSet' : characterSet,
      'columnNumber' : columnNumber,
      'language'     : language,
      'lineNumber'   : lineNumber,
      'logDate'      : dateTime,
      'msg'          : '[' + level + ']: ' + errorMessage,
      'pageURL'      : pageURI,
      'stack'        : stack || Canadarm.constant.UNKNOWN_LOG,
      'type'         : 'jserror',
      'scriptURL'    : scriptURL
    };
  
    // Gather the data and add it to our standard logger.
    for (dataKey in data) {
      if (!data.hasOwnProperty(dataKey)){
        continue;
      }
      // Only set value for legitimate data.
      if (data[dataKey] !== null && data[dataKey] !== undefined) {
        logAttributes[dataKey] = data[dataKey];
      }
    }
  
    return logAttributes;
  }