Skip to content

Commit 3c1e208

Browse files
authored
Fixes #85 Improvements to session management (Breaking Change) (#124)
* Fixes #85 Improvements to session management (Breaking Change) Added useSessions() * Sync compiler options * Provide better hinting around event type
1 parent a889cd2 commit 3c1e208

22 files changed

+141
-59
lines changed

example/browser/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ await Exceptionless.startup((c) => {
1313
c.updateSettingsWhenIdleInterval = 15000;
1414
c.usePersistedQueueStorage = true;
1515
c.setUserIdentity("12345678", "Blake");
16+
c.useSessions();
1617

1718
// set some default data
1819
c.defaultData["SampleUser"] = {

packages/angularjs/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
"./package.json": "./package.json"
3434
},
3535
"scripts": {
36-
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --sourcemap --target=es2015 --format=esm --outfile=dist/index.bundle.js && esbuild src/index.ts --bundle --minify --sourcemap --target=es2015 --format=esm --outfile=dist/index.bundle.min.js",
37-
"watch": "tsc -p ../core/tsconfig.json -w --preserveWatchOutput & tsc -p tsconfig.json -w --preserveWatchOutput & esbuild src/index.ts --bundle --sourcemap --target=es2015 --format=esm --watch --outfile=dist/index.bundle.js"
36+
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.js && esbuild src/index.ts --bundle --minify --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.min.js",
37+
"watch": "tsc -p ../core/tsconfig.json -w --preserveWatchOutput & tsc -p tsconfig.json -w --preserveWatchOutput & esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --watch --outfile=dist/index.bundle.js"
3838
},
3939
"sideEffects": false,
4040
"publishConfig": {

packages/browser/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"testEnvironment": "jsdom"
4646
},
4747
"scripts": {
48-
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.js && esbuild src/index.ts --bundle --minify --sourcemap --target=es2019 --format=esm --outfile=dist/index.bundle.min.js",
48+
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.js && esbuild src/index.ts --bundle --minify --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.min.js",
4949
"watch": "tsc -p ../core/tsconfig.json -w --preserveWatchOutput & tsc -p tsconfig.json -w --preserveWatchOutput & esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --watch --outfile=dist/index.bundle.js",
5050
"test": "jest"
5151
},

packages/browser/src/plugins/BrowserLifeCyclePlugin.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@ export class BrowserLifeCyclePlugin implements IEventPlugin {
1717

1818
this._client = context.client;
1919

20-
globalThis.addEventListener("beforeunload", () => void this._client?.suspend());
20+
globalThis.addEventListener("beforeunload", () => {
21+
if (this._client?.config.sessionsEnabled) {
22+
void this._client?.submitSessionEnd();
23+
}
24+
25+
void this._client?.suspend();
26+
});
27+
2128
document.addEventListener("visibilitychange", () => {
2229
if (document.visibilityState === 'visible') {
23-
void this._client?.startup()
30+
void this._client?.startup();
2431
} else {
25-
void this._client?.suspend()
32+
void this._client?.suspend();
2633
}
2734
});
2835

packages/browser/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"compilerOptions": {
44
"lib": [
55
"DOM",
6-
"ES2020"
6+
"ES2021"
77
],
88
"outDir": "dist",
99
"rootDir": "src",

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"testEnvironment": "jsdom"
4646
},
4747
"scripts": {
48-
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.js && esbuild src/index.ts --bundle --minify --sourcemap --target=es2019 --format=esm --outfile=dist/index.bundle.min.js",
48+
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.js && esbuild src/index.ts --bundle --minify --sourcemap --target=es2017 --format=esm --outfile=dist/index.bundle.min.js",
4949
"watch": "tsc -p tsconfig.json -w --preserveWatchOutput & esbuild src/index.ts --bundle --sourcemap --target=es2017 --format=esm --watch --outfile=dist/index.bundle.js",
5050
"test": "jest"
5151
},

packages/core/src/EventBuilder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ExceptionlessClient } from "./ExceptionlessClient.js";
2-
import { Event, KnownEventDataKeys } from "./models/Event.js";
2+
import { Event, EventType, KnownEventDataKeys } from "./models/Event.js";
33
import { ManualStackingInfo } from "./models/data/ManualStackingInfo.js";
44
import { UserInfo } from "./models/data/UserInfo.js";
55
import { EventContext } from "./models/EventContext.js";
@@ -19,7 +19,7 @@ export class EventBuilder {
1919
this.context = context || new EventContext();
2020
}
2121

22-
public setType(type: string): EventBuilder {
22+
public setType(type: EventType): EventBuilder {
2323
if (type) {
2424
this.target.type = type;
2525
}

packages/core/src/ExceptionlessClient.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ export class ExceptionlessClient {
4646
// TODO: Can we schedule this as part of startup?
4747
await queue.process();
4848
}
49+
50+
if (this.config.sessionsEnabled) {
51+
await this.submitSessionStart();
52+
}
4953
}
5054

5155
/** Submit events, pause any timers and go into low power mode. */
@@ -175,27 +179,21 @@ export class ExceptionlessClient {
175179
return this.createSessionStart().submit();
176180
}
177181

178-
public async submitSessionEnd(sessionIdOrUserId: string): Promise<void> {
179-
if (sessionIdOrUserId && this.config.enabled && this.config.isValid) {
180-
this.config.services.log.info(
181-
`Submitting session end: ${sessionIdOrUserId}`,
182-
);
183-
await this.config.services.submissionClient.submitHeartbeat(
184-
sessionIdOrUserId,
185-
true,
186-
);
182+
public async submitSessionEnd(sessionIdOrUserId?: string): Promise<void> {
183+
const { currentSessionIdentifier, enabled, isValid, services } = this.config;
184+
const sessionId = sessionIdOrUserId || currentSessionIdentifier;
185+
if (sessionId && enabled && isValid) {
186+
services.log.info(`Submitting session end: ${sessionId}`);
187+
await services.submissionClient.submitHeartbeat(sessionId, true);
187188
}
188189
}
189190

190-
public async submitSessionHeartbeat(sessionIdOrUserId: string): Promise<void> {
191-
if (sessionIdOrUserId && this.config.enabled && this.config.isValid) {
192-
this.config.services.log.info(
193-
`Submitting session heartbeat: ${sessionIdOrUserId}`,
194-
);
195-
await this.config.services.submissionClient.submitHeartbeat(
196-
sessionIdOrUserId,
197-
false,
198-
);
191+
public async submitSessionHeartbeat(sessionIdOrUserId?: string): Promise<void> {
192+
const { currentSessionIdentifier, enabled, isValid, services } = this.config;
193+
const sessionId = sessionIdOrUserId || currentSessionIdentifier;
194+
if (sessionId && enabled && isValid) {
195+
services.log.info(`Submitting session heartbeat: ${sessionId}`);
196+
await services.submissionClient.submitHeartbeat(sessionId, false);
199197
}
200198
}
201199

packages/core/src/configuration/Configuration.ts

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ConsoleLog } from "../logging/ConsoleLog.js";
55
import { NullLog } from "../logging/NullLog.js";
66
import { UserInfo } from "../models/data/UserInfo.js";
77
import { HeartbeatPlugin } from "../plugins/default/HeartbeatPlugin.js";
8+
import { SessionIdManagementPlugin } from "../plugins/default/SessionIdManagementPlugin.js";
89
import { EventPluginContext } from "../plugins/EventPluginContext.js";
910
import { EventPluginManager } from "../plugins/EventPluginManager.js";
1011
import { IEventPlugin } from "../plugins/IEventPlugin.js";
@@ -428,32 +429,24 @@ export class Configuration {
428429
}
429430

430431
/**
431-
* Set the default user identity for all events. If the heartbeat interval is
432-
* greater than 0 (default: 30000ms), heartbeats will be sent after the first
433-
* event submission.
432+
* Set the default user identity for all events.
434433
*/
435-
public setUserIdentity(userInfo: UserInfo, heartbeatInterval?: number): void;
436-
public setUserIdentity(identity: string, heartbeatInterval?: number): void;
437-
public setUserIdentity(identity: string, name: string, heartbeatInterval?: number): void;
438-
public setUserIdentity(userInfoOrIdentity: UserInfo | string, nameOrHeartbeatInterval?: string | number, heartbeatInterval: number = 30000): void {
439-
const name: string | undefined = typeof nameOrHeartbeatInterval === "string" ? nameOrHeartbeatInterval : undefined;
434+
public setUserIdentity(userInfo: UserInfo): void;
435+
public setUserIdentity(identity: string): void;
436+
public setUserIdentity(identity: string, name: string): void;
437+
public setUserIdentity(userInfoOrIdentity: UserInfo | string, name?: string): void {
440438
const userInfo: UserInfo = typeof userInfoOrIdentity !== "string"
441439
? userInfoOrIdentity
442440
: <UserInfo>{ identity: userInfoOrIdentity, name };
443441

444-
const interval: number = typeof nameOrHeartbeatInterval === "number" ? nameOrHeartbeatInterval : heartbeatInterval;
445-
const plugin = new HeartbeatPlugin(interval);
446-
447442
const shouldRemove: boolean = !userInfo || (!userInfo.identity && !userInfo.name);
448443
if (shouldRemove) {
449-
this.removePlugin(plugin)
450444
delete this.defaultData[KnownEventDataKeys.UserInfo];
451445
} else {
452-
this.addPlugin(plugin)
453446
this.defaultData[KnownEventDataKeys.UserInfo] = userInfo;
454447
}
455448

456-
this.services.log.info(`user identity: ${shouldRemove ? "null" : <string>userInfo.identity} (heartbeat interval: ${interval}ms)`);
449+
this.services.log.info(`user identity: ${shouldRemove ? "null" : <string>userInfo.identity}`);
457450
}
458451

459452
/**
@@ -477,7 +470,39 @@ export class Configuration {
477470
* This setting only works in environments that supports persisted storage.
478471
* There is also a performance penalty of extra IO/serialization.
479472
*/
480-
public usePersistedQueueStorage = false;
473+
public usePersistedQueueStorage: boolean = false;
474+
475+
/**
476+
* Gets or sets a value indicating whether to automatically send session start,
477+
* session heartbeats and session end events.
478+
*/
479+
public sessionsEnabled = false;
480+
481+
/**
482+
* Internal property used to track the current session identifier.
483+
*/
484+
public currentSessionIdentifier: string | null = null;
485+
486+
/**
487+
*
488+
* @param sendHeartbeats Controls whether heartbeat events are sent on an interval.
489+
* @param heartbeatInterval The interval at which heartbeats are sent after the last sent event. The default is 1 minutes.
490+
* @param useSessionIdManagement Allows you to manually control the session id. This is only recommended for single user desktop environments.
491+
*/
492+
public useSessions(sendHeartbeats: boolean = true, heartbeatInterval: number = 60000, useSessionIdManagement: boolean = false) {
493+
this.sessionsEnabled = true;
494+
495+
if (useSessionIdManagement) {
496+
this.addPlugin(new SessionIdManagementPlugin());
497+
}
498+
499+
const plugin = new HeartbeatPlugin(heartbeatInterval);
500+
if (sendHeartbeats) {
501+
this.addPlugin(plugin);
502+
} else {
503+
this.removePlugin(plugin);
504+
}
505+
}
481506

482507
private originalSettings?: Record<string, string>;
483508

packages/core/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export type { ILog } from "./logging/ILog.js";
88
export { ConsoleLog } from "./logging/ConsoleLog.js";
99
export { NullLog } from "./logging/NullLog.js";
1010

11-
export type { Event, IEventData } from "./models/Event.js";
11+
export type { Event, EventType, IEventData } from "./models/Event.js";
1212
export { KnownEventDataKeys } from "./models/Event.js";
1313
export type { EnvironmentInfo } from "./models/data/EnvironmentInfo.js";
1414
export type { ManualStackingInfo } from "./models/data/ManualStackingInfo.js";
@@ -30,6 +30,7 @@ export { DuplicateCheckerPlugin } from "./plugins/default/DuplicateCheckerPlugin
3030
export { EventExclusionPlugin } from "./plugins/default/EventExclusionPlugin.js";
3131
export { HeartbeatPlugin } from "./plugins/default/HeartbeatPlugin.js";
3232
export { ReferenceIdPlugin } from "./plugins/default/ReferenceIdPlugin.js";
33+
export { SessionIdManagementPlugin } from "./plugins/default/SessionIdManagementPlugin.js";
3334
export { IgnoredErrorProperties, SimpleErrorPlugin } from "./plugins/default/SimpleErrorPlugin.js"
3435
export { SubmissionMethodPlugin } from "./plugins/default/SubmissionMethodPlugin.js";
3536
export { EventContext } from "./models/EventContext.js";

0 commit comments

Comments
 (0)