@grafana/faro-web-sdk
Version:
Faro instrumentations, metas, transports for web.
108 lines • 7.02 kB
JavaScript
import { BaseInstrumentation, dateNow, EVENT_SESSION_EXTEND, EVENT_SESSION_RESUME, EVENT_SESSION_START, VERSION, } from '@grafana/faro-core';
import { createSession } from '../../metas';
import { getSessionManagerByConfig, isSampled } from './sessionManager';
import { PersistentSessionsManager } from './sessionManager/PersistentSessionsManager';
import { createUserSessionObject, isUserSessionValid } from './sessionManager/sessionManagerUtils';
export class SessionInstrumentation extends BaseInstrumentation {
constructor() {
super(...arguments);
this.name = '@grafana/faro-web-sdk:instrumentation-session';
this.version = VERSION;
}
sendSessionStartEvent(meta) {
var _a, _b;
const session = meta.session;
if (session && session.id !== ((_a = this.notifiedSession) === null || _a === void 0 ? void 0 : _a.id)) {
if (this.notifiedSession && this.notifiedSession.id === ((_b = session.attributes) === null || _b === void 0 ? void 0 : _b['previousSession'])) {
this.api.pushEvent(EVENT_SESSION_EXTEND, {}, undefined, { skipDedupe: true });
this.notifiedSession = session;
return;
}
this.notifiedSession = session;
// no need to add attributes and session id, they are included as part of meta
// automatically
this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });
}
}
createInitialSession(SessionManager, sessionsConfig) {
var _a, _b, _c, _d, _e, _f;
let storedUserSession = SessionManager.fetchUserSession();
if (sessionsConfig.persistent && sessionsConfig.maxSessionPersistenceTime && storedUserSession) {
const now = dateNow();
const shouldClearPersistentSession = storedUserSession.lastActivity < now - sessionsConfig.maxSessionPersistenceTime;
if (shouldClearPersistentSession) {
PersistentSessionsManager.removeUserSession();
storedUserSession = null;
}
}
let lifecycleType;
let initialSession;
if (isUserSessionValid(storedUserSession)) {
const sessionId = storedUserSession === null || storedUserSession === void 0 ? void 0 : storedUserSession.sessionId;
initialSession = createUserSessionObject({
sessionId,
isSampled: storedUserSession.isSampled || false,
started: storedUserSession === null || storedUserSession === void 0 ? void 0 : storedUserSession.started,
});
const storedUserSessionMeta = storedUserSession === null || storedUserSession === void 0 ? void 0 : storedUserSession.sessionMeta;
// For resumed sessions we want to merge the previous overrides with the configured ones.
// If the same key is present in both, the new one will override the old one.
const overrides = Object.assign(Object.assign({}, (_a = sessionsConfig.session) === null || _a === void 0 ? void 0 : _a.overrides), storedUserSessionMeta === null || storedUserSessionMeta === void 0 ? void 0 : storedUserSessionMeta.overrides);
initialSession.sessionMeta = Object.assign(Object.assign({}, sessionsConfig.session), { id: sessionId, attributes: Object.assign(Object.assign(Object.assign({}, (_b = sessionsConfig.session) === null || _b === void 0 ? void 0 : _b.attributes), storedUserSessionMeta === null || storedUserSessionMeta === void 0 ? void 0 : storedUserSessionMeta.attributes), {
// For valid resumed sessions we do not want to recalculate the sampling decision on each init phase.
isSampled: initialSession.isSampled.toString() }), overrides });
lifecycleType = EVENT_SESSION_RESUME;
}
else {
const sessionId = (_d = (_c = sessionsConfig.session) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : createSession().id;
initialSession = createUserSessionObject({
sessionId,
isSampled: isSampled(),
});
const overrides = (_e = sessionsConfig.session) === null || _e === void 0 ? void 0 : _e.overrides;
initialSession.sessionMeta = Object.assign({ id: sessionId, attributes: Object.assign({ isSampled: initialSession.isSampled.toString() }, (_f = sessionsConfig.session) === null || _f === void 0 ? void 0 : _f.attributes) }, (overrides ? { overrides } : {}));
lifecycleType = EVENT_SESSION_START;
}
return { initialSession, lifecycleType };
}
registerBeforeSendHook(SessionManager) {
var _a;
const { updateSession } = new SessionManager();
(_a = this.transports) === null || _a === void 0 ? void 0 : _a.addBeforeSendHooks((item) => {
var _a, _b, _c;
updateSession();
const attributes = (_a = item.meta.session) === null || _a === void 0 ? void 0 : _a.attributes;
if (attributes && (attributes === null || attributes === void 0 ? void 0 : attributes['isSampled']) === 'true') {
let newItem = JSON.parse(JSON.stringify(item));
const newAttributes = (_b = newItem.meta.session) === null || _b === void 0 ? void 0 : _b.attributes;
newAttributes === null || newAttributes === void 0 ? true : delete newAttributes['isSampled'];
if (Object.keys(newAttributes !== null && newAttributes !== void 0 ? newAttributes : {}).length === 0) {
(_c = newItem.meta.session) === null || _c === void 0 ? true : delete _c.attributes;
}
return newItem;
}
return null;
});
}
initialize() {
this.logDebug('init session instrumentation');
const sessionTrackingConfig = this.config.sessionTracking;
if (sessionTrackingConfig === null || sessionTrackingConfig === void 0 ? void 0 : sessionTrackingConfig.enabled) {
const SessionManager = getSessionManagerByConfig(sessionTrackingConfig);
this.registerBeforeSendHook(SessionManager);
const { initialSession, lifecycleType } = this.createInitialSession(SessionManager, sessionTrackingConfig);
SessionManager.storeUserSession(initialSession);
const initialSessionMeta = initialSession.sessionMeta;
this.notifiedSession = initialSessionMeta;
this.api.setSession(initialSessionMeta);
if (lifecycleType === EVENT_SESSION_START) {
this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });
}
if (lifecycleType === EVENT_SESSION_RESUME) {
this.api.pushEvent(EVENT_SESSION_RESUME, {}, undefined, { skipDedupe: true });
}
}
this.metas.addListener(this.sendSessionStartEvent.bind(this));
}
}
//# sourceMappingURL=instrumentation.js.map