feat(tracker):3.5.3:internal API - session change callbacks & log-levels fix

This commit is contained in:
ShiKhu 2022-03-06 14:56:43 +01:00
parent ffdd053c7b
commit 135a8d0e7b
5 changed files with 118 additions and 51 deletions

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
"version": "3.5.1",
"version": "3.5.3",
"keywords": [
"logging",
"replay"
@ -41,6 +41,6 @@
"error-stack-parser": "^2.0.6"
},
"engines": {
"node": ">=14"
"node": ">=12"
}
}

View file

@ -6,6 +6,7 @@ import Observer from "./observer/top_observer.js";
import Sanitizer from "./sanitizer.js";
import Ticker from "./ticker.js";
import Logger, { LogLevel } from "./logger.js";
import Session from "./session.js";
import { deviceMemory, jsHeapSizeLimit } from "../modules/performance.js";
@ -69,6 +70,7 @@ export default class App {
readonly sanitizer: Sanitizer;
readonly debug: Logger;
readonly notify: Logger;
readonly session: Session;
private readonly messages: Array<Message> = [];
private readonly observer: Observer;
private readonly startCallbacks: Array<Callback> = [];
@ -76,9 +78,6 @@ export default class App {
private readonly commitCallbacks: Array<CommitCallback> = [];
private readonly options: AppOptions;
private readonly revID: string;
private _sessionID: string | null = null;
private _userID: string | null = null;
private _metadata: Record<string, string> = {};
private activityState: ActivityState = ActivityState.NotActive;
private version = 'TRACKER_VERSION'; // TODO: version compatability check inside each plugin.
private readonly worker?: Worker;
@ -120,6 +119,7 @@ export default class App {
this.ticker.attach(() => this.commit());
this.debug = new Logger(this.options.__debug__);
this.notify = new Logger(this.options.verbose ? LogLevel.Warnings : LogLevel.Silent);
this.session = new Session(this);
try {
this.worker = new Worker(
URL.createObjectURL(
@ -129,18 +129,12 @@ export default class App {
this.worker.onerror = e => {
this._debug("webworker_error", e)
}
let lastTs = timestamp();
let fileno = 0;
this.worker.onmessage = ({ data }: MessageEvent) => {
if (data === null) {
this.stop();
} else if (data === "restart") {
this.stop();
this.start({
forceNew: true,
userID: this._userID || undefined,
metadata: this._metadata || undefined,
});
this.start({ forceNew: true });
}
};
const alertWorker = () => {
@ -197,10 +191,6 @@ export default class App {
}
}
attachCommitCallback(cb: CommitCallback): void {
this.commitCallbacks.push(cb)
}
safe<T extends (...args: any[]) => void>(fn: T): T {
const app = this;
return function (this: any, ...args: any) {
@ -216,6 +206,10 @@ export default class App {
} as any // TODO: correct typing
}
attachCommitCallback(cb: CommitCallback): void {
this.commitCallbacks.push(cb)
}
attachStartCallback(cb: Callback): void {
this.startCallbacks.push(cb);
}
@ -258,15 +252,12 @@ export default class App {
revID: this.revID,
timestamp: timestamp(),
trackerVersion: this.version,
userID: this._userID,
isSnippet: this.options.__is_snippet,
}
}
getSessionInfo() {
return {
sessionID: this._sessionID,
metadata: this._metadata,
...this.session.getInfo(),
...this.getStartInfo()
}
}
@ -277,7 +268,7 @@ export default class App {
}
}
getSessionID(): string | undefined {
return this._sessionID || undefined;
return this.session.getInfo().sessionID || undefined;
}
getHost(): string {
return new URL(this.options.ingestPoint).hostname
@ -338,10 +329,7 @@ export default class App {
}
sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
this._userID = startOpts.userID || null
this._metadata = startOpts.metadata || {} // TODO: update both dynamically on corresponding messages
const startInfo = this.getStartInfo()
const messageData: WorkerMessageData = {
ingestPoint: this.options.ingestPoint,
pageNo,
@ -361,6 +349,7 @@ export default class App {
},
body: JSON.stringify({
...startInfo,
userID: startOpts.userID || this.session.getInfo().userID,
token: sessionStorage.getItem(this.options.session_token_key),
deviceMemory,
jsHeapSizeLimit,
@ -389,9 +378,10 @@ export default class App {
}
sessionStorage.setItem(this.options.session_token_key, token);
localStorage.setItem(this.options.local_uuid_key, userUUID);
if (typeof sessionID === 'string') {
this._sessionID = sessionID;
}
this.session.update({
sessionID, // any. TODO check
...startOpts
});
this.activityState = ActivityState.Active
this.worker.postMessage({ token, beaconSizeLimit });
@ -399,9 +389,6 @@ export default class App {
this.observer.observe();
this.ticker.start();
Object.entries(this._metadata).forEach(([key, value]) =>
this.send(new Metadata(key, value)))
this.notify.log("OpenReplay tracking started.");
// TODO: get rid of onStart
const onStartInfo = { sessionToken: token, userUUID, sessionID };

View file

@ -1,11 +1,9 @@
export const LogLevel = {
Verbose: 4,
Errors: 4,
Verbose: 5,
Log: 4,
Warnings: 3,
Log: 2,
Errors: 2,
Silent: 0,
} as const;
type LogLevel = typeof LogLevel[keyof typeof LogLevel]
@ -31,30 +29,30 @@ interface _Options {
export type Options = true | _Options | LogLevel
export default class Logger {
private readonly opts: _Options;
constructor(private readonly options: Options = LogLevel.Silent) {
this.opts = options === true
private readonly options: _Options;
constructor(options: Options = LogLevel.Silent) {
this.options = options === true
? { level: LogLevel.Verbose }
: typeof options === "number" ? { level: options } : options;
}
log(...args: any) {
if (IsCustomLevel(this.opts.level)
? this.opts.level.log
: this.opts.level >= LogLevel.Log) {
if (IsCustomLevel(this.options.level)
? this.options.level.log
: this.options.level >= LogLevel.Log) {
console.log(...args)
}
}
warn(...args: any) {
if (IsCustomLevel(this.opts.level)
? this.opts.level.warn
: this.opts.level >= LogLevel.Warnings) {
if (IsCustomLevel(this.options.level)
? this.options.level.warn
: this.options.level >= LogLevel.Warnings) {
console.warn(...args)
}
}
error(...args: any) {
if (IsCustomLevel(this.opts.level)
? this.opts.level.error
: this.opts.level >= LogLevel.Errors) {
if (IsCustomLevel(this.options.level)
? this.options.level.error
: this.options.level >= LogLevel.Errors) {
console.error(...args)
}
}

View file

@ -0,0 +1,84 @@
import App, { StartOptions } from "./index.js";
import { UserID, UserAnonymousID, Metadata } from "../../messages/index.js";
enum ActivityState {
NotActive,
Starting,
Active,
}
interface SessionInfo {
sessionID: string | null,
metadata: Record<string, string>,
userID: string | null,
}
type OnUpdateCallback = (i: Partial<SessionInfo>) => void
export default class Session {
private metadata: Record<string, string> = {}
private userID: string | null = null
private sessionID: string | null = null
private activityState: ActivityState = ActivityState.NotActive
private callbacks: OnUpdateCallback[] = []
constructor(private app: App) {}
attachUpdateCallback(cb: OnUpdateCallback) {
this.callbacks.push(cb)
}
private handleUpdate() {
const sessInfo: Partial<SessionInfo> = this.getInfo()
if (sessInfo.userID == null) {
delete sessInfo.userID
}
if (sessInfo.sessionID == null) {
delete sessInfo.sessionID
}
this.callbacks.forEach(cb => cb(sessInfo))
}
update({ userID, metadata, sessionID }: Partial<SessionInfo>) {
if (userID != null) { // TODO clear nullable/undefinable types
this._setUserID(userID)
}
if (metadata !== undefined) {
Object.entries(metadata).forEach(kv => this._setMetadata(...kv))
}
if (sessionID !== undefined) {
this.sessionID = sessionID
}
this.handleUpdate()
}
private _setMetadata(key: string, value: string) {
this.app.send(new Metadata(key, value))
this.metadata[key] = value
}
private _setUserID(userID: string) {
this.app.send(new UserID(userID))
this.userID = userID
}
setMetadata(key: string, value: string) {
this._setMetadata(key, value)
this.handleUpdate()
}
setUserID(userID: string) {
this._setUserID(userID)
this.handleUpdate()
}
getInfo(): SessionInfo {
return {
sessionID: this.sessionID,
metadata: this.metadata,
userID: this.userID,
}
}
}

View file

@ -15,7 +15,6 @@ import Timing from "./modules/timing.js";
import Performance from "./modules/performance.js";
import Scroll from "./modules/scroll.js";
import Viewport from "./modules/viewport.js";
import Longtasks from "./modules/longtasks.js";
import CSSRules from "./modules/cssrules.js";
import { IN_BROWSER, deprecationWarn, DOCS_HOST } from "./utils.js";
@ -109,7 +108,6 @@ export default class API {
Timing(app, options);
Performance(app, options);
Scroll(app);
Longtasks(app);
(window as any).__OPENREPLAY__ = this;
if (options.autoResetOnWindowOpen) {
@ -190,7 +188,7 @@ export default class API {
setUserID(id: string): void {
if (typeof id === 'string' && this.app !== null) {
this.app.send(new UserID(id));
this.app.session.setUserID(id);
}
}
userID(id: string): void {
@ -214,7 +212,7 @@ export default class API {
typeof value === 'string' &&
this.app !== null
) {
this.app.send(new Metadata(key, value));
this.app.session.setMetadata(key, value);
}
}
metadata(key: string, value: string): void {