Merge pull request #3145 from Bilb/fix/logs-from-ui-empty

fix: bunyan logs rotating file are broken, don't use them
pull/3173/head
Audric Ackermann 8 months ago committed by GitHub
commit e83c047053
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -95,6 +95,7 @@
"closedGroupInviteSuccessTitle": "Group Invitation Succeeded",
"closedGroupInviteSuccessTitlePlural": "Group Invitations Completed",
"closedGroupMaxSize": "A group cannot have more than 100 members",
"commitHashDesktop": "Commit Hash: $hash$",
"confirm": "Confirm",
"confirmNewPassword": "Confirm new password",
"confirmPassword": "Confirm password",
@ -503,6 +504,7 @@
"startInTrayTitle": "Keep in System Tray",
"support": "Support",
"surveyTitle": "We'd Love Your Feedback",
"systemInformationDesktop": "System Information: $information$",
"termsOfService": "Terms of Service",
"themesSettingTitle": "Themes",
"theyChangedTheTimer": "<b>$name$</b> has set messages to disappear <b>$time$</b> after they have been <b>$mode$</b>",

@ -1,28 +0,0 @@
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none';
child-src 'self';
connect-src 'self' https: wss:;
font-src 'self';
form-action 'self';
frame-src 'none';
img-src 'self' blob: data:;
media-src 'self' blob:;
object-src 'none';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';"
/>
<!-- Load first to prevent font swapping on start -->
<link href="stylesheets/fonts.css" rel="stylesheet" type="text/css" />
<link href="stylesheets/dist/manifest.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="root"></div>
</body>
<script type="text/javascript">
require('./ts/mains/debug_log_start.js');
</script>
</html>

@ -1,35 +0,0 @@
/* global window */
/* eslint-disable @typescript-eslint/no-var-requires */
const { ipcRenderer } = require('electron');
const url = require('url');
const os = require('os');
const i18n = require('./ts/util/i18n');
const config = url.parse(window.location.toString(), true).query;
const { locale } = config;
const localeMessages = ipcRenderer.sendSync('locale-data');
window._ = require('lodash');
window.getVersion = () => config.version;
window.theme = config.theme;
window.i18n = i18n.setupi18n(locale, localeMessages);
// got.js appears to need this to successfully submit debug logs to the cloud
window.nodeSetImmediate = setImmediate;
window.getNodeVersion = () => config.node_version;
window.getEnvironment = () => config.environment;
require('./ts/util/logging');
window.getOSRelease = () =>
`${os.type()} ${os.release()}, Node.js ${config.node_version} ${os.platform()} ${os.arch()}`;
window.getCommitHash = () => config.commitHash;
window.closeDebugLog = () => ipcRenderer.send('close-debug-log');
window.saveLog = logText => ipcRenderer.send('save-debug-log', logText);

@ -92,7 +92,6 @@
"electron-updater": "^4.2.2",
"emoji-mart": "^5.5.2",
"filesize": "3.6.1",
"firstline": "1.2.1",
"focus-trap-react": "^10.2.3",
"framer-motion": "^11.0.3",
"fs-extra": "9.0.0",
@ -126,7 +125,6 @@
"react-toastify": "^6.0.9",
"react-use": "^17.5.0",
"react-virtualized": "^9.22.4",
"read-last-lines-ts": "^1.2.1",
"redux": "4.2.1",
"redux-logger": "3.0.6",
"redux-persist": "^6.0.0",
@ -158,7 +156,6 @@
"@types/dompurify": "^2.0.0",
"@types/electron-localshortcut": "^3.1.0",
"@types/filesize": "3.6.0",
"@types/firstline": "^2.0.2",
"@types/fs-extra": "5.0.5",
"@types/jsdom": "^21.1.6",
"@types/libsodium-wrappers-sumo": "^0.7.5",
@ -300,7 +297,6 @@
"background.html",
"about.html",
"password.html",
"debug_log.html",
"_locales/**",
"mnemonic_languages/**",
"protos/*",
@ -314,7 +310,6 @@
"preload.js",
"about_preload.js",
"settings_preload.js",
"debug_log_preload.js",
"password_preload.js",
"images/**",
"fonts/*",

@ -4,6 +4,7 @@ const { Storage } = require('./ts/util/storage');
const { isTestNet, isTestIntegration } = require('./ts/shared/env_vars');
const os = require('os');
const url = require('url');
const _ = require('lodash');
@ -26,6 +27,9 @@ window.getAppInstance = () => configAny.appInstance;
window.getVersion = () => configAny.version;
window.getCommitHash = () => configAny.commitHash;
window.getNodeVersion = () => configAny.node_version;
window.getOSRelease = () =>
`${os.type()} ${os.release()}, Node.js ${config.node_version} ${os.platform()} ${os.arch()}`;
window.saveLog = additionalText => ipcRenderer.send('save-debug-log', additionalText);
window.sessionFeatureFlags = {
useOnionRequests: true,

@ -54,8 +54,10 @@ export const AboutView = () => {
}
const versionInfo = `v${window.getVersion()}`;
const commitInfo = `Commit ${window.getCommitHash()}` || '';
const osInfo = `${window.getOSRelease()}`;
const systemInfo = window.i18n('systemInformationDesktop', [window.getOSRelease()]);
const commitInfo = window.i18n('commitHashDesktop', [
window.getCommitHash() || window.i18n('unknown'),
]);
useEffect(() => {
if (window.theme) {
@ -92,12 +94,16 @@ export const AboutView = () => {
text={versionInfo}
buttonType={SessionButtonType.Simple}
/>
<CopyToClipboardButton
className="os"
text={systemInfo}
buttonType={SessionButtonType.Simple}
/>
<CopyToClipboardButton
className="commitHash"
text={commitInfo}
buttonType={SessionButtonType.Simple}
/>
<CopyToClipboardButton className="os" text={osInfo} buttonType={SessionButtonType.Simple} />
{environmentStates.length ? (
<CopyToClipboardButton
className="environment"

@ -1,142 +0,0 @@
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import { SessionTheme } from '../themes/SessionTheme';
import { switchThemeTo } from '../themes/switchTheme';
import { fetchNodeLog } from '../util/logging';
import { SessionButton, SessionButtonType } from './basic/SessionButton';
import { SessionIconButton } from './icon';
import { CopyToClipboardButton } from './buttons';
import { Flex } from './basic/Flex';
const StyledContent = styled.div`
background-color: var(--modal-background-content-color);
color: var(--modal-text-color);
font-family: var(--font-default);
display: flex;
flex-direction: column;
padding: 20px;
height: 100%;
.session-button {
margin: 1rem auto 1rem 0;
padding: 1rem;
width: fit-content;
}
.session-icon-button {
float: right;
}
h1 {
color: var(--modal-text-color);
}
textarea {
flex-grow: 1;
width: 100%;
box-sizing: border-box;
padding: var(--margins-md);
background-color: var(--input-background-color);
color: var(--input-text-color);
border: 2px solid var(--border-color);
border-radius: 4px;
resize: none;
min-height: 100px;
font-family: var(--font-debug);
font-size: 12px;
line-height: 18px;
}
`;
const DebugLogTextArea = (props: { content: string }) => {
return <textarea spellCheck="false" rows={10} value={props.content} style={{ height: '100%' }} />;
};
const DebugLogButtons = (props: { content: string }) => {
return (
<Flex
container={true}
width={'fit-content'}
justifyContent={'flex-start'}
alignItems={'center'}
flexGap="var(--margins-md)"
className="buttons"
>
<SessionButton
text={window.i18n('saveLogToDesktop')}
buttonType={SessionButtonType.Simple}
onClick={() => {
if (props.content.length <= 20) {
// loading
return;
}
(window as any).saveLog(props.content);
}}
/>
<CopyToClipboardButton
copyContent={props.content}
buttonType={SessionButtonType.Simple}
hotkey={true}
/>
</Flex>
);
};
const DebugLogViewAndSave = () => {
const [content, setContent] = useState(window.i18n('loading'));
useEffect(() => {
const operatingSystemInfo = `Operating System ${window.getOSRelease()}`;
const commitHashInfo = window.getCommitHash() ? `Commit ${window.getCommitHash()}` : '';
// eslint-disable-next-line more/no-then
fetchNodeLog()
.then((text: any) => {
const debugLogWithSystemInfo = `${operatingSystemInfo} ${commitHashInfo} ${text}`;
setContent(debugLogWithSystemInfo);
})
// eslint-disable-next-line no-console
.catch(console.error);
}, []);
return (
<>
<DebugLogTextArea content={content} />
<DebugLogButtons content={content} />
</>
);
};
export const DebugLogView = () => {
useEffect(() => {
if (window.theme) {
void switchThemeTo({
theme: window.theme,
usePrimaryColor: true,
});
}
}, []);
return (
<SessionTheme runSetup={false}>
<StyledContent>
<div>
<SessionIconButton
aria-label="close debug log"
iconType="exit"
iconSize="medium"
onClick={() => {
window.closeDebugLog();
}}
/>
<h1> {window.i18n('debugLog')} </h1>
<p> {window.i18n('debugLogExplanation')}</p>
</div>
<DebugLogViewAndSave />
</StyledContent>
</SessionTheme>
);
};

@ -1,5 +1,3 @@
import { ipcRenderer } from 'electron';
import { useCallback } from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector';
@ -11,6 +9,7 @@ import { useSelectedIsGroupOrCommunity } from '../../../../state/selectors/selec
import { SpacerXS } from '../../../basic/Text';
import { SessionIcon, SessionIconType } from '../../../icon';
import { ExpireTimer } from '../../ExpireTimer';
import { saveLogToDesktop } from '../../../../util/logging';
type Props = {
messageId: string;
@ -228,9 +227,6 @@ const MessageStatusRead = ({
};
const MessageStatusError = ({ dataTestId }: Omit<Props, 'isDetailView'>) => {
const showDebugLog = useCallback(() => {
ipcRenderer.send('show-debug-log');
}, []);
// when on error, we do not display the expire timer at all.
const isGroup = useSelectedIsGroupOrCommunity();
@ -238,7 +234,9 @@ const MessageStatusError = ({ dataTestId }: Omit<Props, 'isDetailView'>) => {
<MessageStatusContainer
data-testid={dataTestId}
data-testtype="failed"
onClick={showDebugLog}
onClick={() => {
void saveLogToDesktop();
}}
title={window.i18n('sendFailed')}
isIncoming={false}
isGroup={isGroup}

@ -1,5 +1,4 @@
import { format, formatDistanceStrict } from 'date-fns';
import { ipcRenderer } from 'electron';
import { isEmpty } from 'lodash';
import moment from 'moment';
@ -24,6 +23,7 @@ import { useSelectedConversationKey } from '../../../../../../state/selectors/se
import { Flex } from '../../../../../basic/Flex';
import { SpacerSM } from '../../../../../basic/Text';
import { CopyToClipboardIcon } from '../../../../../buttons';
import { saveLogToDesktop } from '../../../../../../util/logging';
export const MessageInfoLabel = styled.label<{ color?: string }>`
font-size: var(--font-size-lg);
@ -74,10 +74,6 @@ export const LabelWithInfo = (props: LabelWithInfoProps) => {
// Message timestamp format: "06:02 PM Tue, 15/11/2022"
const formatTimestamps = 'hh:mm A ddd, D/M/Y';
const showDebugLog = () => {
ipcRenderer.send('show-debug-log');
};
const DebugMessageInfo = ({ messageId }: { messageId: string }) => {
const convoId = useSelectedConversationKey();
const messageHash = useMessageHash(messageId);
@ -162,7 +158,9 @@ export const MessageInfo = ({ messageId, errors }: { messageId: string; errors:
label={`${window.i18n('error')}:`}
info={errorString || window.i18n('unknownError')}
dataColor={'var(--danger-color)'}
onClick={showDebugLog}
onClick={() => {
void saveLogToDesktop();
}}
/>
</>
)}

@ -1,15 +1,14 @@
import { ipcRenderer } from 'electron';
import { SessionButtonShape, SessionButtonType } from '../../basic/SessionButton';
import { SessionSettingButtonItem, SessionSettingsTitleWithLink } from '../SessionSettingListItem';
import { saveLogToDesktop } from '../../../util/logging';
export const SettingsCategoryHelp = () => {
return (
<>
<SessionSettingButtonItem
onClick={() => {
ipcRenderer.send('show-debug-log');
void saveLogToDesktop();
}}
buttonShape={SessionButtonShape.Square}
buttonType={SessionButtonType.Solid}

@ -1,6 +0,0 @@
import { createRoot } from 'react-dom/client';
import { DebugLogView } from '../components/DebugLogView';
const container = document.getElementById('root');
const root = createRoot(container!);
root.render(<DebugLogView />);

@ -20,6 +20,7 @@ import {
import crypto from 'crypto';
import fs from 'fs';
import { copyFile, appendFile } from 'node:fs/promises';
import os from 'os';
import path, { join } from 'path';
import { platform as osPlatform } from 'process';
@ -77,7 +78,7 @@ import { initAttachmentsChannel } from '../node/attachment_channel';
import * as updater from '../updater/index'; // checked - only node
import { ephemeralConfig } from '../node/config/ephemeral_config'; // checked - only node
import { getLogger, initializeLogger } from '../node/logging'; // checked - only node
import { getLoggerFilePath, getLogger, initializeLogger } from '../node/logging'; // checked - only node
import { createTemplate } from '../node/menu'; // checked - only node
import { installPermissionsHandler } from '../node/permissions'; // checked - only node
import { installFileHandler, installWebHandler } from '../node/protocol_filter'; // checked - only node
@ -648,79 +649,37 @@ async function showAbout() {
aboutWindow?.show();
}
let debugLogWindow: BrowserWindow | null = null;
async function showDebugLogWindow() {
if (debugLogWindow) {
debugLogWindow.show();
return;
}
if (!mainWindow) {
console.info('debug log needs mainwindow size to open');
return;
}
const theme = await getThemeFromMainWindow();
const size = mainWindow.getSize();
const options = {
width: Math.max(size[0] - 100, getDefaultWindowSize().minWidth),
height: Math.max(size[1] - 100, getDefaultWindowSize().minHeight),
resizable: true,
title: locale.messages.debugLog,
autoHideMenuBar: true,
backgroundColor: classicDark['--background-primary-color'],
shadow: true,
show: false,
modal: true,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: false,
contextIsolation: false,
preload: path.join(getAppRootPath(), 'debug_log_preload.js'),
nativeWindowOpen: true,
},
parent: mainWindow,
};
debugLogWindow = new BrowserWindow(options);
captureClicks(debugLogWindow);
await debugLogWindow.loadURL(prepareURL([getAppRootPath(), 'debug_log.html'], { theme }));
debugLogWindow.on('closed', () => {
debugLogWindow = null;
});
debugLogWindow.once('ready-to-show', () => {
debugLogWindow?.setBackgroundColor(classicDark['--background-primary-color']);
});
// see above: looks like sometimes ready-to-show is not fired by electron
debugLogWindow?.show();
}
async function saveDebugLog(_event: any, logText: any) {
async function saveDebugLog(_event: any, additionalInfo: string) {
const options: Electron.SaveDialogOptions = {
title: 'Save debug log',
defaultPath: path.join(app.getPath('desktop'), `session_debug_${Date.now()}.txt`),
defaultPath: path.join(
app.getPath('desktop'),
`session_debug_${new Date().toISOString().replace(/:/g, '_')}.txt`
),
properties: ['createDirectory'],
};
try {
const result = await dialog.showSaveDialog(options);
const outputPath = result.filePath;
console.info(`Trying to save logs to ${outputPath}`);
console.info(`[log] Trying to save logs to ${outputPath}`);
if (result === undefined || outputPath === undefined || outputPath === '') {
throw Error("User clicked Save button but didn't create a file");
}
fs.writeFile(outputPath, logText, err => {
if (err) {
throw Error(`${err}`);
}
console.info(`Saved log - ${outputPath}`);
});
const loggerFilePath = getLoggerFilePath();
if (!loggerFilePath) {
throw Error('No logger file path');
}
await copyFile(loggerFilePath, outputPath);
console.info(`[log] Copied logs to ${outputPath} from ${loggerFilePath}`);
// append any additional info
if (additionalInfo) {
await appendFile(outputPath, additionalInfo, { encoding: 'utf-8' });
console.info(`[log] Saved additional info to logs ${outputPath} from ${loggerFilePath}`);
}
} catch (err) {
console.error('Error saving debug log', err);
}
@ -840,7 +799,6 @@ function setupMenu() {
const { platform } = process;
const menuOptions = {
development,
showDebugLog: showDebugLogWindow,
showWindow,
showAbout,
openReleaseNotes,
@ -1080,13 +1038,6 @@ ipc.on('set-password', async (event, passPhrase, oldPhrase) => {
});
// Debug Log-related IPC calls
ipc.on('show-debug-log', showDebugLogWindow);
ipc.on('close-debug-log', () => {
if (debugLogWindow) {
debugLogWindow.close();
}
});
ipc.on('save-debug-log', saveDebugLog);
ipc.on('load-maxmind-data', async (event: IpcMainEvent) => {
try {

@ -13,7 +13,6 @@ import { Data } from '../data/data';
import { OpenGroupData } from '../data/opengroups';
import { SettingsKey } from '../data/settings-key';
import { MessageModel } from '../models/message';
import { deleteAllLogs } from '../node/logs';
import { queueAllCached } from '../receiver/receiver';
import { loadKnownBlindedKeys } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys';
import { getConversationController } from '../session/conversations';
@ -203,8 +202,6 @@ Storage.onready(async () => {
window.log.info(`New version detected: ${currentVersion}; previous: ${lastVersion}`);
await Data.cleanupOrphanedAttachments();
await deleteAllLogs();
}
const themeSetting = window.Events.getThemeSetting();

@ -7,15 +7,16 @@ import fs from 'fs';
import { app, ipcMain as ipc } from 'electron';
import Logger from 'bunyan';
import _ from 'lodash';
import firstline from 'firstline';
import { readLastLinesEnc } from 'read-last-lines-ts';
import rimraf from 'rimraf';
import { readFile } from 'fs-extra';
import { redactAll } from '../util/privacy';
const LEVELS = ['fatal', 'error', 'warn', 'info', 'debug', 'trace'];
let logger: Logger | undefined;
let loggerFilePath: string | undefined;
export type ConsoleCustom = typeof console & {
_log: (...args: any) => void;
_warn: (...args: any) => void;
@ -28,68 +29,76 @@ export async function initializeLogger() {
}
const basePath = app.getPath('userData');
const logPath = path.join(basePath, 'logs');
fs.mkdirSync(logPath, { recursive: true });
const logFolder = path.join(basePath, 'logs');
const logFile = path.join(logFolder, 'log.log');
loggerFilePath = logFile;
return cleanupLogs(logPath).then(() => {
if (logger) {
return;
}
fs.mkdirSync(logFolder, { recursive: true });
const logFile = path.join(logPath, 'log.log');
logger = Logger.createLogger({
name: 'log',
streams: [
{
level: 'debug',
stream: process.stdout,
},
{
type: 'rotating-file',
path: logFile,
period: '1d',
count: 1,
},
],
});
await cleanupLogs(logFile, logFolder);
LEVELS.forEach(level => {
ipc.on(`log-${level}`, (_first, ...rest) => {
(logger as any)[level](...rest);
});
});
console.warn('[log] filepath', logFile);
ipc.on('fetch-log', event => {
fs.mkdirSync(logPath, { recursive: true });
console.info('fetching logs from logPath');
logger = Logger.createLogger({
name: 'log',
level: 'debug',
streams: [
{
stream: process.stdout,
},
{
path: logFile,
},
],
});
fetchLogFile(logPath).then(
data => {
event.sender.send('fetched-log', data);
},
error => {
logger?.error(`Problem loading log from disk: ${error.stack}`);
}
);
logger.level('debug');
// eslint-disable-next-line dot-notation
(logger as any)['warn']('app start: logger created'); // keep this so we always have restart indications in the app
LEVELS.forEach(level => {
ipc.on(`log-${level}`, (_first, ...rest) => {
(logger as any)[level](...rest);
});
});
// eslint-disable-next-line @typescript-eslint/no-misused-promises
ipc.on('delete-all-logs', async event => {
try {
await deleteAllLogs(logPath);
} catch (error) {
logger?.error(`Problem deleting all logs: ${error.stack}`);
ipc.on('fetch-log', event => {
if (!fs.existsSync(logFolder)) {
fs.mkdirSync(logFolder, { recursive: true });
}
console.info('[log] fetching logs from', logFile);
fetchLogFile(logFile).then(
data => {
event.sender.send('fetched-log', data);
},
error => {
logger?.error(`[log] Problem loading log from disk: ${error.stack}`);
}
);
});
event.sender.send('delete-all-logs-complete');
});
// eslint-disable-next-line @typescript-eslint/no-misused-promises
ipc.on('delete-all-logs', async event => {
try {
await deleteAllLogs(logFile);
} catch (error) {
logger?.error(`[log] Problem deleting all logs: ${error.stack}`);
}
event.sender.send('delete-all-logs-complete');
});
}
async function deleteAllLogs(logPath: string) {
export function getLoggerFilePath() {
return loggerFilePath;
}
async function deleteAllLogs(logFile: string) {
return new Promise((resolve, reject) => {
rimraf(
logPath,
logFile,
{
disableGlob: true,
},
@ -105,85 +114,36 @@ async function deleteAllLogs(logPath: string) {
});
}
async function cleanupLogs(logPath: string) {
async function cleanupLogs(logFile: string, logFolder: string) {
const now = new Date();
const earliestDate = new Date(
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - 6)
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - 2) // we keep 2 days worth of logs when we start the app and delete the rest
);
try {
const remaining = await eliminateOutOfDateFiles(logPath, earliestDate);
const files = _.filter(remaining, file => !file.start && file.end);
if (!files.length) {
return;
}
await eliminateOldEntries(files, earliestDate);
await eliminateOldEntries(logFile, earliestDate);
} catch (error) {
console.error('Error cleaning logs; deleting and starting over from scratch.', error.stack);
// delete and re-create the log directory
await deleteAllLogs(logPath);
fs.mkdirSync(logPath, { recursive: true });
console.error(
'[log] Error cleaning logs; deleting and starting over from scratch.',
error.stack
);
fs.mkdirSync(logFolder, { recursive: true });
}
}
function isLineAfterDate(line: string, date: Date) {
if (!line) {
return false;
}
async function eliminateOldEntries(logFile: string, date: Date) {
const earliest = date.getTime();
try {
const data = JSON.parse(line);
return new Date(data.time).getTime() > date.getTime();
} catch (e) {
console.log('error parsing log line', e.stack, line);
return false;
if (!fs.existsSync(logFile)) {
return;
}
}
async function eliminateOutOfDateFiles(logPath: string, date: Date) {
const files = fs.readdirSync(logPath);
const paths = files.map(file => path.join(logPath, file));
return Promise.all(
_.map(paths, target =>
Promise.all([firstline(target), readLastLinesEnc('utf8')(target, 2)]).then(results => {
const start = results[0];
const end = results[1].split('\n');
const file = {
path: target,
start: isLineAfterDate(start, date),
end:
isLineAfterDate(end[end.length - 1], date) ||
isLineAfterDate(end[end.length - 2], date),
};
if (!file.start && !file.end) {
fs.unlinkSync(file.path);
}
const lines = await fetchLog(logFile);
return file;
})
)
);
}
const recent = _.filter(lines, line => new Date(line.time).getTime() >= earliest);
const text = _.map(recent, line => JSON.stringify(line)).join('\n');
async function eliminateOldEntries(files: any, date: Date) {
const earliest = date.getTime();
return Promise.all(
_.map(files, file =>
fetchLog(file.path).then((lines: any) => {
const recent = _.filter(lines, line => new Date(line.time).getTime() >= earliest);
const text = _.map(recent, line => JSON.stringify(line)).join('\n');
fs.writeFileSync(file.path, `${text}\n`);
})
)
);
fs.writeFileSync(logFile, `${text}\n`);
}
export function getLogger() {
@ -194,56 +154,45 @@ export function getLogger() {
return logger;
}
async function fetchLog(logFile: string) {
return new Promise((resolve, reject) => {
fs.readFile(logFile, { encoding: 'utf8' }, (err, text) => {
if (err) {
reject(err);
return;
type LogEntry = { level: number; time: string; msg: string };
async function fetchLog(logFile: string): Promise<Array<LogEntry>> {
const text = await readFile(logFile, { encoding: 'utf8' });
const lines = _.compact(text.split('\n'));
const data = _.compact(
lines.map(line => {
try {
return _.pick(JSON.parse(line), ['level', 'time', 'msg']);
} catch (e) {
return null;
}
})
);
const lines = _.compact(text.split('\n'));
const data = _.compact(
lines.map(line => {
try {
return _.pick(JSON.parse(line), ['level', 'time', 'msg']);
} catch (e) {
return null;
}
})
);
resolve(data);
});
});
return data;
}
export async function fetchLogFile(logPath: string) {
async function fetchLogFile(logFile: string) {
// Check that the file exists locally
if (!fs.existsSync(logPath)) {
(console as ConsoleCustom)._log(
'Log folder not found while fetching its content. Quick! Creating it.'
);
fs.mkdirSync(logPath, { recursive: true });
if (!fs.existsSync(logFile)) {
throw new Error('Log folder not found while fetching its content');
}
const files = fs.readdirSync(logPath);
const paths = files.map(file => path.join(logPath, file));
// creating a manual log entry for the final log result
const now = new Date();
const fileListEntry = {
level: 30, // INFO
time: now.toJSON(),
msg: `Loaded this list of log files from logPath: ${files.join(', ')}`,
msg: `Loaded this from logfile: "${logFile}"`,
};
return Promise.all(paths.map(fetchLog)).then(results => {
const data = _.flatten(results);
const read = await fetchLog(logFile);
const data = _.flatten(read);
data.push(fileListEntry);
data.push(fileListEntry);
return _.sortBy(data, 'time');
});
return _.sortBy(data, 'time');
}
function logAtLevel(level: string, ...args: any) {

@ -7,7 +7,6 @@ export const createTemplate = (
openSupportPage: () => void;
platform: string;
showAbout: () => void;
showDebugLog: () => void;
showWindow: () => void;
},
messages: LocaleMessagesType
@ -16,8 +15,7 @@ export const createTemplate = (
throw new TypeError('`options.platform` must be a string');
}
const { openReleaseNotes, openSupportPage, platform, showAbout, showDebugLog, showWindow } =
options;
const { openReleaseNotes, openSupportPage, platform, showAbout, showWindow } = options;
const template = [
{
@ -94,13 +92,6 @@ export const createTemplate = (
{
type: 'separator',
},
{
label: messages.debugLog,
click: showDebugLog,
},
{
type: 'separator',
},
{
role: 'toggledevtools',
label: messages.viewMenuToggleDevTools,
@ -125,9 +116,6 @@ export const createTemplate = (
label: messages.goToReleaseNotes,
click: openReleaseNotes,
},
{
type: 'separator',
},
{
label: messages.goToSupportPage,
click: openSupportPage,

@ -21,7 +21,6 @@ type ThemeGlobals = {
'--font-default': string;
'--font-accent': string;
'--font-mono': string;
'--font-debug': string;
'--font-size-xl': string;
'--font-size-lg': string;
@ -160,7 +159,6 @@ export const THEME_GLOBALS: ThemeGlobals = {
'--font-default': 'Roboto',
'--font-accent': 'Loor',
'--font-mono': 'SpaceMono',
'--font-debug': "Monaco, Consolas, 'Courier New', Courier, monospace",
'--font-size-xl': '19px',
'--font-size-lg': '17px',

@ -95,6 +95,7 @@ export type LocalizerKeys =
| 'closedGroupInviteSuccessTitle'
| 'closedGroupInviteSuccessTitlePlural'
| 'closedGroupMaxSize'
| 'commitHashDesktop'
| 'confirm'
| 'confirmNewPassword'
| 'confirmPassword'
@ -503,6 +504,7 @@ export type LocalizerKeys =
| 'startedACall'
| 'support'
| 'surveyTitle'
| 'systemInformationDesktop'
| 'termsOfService'
| 'themesSettingTitle'
| 'theyChangedTheTimer'

@ -148,3 +148,17 @@ window.addEventListener('unhandledrejection', rejectionEvent => {
const errorInfo = error && error.stack ? error.stack : error;
window.log.error('Top-level unhandled promise rejection:', errorInfo);
});
export function saveLogToDesktop() {
const versionInfo = `v${window.getVersion()}`;
const systemInfo = `System Information: ${window.getOSRelease()}`;
const commitInfo = `Commit Hash: ${window.getCommitHash()}` || 'Unknown';
const debugLogWithSystemInfo = `
******************************************************************************
# Application Info
${versionInfo}
${systemInfo}
${commitInfo}
******************************************************************************`;
window.saveLog(debugLogWithSystemInfo);
}

2
ts/window.d.ts vendored

@ -82,6 +82,7 @@ declare global {
getCommitHash: () => string | undefined;
getVersion: () => string;
getOSRelease: () => string;
saveLog: (additionalText?: string) => void;
setAutoHideMenuBar: (val: boolean) => void;
setMenuBarVisibility: (val: boolean) => void;
contextMenuShown: boolean;
@ -98,7 +99,6 @@ declare global {
getOpengroupPruning: () => Promise<boolean>;
setOpengroupPruning: (val: boolean) => Promise<void>;
closeAbout: () => void;
closeDebugLog: () => void;
getAutoUpdateEnabled: () => boolean;
setAutoUpdateEnabled: (enabled: boolean) => void;
setZoomFactor: (newZoom: number) => void;

Loading…
Cancel
Save