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);
|
||||
|
||||
bridge().setLogFilePath(Logger.globalLogger.logFilePath());
|
||||
|
||||
await this.applySettingsSideEffects();
|
||||
|
||||
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 { 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;
|
||||
@ -38,6 +41,7 @@ export class Bridge {
|
||||
private rootProfileDir_: string;
|
||||
private appName_: string;
|
||||
private appId_: string;
|
||||
private logFilePath_ = '';
|
||||
|
||||
private extraAllowedExtensions_: string[] = [];
|
||||
private onAllowedExtensionsChangeListener_: OnAllowedExtensionsChange = ()=>{};
|
||||
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
// });
|
||||
|
||||
});
|
||||
|
@ -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;
|
||||
@ -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}`);
|
||||
@ -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
|
||||
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user