feat(tracker):3.5.3:internal API - session change callbacks & log-levels fix
This commit is contained in:
parent
ffdd053c7b
commit
135a8d0e7b
5 changed files with 118 additions and 51 deletions
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
84
tracker/tracker/src/main/app/session.ts
Normal file
84
tracker/tracker/src/main/app/session.ts
Normal 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,
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue