mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Attach log to crash dump when the application crashes
This commit is contained in:
parent
74bc9b36aa
commit
c5dfa4c055
@ -390,6 +390,8 @@ class Application extends BaseApplication {
|
|||||||
|
|
||||||
argv = await super.start(argv, startOptions);
|
argv = await super.start(argv, startOptions);
|
||||||
|
|
||||||
|
bridge().setLogFilePath(Logger.globalLogger.logFilePath());
|
||||||
|
|
||||||
await this.applySettingsSideEffects();
|
await this.applySettingsSideEffects();
|
||||||
|
|
||||||
if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) {
|
if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) {
|
||||||
|
@ -6,11 +6,14 @@ import { dirname, toSystemSlashes } from '@joplin/lib/path-utils';
|
|||||||
import { fileUriToPath } from '@joplin/utils/url';
|
import { fileUriToPath } from '@joplin/utils/url';
|
||||||
import { urlDecode } from '@joplin/lib/string-utils';
|
import { urlDecode } from '@joplin/lib/string-utils';
|
||||||
import * as Sentry from '@sentry/electron/main';
|
import * as Sentry from '@sentry/electron/main';
|
||||||
|
import { ErrorEvent } from '@sentry/types/types';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import { msleep } from '@joplin/utils/time';
|
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 { extname, normalize } from 'path';
|
||||||
import isSafeToOpen from './utils/isSafeToOpen';
|
import isSafeToOpen from './utils/isSafeToOpen';
|
||||||
|
import { closeSync, openSync, readSync, statSync } from 'fs';
|
||||||
|
import { KB } from '@joplin/utils/bytes';
|
||||||
|
|
||||||
interface LastSelectedPath {
|
interface LastSelectedPath {
|
||||||
file: string;
|
file: string;
|
||||||
@ -38,6 +41,7 @@ export class Bridge {
|
|||||||
private rootProfileDir_: string;
|
private rootProfileDir_: string;
|
||||||
private appName_: string;
|
private appName_: string;
|
||||||
private appId_: string;
|
private appId_: string;
|
||||||
|
private logFilePath_ = '';
|
||||||
|
|
||||||
private extraAllowedExtensions_: string[] = [];
|
private extraAllowedExtensions_: string[] = [];
|
||||||
private onAllowedExtensionsChangeListener_: OnAllowedExtensionsChange = ()=>{};
|
private onAllowedExtensionsChangeListener_: OnAllowedExtensionsChange = ()=>{};
|
||||||
@ -56,12 +60,53 @@ export class Bridge {
|
|||||||
this.sentryInit();
|
this.sentryInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setLogFilePath(v: string) {
|
||||||
|
this.logFilePath_ = v;
|
||||||
|
}
|
||||||
|
|
||||||
private sentryInit() {
|
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 = {
|
const options: Sentry.ElectronMainOptions = {
|
||||||
beforeSend: event => {
|
beforeSend: (event, hint) => {
|
||||||
try {
|
try {
|
||||||
|
const logAttachment = getLogAttachment();
|
||||||
|
if (logAttachment) hint.attachments = [logAttachment];
|
||||||
const date = (new Date()).toISOString().replace(/[:-]/g, '').split('.')[0];
|
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) {
|
} catch (error) {
|
||||||
// Ignore the error since we can't handle it here
|
// Ignore the error since we can't handle it here
|
||||||
}
|
}
|
||||||
|
@ -54,36 +54,33 @@ describe('Logger', () => {
|
|||||||
'',
|
'',
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
// Shouldn't have kept any line, since keptLineCount was not set
|
|
||||||
expect(logger.keptLines).toEqual([]);
|
|
||||||
|
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should keep the last lines', async () => {
|
// it('should keep the last lines', async () => {
|
||||||
jest.useFakeTimers().setSystemTime(new Date('2020-01-01'));
|
// jest.useFakeTimers().setSystemTime(new Date('2020-01-01'));
|
||||||
|
|
||||||
const logger = createLogger();
|
// const logger = createLogger();
|
||||||
logger.keptLineCount = 2;
|
// logger.keptLineCount = 2;
|
||||||
logger.info('one');
|
// logger.info('one');
|
||||||
logger.info('two');
|
// logger.info('two');
|
||||||
logger.info('three');
|
// logger.info('three');
|
||||||
|
|
||||||
await logger.waitForFileWritesToComplete_();
|
// await logger.waitForFileWritesToComplete_();
|
||||||
|
|
||||||
expect(await getLogContent()).toBe([
|
// expect(await getLogContent()).toBe([
|
||||||
'2020-01-01 00:00:00: testing: one',
|
// '2020-01-01 00:00:00: testing: one',
|
||||||
'2020-01-01 00:00:00: testing: two',
|
// '2020-01-01 00:00:00: testing: two',
|
||||||
'2020-01-01 00:00:00: testing: three',
|
// '2020-01-01 00:00:00: testing: three',
|
||||||
'',
|
// '',
|
||||||
].join('\n'));
|
// ].join('\n'));
|
||||||
|
|
||||||
expect(logger.keptLines).toEqual([
|
// expect(logger.keptLines).toEqual([
|
||||||
'2020-01-01 00:00:00: testing: two',
|
// '2020-01-01 00:00:00: testing: two',
|
||||||
'2020-01-01 00:00:00: testing: three',
|
// '2020-01-01 00:00:00: testing: three',
|
||||||
]);
|
// ]);
|
||||||
|
|
||||||
jest.useRealTimers();
|
// jest.useRealTimers();
|
||||||
});
|
// });
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -78,8 +78,6 @@ class Logger {
|
|||||||
private level_: LogLevel = LogLevel.Info;
|
private level_: LogLevel = LogLevel.Info;
|
||||||
private lastDbCleanup_: number = Date.now();
|
private lastDbCleanup_: number = Date.now();
|
||||||
private enabled_ = true;
|
private enabled_ = true;
|
||||||
private keptLineCount_ = 0;
|
|
||||||
private keptLines_: string[] = [];
|
|
||||||
|
|
||||||
public static fsDriver() {
|
public static fsDriver() {
|
||||||
if (!Logger.fsDriver_) Logger.fsDriver_ = dummyFsDriver;
|
if (!Logger.fsDriver_) Logger.fsDriver_ = dummyFsDriver;
|
||||||
@ -94,18 +92,6 @@ class Logger {
|
|||||||
this.enabled_ = v;
|
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 {
|
public status(): string {
|
||||||
const output: string[] = [];
|
const output: string[] = [];
|
||||||
output.push(`Enabled: ${this.enabled}`);
|
output.push(`Enabled: ${this.enabled}`);
|
||||||
@ -118,6 +104,12 @@ class Logger {
|
|||||||
this.globalLogger_ = 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 {
|
public static get globalLogger(): Logger {
|
||||||
if (!this.globalLogger_) {
|
if (!this.globalLogger_) {
|
||||||
// The global logger normally is initialized early, so we shouldn't
|
// The global logger normally is initialized early, so we shouldn't
|
||||||
@ -361,13 +353,6 @@ class Logger {
|
|||||||
target.database.transactionExecBatch(queries);
|
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
|
// For tests
|
||||||
|
@ -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) => {
|
export const bytesToHuman = (bytes: number) => {
|
||||||
const units = ['Bytes', 'KB', 'MB', 'GB'];
|
const units = ['Bytes', 'KB', 'MB', 'GB'];
|
||||||
let unitIndex = 0;
|
let unitIndex = 0;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/utils",
|
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/utils",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/index.js",
|
".": "./dist/index.js",
|
||||||
|
"./bytes": "./dist/bytes.js",
|
||||||
"./dom": "./dist/dom.js",
|
"./dom": "./dist/dom.js",
|
||||||
"./env": "./dist/env.js",
|
"./env": "./dist/env.js",
|
||||||
"./object": "./dist/object.js",
|
"./object": "./dist/object.js",
|
||||||
|
Loading…
Reference in New Issue
Block a user