Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ export class BacktraceMainElectronModule implements BacktraceModule {
) {
// Sort crashes and sessions by timestamp descending
const crashes = crashReporter.getUploadedReports().sort((a, b) => b.date.getTime() - a.date.getTime());
const previousSessions = session.getPreviousSessions().sort((a, b) => b.timestamp - a.timestamp);
const previousSessions = session
.getPreviousSessions()
.sort((a, b) => b.sessionId.timestamp - a.sessionId.timestamp);

for (const crash of crashes) {
const rxid = this.getCrashRxid(crash.id);
Expand All @@ -159,7 +161,7 @@ export class BacktraceMainElectronModule implements BacktraceModule {

try {
// Get first session that happened before the crash
const session = previousSessions.find((p) => p.timestamp < crash.date.getTime());
const session = previousSessions.find((p) => p.sessionId.timestamp < crash.date.getTime());
// If there is no such session, there won't be any other sessions
if (!session) {
break;
Expand Down
12 changes: 6 additions & 6 deletions packages/node/tests/breadcrumbs/FileBreadcrumbsStorage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const nextTick = promisify(process.nextTick);
describe('FileBreadcrumbsStorage', () => {
it('should return added breadcrumbs', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -108,7 +108,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return added breadcrumbs in two attachments', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -190,7 +190,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return no more than maximumBreadcrumbs breadcrumbs', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -262,7 +262,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return breadcrumbs up to the json size', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -330,7 +330,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return attachments with a valid name from getAttachments', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -374,7 +374,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return attachments with a valid name from getAttachmentProviders', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const nextTick = promisify(process.nextTick);
describe('FileBreadcrumbsStorage', () => {
it('should return added breadcrumbs', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -99,7 +99,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return added breadcrumbs in two attachments', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -181,7 +181,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return no more than maximumBreadcrumbs breadcrumbs', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -253,7 +253,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return breadcrumbs up to the json size', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -322,7 +322,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return attachments with a valid name from getAttachments', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down Expand Up @@ -366,7 +366,7 @@ describe('FileBreadcrumbsStorage', () => {

it('should return attachments with a valid name from getAttachmentProviders', async () => {
const fs = mockStreamFileSystem();
const session = new SessionFiles(fs, '.', 'sessionId');
const session = new SessionFiles(fs, '.', { id: 'sessionId', timestamp: Date.now() });

const breadcrumbs: RawBreadcrumb[] = [
{
Expand Down
5 changes: 4 additions & 1 deletion packages/sdk-core/src/BacktraceCoreClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,10 @@ export abstract class BacktraceCoreClient<
const sessionFiles = new SessionFiles(
this.fileSystem,
this.options.database.path,
this.sessionId,
{
id: this.sessionId,
timestamp: Date.now(),
},
this.options.database.maximumOldSessions ?? 1,
);
this._modules.set(SessionFiles, sessionFiles);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BacktraceAttachment } from '../../model/attachment/index.js';
import { isFileAttachment } from '../attachments/isFileAttachment.js';
import { FileSystem } from '../storage/index.js';
import { FileSystem, SessionId } from '../storage/index.js';
import { BacktraceDatabaseFileRecord } from './BacktraceDatabaseFileRecord.js';
import { AttachmentBacktraceDatabaseRecord } from './model/BacktraceDatabaseRecord.js';

Expand All @@ -10,7 +10,7 @@ export class AttachmentBacktraceDatabaseFileRecord implements AttachmentBacktrac
public readonly rxid: string;
public readonly timestamp: number;
public readonly attachment: BacktraceAttachment<unknown>;
public readonly sessionId: string;
public readonly sessionId: SessionId | string;
public locked: boolean;

private constructor(record: AttachmentBacktraceDatabaseRecord) {
Expand Down
10 changes: 4 additions & 6 deletions packages/sdk-core/src/modules/database/BacktraceDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { BacktraceDatabaseConfiguration } from '../../model/configuration/Backtr
import { BacktraceData } from '../../model/data/BacktraceData.js';
import { BacktraceReportSubmission } from '../../model/http/BacktraceReportSubmission.js';
import { BacktraceModule, BacktraceModuleBindData } from '../BacktraceModule.js';
import { SessionFiles } from '../storage/index.js';
import { SessionFiles, SessionId } from '../storage/index.js';
import { BacktraceDatabaseContext } from './BacktraceDatabaseContext.js';
import { BacktraceDatabaseEvents } from './BacktraceDatabaseEvents.js';
import { BacktraceDatabaseStorageProvider } from './BacktraceDatabaseStorageProvider.js';
Expand Down Expand Up @@ -135,16 +135,14 @@ export class BacktraceDatabase extends Events<BacktraceDatabaseEvents> implement
return undefined;
}

const sessionId = backtraceData.attributes?.['application.session'];

const record: ReportBacktraceDatabaseRecord = {
type: 'report',
data: backtraceData,
timestamp: TimeHelper.now(),
id: IdGenerator.uuid(),
locked: false,
attachments: attachments,
sessionId: typeof sessionId === 'string' ? sessionId : undefined,
sessionId: this._sessionFiles?.sessionId,
};

this.prepareDatabase([record]);
Expand All @@ -171,7 +169,7 @@ export class BacktraceDatabase extends Events<BacktraceDatabaseEvents> implement
public addAttachment(
rxid: string,
attachment: BacktraceAttachment,
sessionId: string,
sessionId: SessionId,
): AttachmentBacktraceDatabaseRecord | undefined {
if (!this._enabled) {
return undefined;
Expand Down Expand Up @@ -398,7 +396,7 @@ export class BacktraceDatabase extends Events<BacktraceDatabaseEvents> implement
}

const sessionId = record.sessionId;
if (typeof sessionId !== 'string') {
if (!SessionFiles.isValidSessionId(sessionId)) {
this._sessionFiles.lockPreviousSessions(record.id);
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BacktraceAttachment } from '../../model/attachment/index.js';
import { BacktraceData } from '../../model/data/index.js';
import { isFileAttachment } from '../attachments/isFileAttachment.js';
import { FileSystem } from '../storage/index.js';
import { FileSystem, SessionId } from '../storage/index.js';
import { BacktraceDatabaseFileRecord } from './BacktraceDatabaseFileRecord.js';
import { ReportBacktraceDatabaseRecord } from './model/BacktraceDatabaseRecord.js';

Expand All @@ -10,7 +10,7 @@ export class ReportBacktraceDatabaseFileRecord implements ReportBacktraceDatabas
public readonly data: BacktraceData;
public readonly id: string;
public readonly timestamp: number;
public readonly sessionId?: string;
public readonly sessionId?: SessionId | string;
public locked: boolean;

private constructor(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { BacktraceAttachment } from '../../../model/attachment/index.js';
import { BacktraceData } from '../../../model/data/BacktraceData.js';
import { SessionId } from '../../storage/SessionFiles.js';

export interface ReportBacktraceDatabaseRecord {
readonly type: 'report';
readonly data: BacktraceData;
readonly id: string;
readonly timestamp: number;
readonly sessionId?: string;
readonly sessionId?: string | SessionId;
attachments: BacktraceAttachment[];
/**
* Determines if the record is in use
Expand All @@ -20,7 +21,7 @@ export interface AttachmentBacktraceDatabaseRecord {
readonly rxid: string;
readonly timestamp: number;
readonly attachment: BacktraceAttachment;
readonly sessionId: string;
readonly sessionId: string | SessionId;
/**
* Determines if the record is in use
*/
Expand Down
60 changes: 45 additions & 15 deletions packages/sdk-core/src/modules/storage/SessionFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import { FileSystem } from './FileSystem.js';

interface FileSession {
readonly file: string;
readonly sessionId: string;
readonly sessionId: SessionId;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about backward compatibility?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this could fail. I added a check for this where required.

readonly escapedSessionId: string;
readonly timestamp: number;
}

type SessionEvents = {
unlocked: [];
};

export interface SessionId {
readonly id: string;
readonly timestamp: number;
}

const SESSION_MARKER_PREFIX = 'bt-session';

const isDefined = <T>(t: T | undefined): t is T => !!t;
Expand All @@ -30,17 +34,16 @@ export class SessionFiles implements BacktraceModule {
constructor(
private readonly _fileSystem: FileSystem,
private readonly _directory: string,
public readonly sessionId: string,
public readonly sessionId: SessionId,
private readonly _maxPreviousLockedSessions = 1,
public readonly timestamp = Date.now(),
private readonly _lockable = true,
) {
this._escapedSessionId = this.escapeFileName(sessionId);
this._escapedSessionId = this.escapeFileName(sessionId.id);
this.marker = this.getFileName(SESSION_MARKER_PREFIX);
}

public initialize(): void {
this.createSessionMarker();
return this.createSessionMarker();
}

public getPreviousSession() {
Expand All @@ -57,12 +60,14 @@ export class SessionFiles implements BacktraceModule {
.filter((f) => f.startsWith(SESSION_MARKER_PREFIX))
.map((f) => this.getFileSession(f))
.filter(isDefined)
.sort((a, b) => b.timestamp - a.timestamp);
.sort((a, b) => b.sessionId.timestamp - a.sessionId.timestamp);

const currentSessionMarker = sessionMarkers.find((s) => s.sessionId === this.sessionId);
const currentSessionMarker = sessionMarkers.find((s) => this.sessionIdEquals(s.sessionId, this.sessionId));

const lastSessionMarker = currentSessionMarker
? sessionMarkers.filter(({ timestamp }) => currentSessionMarker.timestamp > timestamp)[0]
? sessionMarkers.filter(
({ sessionId }) => currentSessionMarker.sessionId.timestamp > sessionId.timestamp,
)[0]
: sessionMarkers[0];

if (!lastSessionMarker) {
Expand All @@ -74,15 +79,14 @@ export class SessionFiles implements BacktraceModule {
this._directory,
lastSessionMarker.sessionId,
this._maxPreviousLockedSessions - 1,
lastSessionMarker.timestamp,
this._maxPreviousLockedSessions > 0,
));
}

public getSessionWithId(sessionId: string) {
public getSessionWithId(sessionId: SessionId) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let session: SessionFiles | undefined = this;
while (session && session.sessionId !== sessionId) {
while (session && this.sessionIdEquals(session.sessionId, sessionId)) {
session = session.getPreviousSession();
}

Expand Down Expand Up @@ -124,7 +128,11 @@ export class SessionFiles implements BacktraceModule {
public getFileName(prefix: string) {
this.throwIfCleared();

return this._directory + '/' + `${this.escapeFileName(prefix)}_${this._escapedSessionId}_${this.timestamp}`;
return (
this._directory +
'/' +
`${this.escapeFileName(prefix)}_${this._escapedSessionId}_${this.sessionId.timestamp}`
);
}

public getSessionFiles() {
Expand All @@ -134,7 +142,7 @@ export class SessionFiles implements BacktraceModule {
return files
.map((file) => this.getFileSession(file))
.filter(isDefined)
.filter(({ sessionId }) => sessionId === this.sessionId)
.filter(({ sessionId }) => this.sessionIdEquals(sessionId, this.sessionId))
.map(({ file }) => this._directory + '/' + file);
}

Expand Down Expand Up @@ -184,7 +192,14 @@ export class SessionFiles implements BacktraceModule {
return undefined;
}

return { file, escapedSessionId, timestamp, sessionId: this.unescapeFileName(escapedSessionId) };
return {
file,
escapedSessionId,
sessionId: {
timestamp,
id: this.unescapeFileName(escapedSessionId),
},
};
}

private readDirectoryFiles() {
Expand Down Expand Up @@ -232,4 +247,19 @@ export class SessionFiles implements BacktraceModule {
throw new Error('This session files are cleared.');
}
}

private sessionIdEquals(a: SessionId, b: SessionId) {
return a.id === b.id && a.timestamp === b.timestamp;
}

public static isValidSessionId(value: unknown): value is SessionId {
return (
typeof value === 'object' &&
!!value &&
'id' in value &&
'timestamp' in value &&
typeof value.id === 'string' &&
typeof value.timestamp === 'number'
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function randomAttachmentRecord(): AttachmentBacktraceDatabaseRecord {
timestamp: Date.now(),
attachment: { name: 'x', get: () => undefined },
rxid: 'x',
sessionId: crypto.randomUUID(),
sessionId: { id: crypto.randomUUID(), timestamp: Date.now() },
};
}

Expand Down
Loading