Introduce buffered console logger
This commit is contained in:
parent
6dab8c70a1
commit
d1791ff6be
6 changed files with 67 additions and 47 deletions
|
@ -46,11 +46,11 @@ async function eraseDumps(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setup(getLogger: () => LoggerType): Promise<void> {
|
export function setup(getLogger: () => LoggerType, forceEnable = false): void {
|
||||||
const isEnabled = !isProduction(app.getVersion());
|
const isEnabled = !isProduction(app.getVersion()) || forceEnable;
|
||||||
|
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
getLogger().info('crashReporter: enabled');
|
getLogger().info(`crashReporter: ${forceEnable ? 'force ' : ''}enabled`);
|
||||||
crashReporter.start({ uploadToServer: false });
|
crashReporter.start({ uploadToServer: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
app/main.ts
27
app/main.ts
|
@ -44,7 +44,7 @@ import { createSupportUrl } from '../ts/util/createSupportUrl';
|
||||||
import { missingCaseError } from '../ts/util/missingCaseError';
|
import { missingCaseError } from '../ts/util/missingCaseError';
|
||||||
import { strictAssert } from '../ts/util/assert';
|
import { strictAssert } from '../ts/util/assert';
|
||||||
import { drop } from '../ts/util/drop';
|
import { drop } from '../ts/util/drop';
|
||||||
import { consoleLogger } from '../ts/util/consoleLogger';
|
import { createBufferedConsoleLogger } from '../ts/util/consoleLogger';
|
||||||
import type { ThemeSettingType } from '../ts/types/StorageUIKeys';
|
import type { ThemeSettingType } from '../ts/types/StorageUIKeys';
|
||||||
import { ThemeType } from '../ts/types/Util';
|
import { ThemeType } from '../ts/types/Util';
|
||||||
import * as Errors from '../ts/types/errors';
|
import * as Errors from '../ts/types/errors';
|
||||||
|
@ -135,6 +135,16 @@ let mainWindow: BrowserWindow | undefined;
|
||||||
let mainWindowCreated = false;
|
let mainWindowCreated = false;
|
||||||
let loadingWindow: BrowserWindow | undefined;
|
let loadingWindow: BrowserWindow | undefined;
|
||||||
|
|
||||||
|
// Create a buffered logger to hold our log lines until we fully initialize
|
||||||
|
// the logger in `app.on('ready')`
|
||||||
|
const consoleLogger = createBufferedConsoleLogger();
|
||||||
|
|
||||||
|
// These will be set after app fires the 'ready' event
|
||||||
|
let logger: LoggerType | undefined;
|
||||||
|
let preferredSystemLocales: Array<string> | undefined;
|
||||||
|
let resolvedTranslationsLocale: LocaleType | undefined;
|
||||||
|
let settingsChannel: SettingsChannel | undefined;
|
||||||
|
|
||||||
const activeWindows = new Set<BrowserWindow>();
|
const activeWindows = new Set<BrowserWindow>();
|
||||||
|
|
||||||
function getMainWindow() {
|
function getMainWindow() {
|
||||||
|
@ -170,6 +180,12 @@ const defaultWebPrefs = {
|
||||||
const DISABLE_GPU =
|
const DISABLE_GPU =
|
||||||
OS.isLinux() && !process.argv.some(arg => arg === '--enable-gpu');
|
OS.isLinux() && !process.argv.some(arg => arg === '--enable-gpu');
|
||||||
|
|
||||||
|
const FORCE_ENABLE_CRASH_REPORTS = process.argv.some(
|
||||||
|
arg => arg === '--enable-crash-reports'
|
||||||
|
);
|
||||||
|
|
||||||
|
setupCrashReports(getLogger, FORCE_ENABLE_CRASH_REPORTS);
|
||||||
|
|
||||||
function showWindow() {
|
function showWindow() {
|
||||||
if (!mainWindow) {
|
if (!mainWindow) {
|
||||||
return;
|
return;
|
||||||
|
@ -347,12 +363,6 @@ if (windowFromUserConfig) {
|
||||||
|
|
||||||
let menuOptions: CreateTemplateOptionsType | undefined;
|
let menuOptions: CreateTemplateOptionsType | undefined;
|
||||||
|
|
||||||
// These will be set after app fires the 'ready' event
|
|
||||||
let logger: LoggerType | undefined;
|
|
||||||
let preferredSystemLocales: Array<string> | undefined;
|
|
||||||
let resolvedTranslationsLocale: LocaleType | undefined;
|
|
||||||
let settingsChannel: SettingsChannel | undefined;
|
|
||||||
|
|
||||||
function getLogger(): LoggerType {
|
function getLogger(): LoggerType {
|
||||||
if (!logger) {
|
if (!logger) {
|
||||||
console.warn('getLogger: Logger not yet initialized!');
|
console.warn('getLogger: Logger not yet initialized!');
|
||||||
|
@ -1673,7 +1683,8 @@ app.on('ready', async () => {
|
||||||
|
|
||||||
logger = await logging.initialize(getMainWindow);
|
logger = await logging.initialize(getMainWindow);
|
||||||
|
|
||||||
await setupCrashReports(getLogger);
|
// Write buffered information into newly created logger.
|
||||||
|
consoleLogger.writeBufferInto(logger);
|
||||||
|
|
||||||
if (!resolvedTranslationsLocale) {
|
if (!resolvedTranslationsLocale) {
|
||||||
preferredSystemLocales = resolveCanonicalLocales(
|
preferredSystemLocales = resolveCanonicalLocales(
|
||||||
|
|
|
@ -39,7 +39,6 @@ const getHeader = (
|
||||||
capabilities,
|
capabilities,
|
||||||
remoteConfig,
|
remoteConfig,
|
||||||
statistics,
|
statistics,
|
||||||
appMetrics,
|
|
||||||
user,
|
user,
|
||||||
}: Omit<FetchLogIpcData, 'logEntries'>,
|
}: Omit<FetchLogIpcData, 'logEntries'>,
|
||||||
nodeVersion: string,
|
nodeVersion: string,
|
||||||
|
@ -57,29 +56,6 @@ const getHeader = (
|
||||||
headerSection('User info', user),
|
headerSection('User info', user),
|
||||||
headerSection('Capabilities', capabilities),
|
headerSection('Capabilities', capabilities),
|
||||||
headerSection('Remote config', remoteConfig),
|
headerSection('Remote config', remoteConfig),
|
||||||
headerSection(
|
|
||||||
'Metrics',
|
|
||||||
appMetrics.reduce((acc, stats, index) => {
|
|
||||||
const {
|
|
||||||
type = '?',
|
|
||||||
serviceName = '?',
|
|
||||||
name = '?',
|
|
||||||
cpu,
|
|
||||||
memory,
|
|
||||||
} = stats;
|
|
||||||
|
|
||||||
const processId = `${index}:${type}/${serviceName}/${name}`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
[processId]:
|
|
||||||
`cpuUsage=${cpu.percentCPUUsage.toFixed(2)} ` +
|
|
||||||
`wakeups=${cpu.idleWakeupsPerSecond} ` +
|
|
||||||
`workingMemory=${memory.workingSetSize} ` +
|
|
||||||
`peakWorkingMemory=${memory.peakWorkingSetSize}`,
|
|
||||||
};
|
|
||||||
}, {})
|
|
||||||
),
|
|
||||||
headerSection('Statistics', statistics),
|
headerSection('Statistics', statistics),
|
||||||
headerSectionTitle('Logs'),
|
headerSectionTitle('Logs'),
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
|
@ -26,7 +26,6 @@ import { CircularBuffer } from 'cirbuf';
|
||||||
|
|
||||||
import type { LoggerType } from '../types/Logging';
|
import type { LoggerType } from '../types/Logging';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
import * as durations from '../util/durations';
|
|
||||||
import { createRotatingPinoDest } from '../util/rotatingPinoDest';
|
import { createRotatingPinoDest } from '../util/rotatingPinoDest';
|
||||||
|
|
||||||
import * as log from './log';
|
import * as log from './log';
|
||||||
|
@ -64,13 +63,6 @@ export async function initialize(
|
||||||
const logPath = join(basePath, 'logs');
|
const logPath = join(basePath, 'logs');
|
||||||
mkdirSync(logPath, { recursive: true });
|
mkdirSync(logPath, { recursive: true });
|
||||||
|
|
||||||
let appMetrics = app.getAppMetrics();
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
// CPU stats are computed since the last call to `getAppMetrics`.
|
|
||||||
appMetrics = app.getAppMetrics();
|
|
||||||
}, 30 * durations.SECOND).unref();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await cleanupLogs(logPath);
|
await cleanupLogs(logPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -141,7 +133,6 @@ export async function initialize(
|
||||||
]);
|
]);
|
||||||
data = {
|
data = {
|
||||||
logEntries,
|
logEntries,
|
||||||
appMetrics,
|
|
||||||
...rest,
|
...rest,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -340,7 +331,7 @@ export function fetchLogs(logPath: string): Promise<Array<LogEntryType>> {
|
||||||
|
|
||||||
export const fetchAdditionalLogData = (
|
export const fetchAdditionalLogData = (
|
||||||
mainWindow: BrowserWindow
|
mainWindow: BrowserWindow
|
||||||
): Promise<Omit<FetchLogIpcData, 'logEntries' | 'appMetrics'>> =>
|
): Promise<Omit<FetchLogIpcData, 'logEntries'>> =>
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
mainWindow.webContents.send('additional-log-data-request');
|
mainWindow.webContents.send('additional-log-data-request');
|
||||||
ipc.once('additional-log-data-response', (_event, data) => {
|
ipc.once('additional-log-data-response', (_event, data) => {
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import pino from 'pino';
|
import pino from 'pino';
|
||||||
import type { ProcessMetric } from 'electron';
|
|
||||||
import { isRecord } from '../util/isRecord';
|
import { isRecord } from '../util/isRecord';
|
||||||
import { redactAll } from '../util/privacy';
|
import { redactAll } from '../util/privacy';
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
|
@ -16,7 +15,6 @@ export type FetchLogIpcData = {
|
||||||
remoteConfig: Record<string, unknown>;
|
remoteConfig: Record<string, unknown>;
|
||||||
statistics: Record<string, unknown>;
|
statistics: Record<string, unknown>;
|
||||||
user: Record<string, unknown>;
|
user: Record<string, unknown>;
|
||||||
appMetrics: ReadonlyArray<ProcessMetric>;
|
|
||||||
|
|
||||||
// We expect `logEntries` to be `Array<LogEntryType>`, but we don't validate that
|
// We expect `logEntries` to be `Array<LogEntryType>`, but we don't validate that
|
||||||
// upfront—we only validate it when we go to log each line. This improves the
|
// upfront—we only validate it when we go to log each line. This improves the
|
||||||
|
|
|
@ -24,4 +24,48 @@ export const consoleLogger: LoggerType = {
|
||||||
console.log(...args);
|
console.log(...args);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BufferedLoggerType = LoggerType & {
|
||||||
|
writeBufferInto(logger: LoggerType): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createBufferedConsoleLogger(): BufferedLoggerType {
|
||||||
|
type BufferEntryType = Readonly<{
|
||||||
|
level: keyof LoggerType;
|
||||||
|
args: Array<unknown>;
|
||||||
|
}>;
|
||||||
|
const buffer = new Array<BufferEntryType>();
|
||||||
|
return {
|
||||||
|
fatal(...args: Array<unknown>) {
|
||||||
|
buffer.push({ level: 'fatal', args });
|
||||||
|
console.error(...args);
|
||||||
|
},
|
||||||
|
error(...args: Array<unknown>) {
|
||||||
|
buffer.push({ level: 'error', args });
|
||||||
|
console.error(...args);
|
||||||
|
},
|
||||||
|
warn(...args: Array<unknown>) {
|
||||||
|
buffer.push({ level: 'warn', args });
|
||||||
|
console.warn(...args);
|
||||||
|
},
|
||||||
|
info(...args: Array<unknown>) {
|
||||||
|
buffer.push({ level: 'info', args });
|
||||||
|
console.info(...args);
|
||||||
|
},
|
||||||
|
debug(...args: Array<unknown>) {
|
||||||
|
buffer.push({ level: 'debug', args });
|
||||||
|
console.debug(...args);
|
||||||
|
},
|
||||||
|
trace(...args: Array<unknown>) {
|
||||||
|
buffer.push({ level: 'trace', args });
|
||||||
|
console.log(...args);
|
||||||
|
},
|
||||||
|
|
||||||
|
writeBufferInto(output) {
|
||||||
|
for (const { level, args } of buffer) {
|
||||||
|
output[level](...args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue