Skip to content

Commit c5dfa4c

Browse files
committed
Desktop: Attach log to crash dump when the application crashes
1 parent 74bc9b3 commit c5dfa4c

File tree

6 files changed

+81
-48
lines changed

6 files changed

+81
-48
lines changed

packages/app-desktop/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,8 @@ class Application extends BaseApplication {
390390

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

393+
bridge().setLogFilePath(Logger.globalLogger.logFilePath());
394+
393395
await this.applySettingsSideEffects();
394396

395397
if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) {

packages/app-desktop/bridge.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import { dirname, toSystemSlashes } from '@joplin/lib/path-utils';
66
import { fileUriToPath } from '@joplin/utils/url';
77
import { urlDecode } from '@joplin/lib/string-utils';
88
import * as Sentry from '@sentry/electron/main';
9+
import { ErrorEvent } from '@sentry/types/types';
910
import { homedir } from 'os';
1011
import { msleep } from '@joplin/utils/time';
11-
import { pathExists, writeFileSync } from 'fs-extra';
12+
import { pathExists, pathExistsSync, writeFileSync } from 'fs-extra';
1213
import { extname, normalize } from 'path';
1314
import isSafeToOpen from './utils/isSafeToOpen';
15+
import { closeSync, openSync, readSync, statSync } from 'fs';
16+
import { KB } from '@joplin/utils/bytes';
1417

1518
interface LastSelectedPath {
1619
file: string;
@@ -38,6 +41,7 @@ export class Bridge {
3841
private rootProfileDir_: string;
3942
private appName_: string;
4043
private appId_: string;
44+
private logFilePath_ = '';
4145

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

63+
public setLogFilePath(v: string) {
64+
this.logFilePath_ = v;
65+
}
66+
5967
private sentryInit() {
68+
const getLogLines = () => {
69+
try {
70+
if (!this.logFilePath_ || !pathExistsSync(this.logFilePath_)) return '';
71+
const { size } = statSync(this.logFilePath_);
72+
if (!size) return '';
73+
74+
const bytesToRead = Math.min(size, 100 * KB);
75+
const handle = openSync(this.logFilePath_, 'r');
76+
const position = size - bytesToRead;
77+
const buffer = Buffer.alloc(bytesToRead);
78+
readSync(handle, buffer, 0, bytesToRead, position);
79+
closeSync(handle);
80+
return buffer.toString('utf-8');
81+
} catch (error) {
82+
// Can't do anything in this context
83+
return '';
84+
}
85+
};
86+
87+
const getLogAttachment = () => {
88+
const lines = getLogLines();
89+
if (!lines) return null;
90+
return { filename: 'joplin-log.txt', data: lines };
91+
};
92+
6093
const options: Sentry.ElectronMainOptions = {
61-
beforeSend: event => {
94+
beforeSend: (event, hint) => {
6295
try {
96+
const logAttachment = getLogAttachment();
97+
if (logAttachment) hint.attachments = [logAttachment];
6398
const date = (new Date()).toISOString().replace(/[:-]/g, '').split('.')[0];
64-
writeFileSync(`${homedir()}/joplin_crash_dump_${date}.json`, JSON.stringify(event, null, '\t'), 'utf-8');
99+
100+
interface ErrorEventWithLog extends ErrorEvent {
101+
log: string[];
102+
}
103+
104+
const errorEventWithLog: ErrorEventWithLog = {
105+
...event,
106+
log: logAttachment ? logAttachment.data.trim().split('\n') : [],
107+
};
108+
109+
writeFileSync(`${homedir()}/joplin_crash_dump_${date}.json`, JSON.stringify(errorEventWithLog, null, '\t'), 'utf-8');
65110
} catch (error) {
66111
// Ignore the error since we can't handle it here
67112
}

packages/utils/Logger.test.ts

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,36 +54,33 @@ describe('Logger', () => {
5454
'',
5555
].join('\n'));
5656

57-
// Shouldn't have kept any line, since keptLineCount was not set
58-
expect(logger.keptLines).toEqual([]);
59-
6057
jest.useRealTimers();
6158
});
6259

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

66-
const logger = createLogger();
67-
logger.keptLineCount = 2;
68-
logger.info('one');
69-
logger.info('two');
70-
logger.info('three');
63+
// const logger = createLogger();
64+
// logger.keptLineCount = 2;
65+
// logger.info('one');
66+
// logger.info('two');
67+
// logger.info('three');
7168

72-
await logger.waitForFileWritesToComplete_();
69+
// await logger.waitForFileWritesToComplete_();
7370

74-
expect(await getLogContent()).toBe([
75-
'2020-01-01 00:00:00: testing: one',
76-
'2020-01-01 00:00:00: testing: two',
77-
'2020-01-01 00:00:00: testing: three',
78-
'',
79-
].join('\n'));
71+
// expect(await getLogContent()).toBe([
72+
// '2020-01-01 00:00:00: testing: one',
73+
// '2020-01-01 00:00:00: testing: two',
74+
// '2020-01-01 00:00:00: testing: three',
75+
// '',
76+
// ].join('\n'));
8077

81-
expect(logger.keptLines).toEqual([
82-
'2020-01-01 00:00:00: testing: two',
83-
'2020-01-01 00:00:00: testing: three',
84-
]);
78+
// expect(logger.keptLines).toEqual([
79+
// '2020-01-01 00:00:00: testing: two',
80+
// '2020-01-01 00:00:00: testing: three',
81+
// ]);
8582

86-
jest.useRealTimers();
87-
});
83+
// jest.useRealTimers();
84+
// });
8885

8986
});

packages/utils/Logger.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ class Logger {
7878
private level_: LogLevel = LogLevel.Info;
7979
private lastDbCleanup_: number = Date.now();
8080
private enabled_ = true;
81-
private keptLineCount_ = 0;
82-
private keptLines_: string[] = [];
8381

8482
public static fsDriver() {
8583
if (!Logger.fsDriver_) Logger.fsDriver_ = dummyFsDriver;
@@ -94,18 +92,6 @@ class Logger {
9492
this.enabled_ = v;
9593
}
9694

97-
public get keptLineCount() {
98-
return this.keptLineCount_;
99-
}
100-
101-
public set keptLineCount(v: number) {
102-
this.keptLineCount_ = v;
103-
}
104-
105-
public get keptLines() {
106-
return this.keptLines_;
107-
}
108-
10995
public status(): string {
11096
const output: string[] = [];
11197
output.push(`Enabled: ${this.enabled}`);
@@ -118,6 +104,12 @@ class Logger {
118104
this.globalLogger_ = logger;
119105
}
120106

107+
public logFilePath() {
108+
const fileTarget = this.targets().find(t => t.type === TargetType.File);
109+
if (!fileTarget) return '';
110+
return fileTarget.path || '';
111+
}
112+
121113
public static get globalLogger(): Logger {
122114
if (!this.globalLogger_) {
123115
// The global logger normally is initialized early, so we shouldn't
@@ -361,13 +353,6 @@ class Logger {
361353
target.database.transactionExecBatch(queries);
362354
}
363355
}
364-
365-
if (this.keptLineCount) {
366-
if (!logLine) logLine = this.logInfoToString(level, prefix, ...object);
367-
this.keptLines_.push(logLine);
368-
const toDelete = this.keptLines_.length - this.keptLineCount;
369-
if (toDelete >= 0) this.keptLines_.splice(0, toDelete);
370-
}
371356
}
372357

373358
// For tests

packages/utils/bytes.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// eslint-disable-next-line import/prefer-default-export
1+
export const KB = 1024;
2+
export const MB = KB * KB;
3+
export const GB = KB * MB;
4+
25
export const bytesToHuman = (bytes: number) => {
36
const units = ['Bytes', 'KB', 'MB', 'GB'];
47
let unitIndex = 0;

packages/utils/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/utils",
66
"exports": {
77
".": "./dist/index.js",
8+
"./bytes": "./dist/bytes.js",
89
"./dom": "./dist/dom.js",
910
"./env": "./dist/env.js",
1011
"./object": "./dist/object.js",

0 commit comments

Comments
 (0)