Skip to content

Commit

Permalink
Desktop: Attach log to crash dump when the application crashes
Browse files Browse the repository at this point in the history
  • Loading branch information
laurent22 committed Apr 26, 2024
1 parent 74bc9b3 commit c5dfa4c
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 48 deletions.
2 changes: 2 additions & 0 deletions packages/app-desktop/app.ts
Expand Up @@ -390,6 +390,8 @@ class Application extends BaseApplication {

argv = await super.start(argv, startOptions);

bridge().setLogFilePath(Logger.globalLogger.logFilePath());

await this.applySettingsSideEffects();

if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) {
Expand Down
51 changes: 48 additions & 3 deletions packages/app-desktop/bridge.ts
Expand Up @@ -6,11 +6,14 @@ import { dirname, toSystemSlashes } from '@joplin/lib/path-utils';
import { fileUriToPath } from '@joplin/utils/url';
import { urlDecode } from '@joplin/lib/string-utils';
import * as Sentry from '@sentry/electron/main';
import { ErrorEvent } from '@sentry/types/types';
import { homedir } from 'os';
import { msleep } from '@joplin/utils/time';
import { pathExists, writeFileSync } from 'fs-extra';
import { pathExists, pathExistsSync, writeFileSync } from 'fs-extra';
import { extname, normalize } from 'path';
import isSafeToOpen from './utils/isSafeToOpen';
import { closeSync, openSync, readSync, statSync } from 'fs';
import { KB } from '@joplin/utils/bytes';

interface LastSelectedPath {
file: string;
Expand Down Expand Up @@ -38,6 +41,7 @@ export class Bridge {
private rootProfileDir_: string;
private appName_: string;
private appId_: string;
private logFilePath_ = '';

private extraAllowedExtensions_: string[] = [];
private onAllowedExtensionsChangeListener_: OnAllowedExtensionsChange = ()=>{};
Expand All @@ -56,12 +60,53 @@ export class Bridge {
this.sentryInit();
}

public setLogFilePath(v: string) {
this.logFilePath_ = v;
}

private sentryInit() {
const getLogLines = () => {
try {
if (!this.logFilePath_ || !pathExistsSync(this.logFilePath_)) return '';
const { size } = statSync(this.logFilePath_);
if (!size) return '';

const bytesToRead = Math.min(size, 100 * KB);
const handle = openSync(this.logFilePath_, 'r');
const position = size - bytesToRead;
const buffer = Buffer.alloc(bytesToRead);
readSync(handle, buffer, 0, bytesToRead, position);
closeSync(handle);
return buffer.toString('utf-8');
} catch (error) {
// Can't do anything in this context
return '';
}
};

const getLogAttachment = () => {
const lines = getLogLines();
if (!lines) return null;
return { filename: 'joplin-log.txt', data: lines };
};

const options: Sentry.ElectronMainOptions = {
beforeSend: event => {
beforeSend: (event, hint) => {
try {
const logAttachment = getLogAttachment();
if (logAttachment) hint.attachments = [logAttachment];
const date = (new Date()).toISOString().replace(/[:-]/g, '').split('.')[0];
writeFileSync(`${homedir()}/joplin_crash_dump_${date}.json`, JSON.stringify(event, null, '\t'), 'utf-8');

interface ErrorEventWithLog extends ErrorEvent {
log: string[];
}

const errorEventWithLog: ErrorEventWithLog = {
...event,
log: logAttachment ? logAttachment.data.trim().split('\n') : [],
};

writeFileSync(`${homedir()}/joplin_crash_dump_${date}.json`, JSON.stringify(errorEventWithLog, null, '\t'), 'utf-8');
} catch (error) {
// Ignore the error since we can't handle it here
}
Expand Down
43 changes: 20 additions & 23 deletions packages/utils/Logger.test.ts
Expand Up @@ -54,36 +54,33 @@ describe('Logger', () => {
'',
].join('\n'));

// Shouldn't have kept any line, since keptLineCount was not set
expect(logger.keptLines).toEqual([]);

jest.useRealTimers();
});

it('should keep the last lines', async () => {
jest.useFakeTimers().setSystemTime(new Date('2020-01-01'));
// it('should keep the last lines', async () => {
// jest.useFakeTimers().setSystemTime(new Date('2020-01-01'));

const logger = createLogger();
logger.keptLineCount = 2;
logger.info('one');
logger.info('two');
logger.info('three');
// const logger = createLogger();
// logger.keptLineCount = 2;
// logger.info('one');
// logger.info('two');
// logger.info('three');

await logger.waitForFileWritesToComplete_();
// await logger.waitForFileWritesToComplete_();

expect(await getLogContent()).toBe([
'2020-01-01 00:00:00: testing: one',
'2020-01-01 00:00:00: testing: two',
'2020-01-01 00:00:00: testing: three',
'',
].join('\n'));
// expect(await getLogContent()).toBe([
// '2020-01-01 00:00:00: testing: one',
// '2020-01-01 00:00:00: testing: two',
// '2020-01-01 00:00:00: testing: three',
// '',
// ].join('\n'));

expect(logger.keptLines).toEqual([
'2020-01-01 00:00:00: testing: two',
'2020-01-01 00:00:00: testing: three',
]);
// expect(logger.keptLines).toEqual([
// '2020-01-01 00:00:00: testing: two',
// '2020-01-01 00:00:00: testing: three',
// ]);

jest.useRealTimers();
});
// jest.useRealTimers();
// });

});
27 changes: 6 additions & 21 deletions packages/utils/Logger.ts
Expand Up @@ -78,8 +78,6 @@ class Logger {
private level_: LogLevel = LogLevel.Info;
private lastDbCleanup_: number = Date.now();
private enabled_ = true;
private keptLineCount_ = 0;
private keptLines_: string[] = [];

public static fsDriver() {
if (!Logger.fsDriver_) Logger.fsDriver_ = dummyFsDriver;
Expand All @@ -94,18 +92,6 @@ class Logger {
this.enabled_ = v;
}

public get keptLineCount() {
return this.keptLineCount_;
}

public set keptLineCount(v: number) {
this.keptLineCount_ = v;
}

public get keptLines() {
return this.keptLines_;
}

public status(): string {
const output: string[] = [];
output.push(`Enabled: ${this.enabled}`);
Expand All @@ -118,6 +104,12 @@ class Logger {
this.globalLogger_ = logger;
}

public logFilePath() {
const fileTarget = this.targets().find(t => t.type === TargetType.File);
if (!fileTarget) return '';
return fileTarget.path || '';
}

public static get globalLogger(): Logger {
if (!this.globalLogger_) {
// The global logger normally is initialized early, so we shouldn't
Expand Down Expand Up @@ -361,13 +353,6 @@ class Logger {
target.database.transactionExecBatch(queries);
}
}

if (this.keptLineCount) {
if (!logLine) logLine = this.logInfoToString(level, prefix, ...object);
this.keptLines_.push(logLine);
const toDelete = this.keptLines_.length - this.keptLineCount;
if (toDelete >= 0) this.keptLines_.splice(0, toDelete);
}
}

// For tests
Expand Down
5 changes: 4 additions & 1 deletion packages/utils/bytes.ts
@@ -1,4 +1,7 @@
// eslint-disable-next-line import/prefer-default-export
export const KB = 1024;
export const MB = KB * KB;
export const GB = KB * MB;

export const bytesToHuman = (bytes: number) => {
const units = ['Bytes', 'KB', 'MB', 'GB'];
let unitIndex = 0;
Expand Down
1 change: 1 addition & 0 deletions packages/utils/package.json
Expand Up @@ -5,6 +5,7 @@
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/utils",
"exports": {
".": "./dist/index.js",
"./bytes": "./dist/bytes.js",
"./dom": "./dist/dom.js",
"./env": "./dist/env.js",
"./object": "./dist/object.js",
Expand Down

0 comments on commit c5dfa4c

Please sign in to comment.