Customer

United Healthcare

Description

Here is an example of how this Client is getting more UX insights with Germain that monitors its SalesForce.com Users, 24x7.

This client has built a custom applet on SalesForce.com (Summer 21 release), to allow SFDC users to do a “quick search”. they needed specific insights from that applet, which they are now getting 24x7 and in near real-time.

Custom Quick Search applet:

Germain dashboard reporting realtime UX insights about this Quick Search applet:
(These are not production data. these are data coming from a testing environment)

Note

Similar benefits can be provide to other apps, this is not specific to SalesForce.com.

Configuration

These lines were added to germainapm-salesforce-init.js:

/* global settings */
// typeof settings = { agent: { servicesUrl; monitoringProfileName; appName; serverHost; }; profile: UxMonitoringProfile; plugins; constants; application; }

settings.plugins = {
RequestMonitoring: {
enabled: settings.profile.networkRequestsMonitoring,
queryStringGenerator: xhrQueryStringGenerator,
taggingEnabled: false,
headerProcessor: GermainAPMSalesforceUtils.headerProcessor
},
ChangeMonitoring: {
enabled: true,
eventInit: "page_ready"
},
ClickMonitoring: {
enabled: true,
fullMonitoringEnabled: true,
frameMonitoringEnabled: false,
clickContainerLookup: GermainAPMSalesforceUtils.clickContainerLookup,
additionalInfoLookup: clickAdditionalInfoLookup,
eventInit: "page_ready"
},
ConsoleMonitoring: {
enabled: true,
errorEventListenerEnabled: false,
consoleMethods: ['log', 'error', 'warn'],
additionalInfoLookup: GermainAPMSalesforceUtils.consoleAdditionalInfoLookup
},
CpuMonitoring: { enabled: false, repeatSeconds: 60, samplesAveragedPerRound: 12, sampleTimeMillis: 2000, eventInit: "page_ready" },
DomMonitoring: {
enabled: settings.profile.sessionReplayMonitoring, compressContent: false,
pushInterval: 10, pushFullInterval: 300, dataTimeout: 30000,
excludeAriaAttributes: true /,excludeAttributes:[],excludeIds:[]/
},
FocusMonitoring: { enabled: settings.profile.sessionReplayMonitoring, pushInterval: 15, eventInit: "page_ready" },
IframeMonitoring: { enabled: settings.profile.networkRequestsMonitoring, eventInit: "page_ready" },
InactivityMonitoring: { enabled: true, eventInit: "page_ready", threshold: 30 },
InputMonitoring: { enabled: settings.profile.sessionReplayMonitoring },
KeyboardMonitoring: { enabled: true, eventInit: "page_ready" },
MediaPlayback: { enabled: settings.profile.sessionReplayMonitoring },
MemoryMonitoring: { enabled: true, eventInit: "page_ready" },
MouseMonitoring: {
enabled: true, frameMonitoringEnabled: true, snapshotInterval: 100,
pushInterval: 15, eventInit: "page_ready"
},
HoverStyles: { enabled: settings.profile.sessionReplayMonitoring },
PopupDialogMonitoring: { enabled: true },
RenderingMonitoring: { enabled: settings.profile.renderingTimeMonitoring },
MediaStateMonitoring: { enabled: true },
RT: { enabled: true },
ScriptingMonitoring: { enabled: settings.profile.scriptTimeMonitoring },
DebuggerMonitoring: {
enabled: false,
profilePageLoad: true,
profilerMaxSeconds: 30,
chrome: { // Requires the Germain Chrome extension
useWebSocket: true, // True requires Chrome to be launched with remote-debugging-port arg. False causes a warning banner in the UI.
webSocketPort: 9922, // chrome.exe --remote-debugging-port=9922
profilerSamplingInterval: 20 // milliseconds
}
},
ScrollMonitoring: { enabled: true, snapshotInterval: 1000, pushInterval: 15, eventInit: "page_ready" },
StaticResourcesMonitoring: { enabled: settings.profile.sessionReplayMonitoring, eventInit: "page_ready" },
VisibilityMonitoring: { enabled: true, eventInit: "page_ready" },
HangMonitoring: { enabled: true, pingInterval: 10, minHangSeconds: 15 },
ContentIndex: { enabled: true, includeVisibleText: true, includeInputFields: true }
};

settings.constants = {
CORS_PROXY_URL: null,
DATA_QUEUE_PUSH_INTERVAL: 10,
USE_WEB_WORKER: false,
DATA_TIMEOUT: 10000, // how long we can try to send collect data back (in ms)
EVENT_SOURCE_ELEMENT_LOOKUP: GermainAPMSalesforceUtils.eventSourceElementLookup,
PAGE_TITLE: GermainAPMSalesforceUtils.titleLookup, // extract request title
RESPONSE_BODY_MONITORING: true, // catch response body
RESPONSE_BODY_PARSER: GermainAPMSalesforceUtils.responseBodyParser,
REQUEST_BODY_MONITORING: true, // catch POST request body
REQUEST_BODY_PARSER: function (fact, reqBody) { return decodeURIComponent(reqBody); },
SEND_SYNC_ON_UNLOAD: true, // this only applies when the navigator.sendBeacon is unavailable (IE)
WITH_CREDENTIALS: false, // send requests with credentials/cookies
USER_CLICK: {
count: 0,
refreshInterval: 15, // (in seconds) we check periodically if we can close current user click txn and send current cum. txn
sequence: new Date().getTime() + Math.random().toString(36).substring(6),
queryStringGenerator: GermainAPMSalesforceUtils.userClickQueryStringGenerator, // user click txn query string extractor
excludeUrls: [// exclude http request from user click txn
/cometd/replay/
],
labelGenerator: GermainAPMSalesforceUtils.labelLookup,
nameGenerator: GermainAPMSalesforceUtils.nameLookup
},
EXCLUDE_URLS: [// exclude data points from monitoring by full URL (including query string)
/germainapm.*.js/i,
/ingestion/beacon/i,
/uxprofile/uxloader/i,
/uxprofile?monitoringProfile/i,
/config/agent/lookup/i,
/cometd/replay/
]
};

settings.application = {
appName: settings.agent.appName || 'Salesforce',
serverHost: settings.agent.serverHost,
username: function(){
var username = GermainAPMSalesforceUtils.usernameLookup();
if(username) return username;
var counter = 0;
var intervalId = setInterval(function(){
counter++;
var username = GermainAPMSalesforceUtils.usernameLookup();
if(username){
BOOMR.data.username = username;
clearInterval(intervalId);
}
if(counter > 20){
clearInterval(intervalId);
}
}, 1000);
return null;
},
session: GermainAPMSalesforceUtils.sessionLookup,
sequence: BOOMR.utils.session.getSequence
};

GermainAPM.init(settings);

// SFDC integration
function sfdcHook(){
window.$A.logger.subscribe("WARNING", function log(level, message, e) {
BOOMR.plugins.ConsoleMonitoring.sendMessage("warn", message, e ? e.message : null);
});
window.$A.logger.subscribe("ASSERT", function(level, message) {
BOOMR.plugins.ConsoleMonitoring.sendMessage("assert", message, null);
});
window.$A.logger.subscribe("ERROR", function(level, message, e) {
BOOMR.plugins.ConsoleMonitoring.sendMessage("error", message, e ? (e.stack && e.stack !== null) ? e.stack : e.message : null);
});
if(window.$A.message){
BOOMR.utils.hookCall(window.$A, 'message',
function(msg, error) {
BOOMR.plugins.PopupDialogMonitoring.sendMessage('Message Error', new Date().getTime(), error, msg);
}
);
}
if(window.$A.createComponent){
BOOMR.utils.hookCall(window.$A, 'createComponent',
function(type, attributes) {
if(type === "markup://one:applicationError"){
BOOMR.plugins.PopupDialogMonitoring.sendMessage('App Error', new Date().getTime(), attributes.error, attributes.error ? attributes.error.message : null);
} else if (type === "ltng:developerError"){
BOOMR.plugins.PopupDialogMonitoring.sendMessage('Developer Error', new Date().getTime(), attributes.stackTrace || attributes.messageText, attributes.messageTitle);
}
}
);
}
}

var counter = 0;
var intervalId = setInterval(function(){
counter++;
if(window.$A && window.$A.logger && window.$A.createComponent && window.$A.message){
sfdcHook();
clearInterval(intervalId);
}
if(counter > 20){
clearInterval(intervalId);
}
}, 1000);