signal-desktop/js/logging.js

153 lines
3.5 KiB
JavaScript
Raw Normal View History

// Copyright 2017-2021 Signal Messenger, LLC
2020-10-30 20:34:04 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-env node */
/* eslint-disable no-console */
const electron = require('electron');
const _ = require('lodash');
const { uploadDebugLogs } = require('../ts/logging/debuglogs');
const Privacy = require('./modules/privacy');
2019-09-26 19:56:31 +00:00
const { createBatcher } = require('../ts/util/batcher');
const ipc = electron.ipcRenderer;
// Default Bunyan levels: https://github.com/trentm/node-bunyan#levels
// To make it easier to visually scan logs, we make all levels the same length
const BLANK_LEVEL = ' ';
const LEVELS = {
60: 'fatal',
50: 'error',
40: 'warn ',
30: 'info ',
20: 'debug',
10: 'trace',
};
// Backwards-compatible logging, simple strings and no level (defaulted to INFO)
function now() {
const date = new Date();
return date.toJSON();
}
// To avoid [Object object] in our log since console.log handles non-strings smoothly
function cleanArgsForIPC(args) {
2018-04-27 21:25:04 +00:00
const str = args.map(item => {
if (typeof item !== 'string') {
try {
return JSON.stringify(item);
2018-03-05 21:14:22 +00:00
} catch (error) {
return item;
}
}
return item;
});
return str.join(' ');
}
function log(...args) {
logAtLevel('info', 'INFO ', ...args);
}
if (window.console) {
console._log = console.log;
console.log = log;
}
// The mechanics of preparing a log for publish
function getHeader() {
Beta versions support: SxS support, in-app env/instance display (#1606) * Script for beta config; unique data dir, in-app env/type display To release a beta build, increment the version and add -beta-N to the end, then go through all the standard release activities. The prepare-build npm script then updates key bits of the package.json to ensure that the beta build can be installed alongside a production build. This includes a new name ('Signal Beta') and a different location for application data. Note: Beta builds can be installed alongside production builds. As part of this, a couple new bits of data are shown across the app: - Environment (development or test, not shown if production) - App Instance (disabled in production; used for multiple accounts) These are shown in: - The window title - both environment and app instance. You can tell beta builds because the app name, preceding these data bits, is different. - The about window - both environment and app instance. You can tell beta builds from the version number. - The header added to the debug log - just environment. The version number will tell us if it's a beta build, and app instance isn't helpful. * Turn on single-window mode in non-production modes Because it's really frightening when you see 'unable to read from db' errors in the console. * aply.sh: More instructions for initial setup and testing * Gruntfile: Get consistent with use of package.json datas * Linux: manually update desktop keys, since macros not available
2017-10-30 20:57:13 +00:00
let header = window.navigator.userAgent;
header += ` node/${window.getNodeVersion()}`;
header += ` env/${window.getEnvironment()}`;
Beta versions support: SxS support, in-app env/instance display (#1606) * Script for beta config; unique data dir, in-app env/type display To release a beta build, increment the version and add -beta-N to the end, then go through all the standard release activities. The prepare-build npm script then updates key bits of the package.json to ensure that the beta build can be installed alongside a production build. This includes a new name ('Signal Beta') and a different location for application data. Note: Beta builds can be installed alongside production builds. As part of this, a couple new bits of data are shown across the app: - Environment (development or test, not shown if production) - App Instance (disabled in production; used for multiple accounts) These are shown in: - The window title - both environment and app instance. You can tell beta builds because the app name, preceding these data bits, is different. - The about window - both environment and app instance. You can tell beta builds from the version number. - The header added to the debug log - just environment. The version number will tell us if it's a beta build, and app instance isn't helpful. * Turn on single-window mode in non-production modes Because it's really frightening when you see 'unable to read from db' errors in the console. * aply.sh: More instructions for initial setup and testing * Gruntfile: Get consistent with use of package.json datas * Linux: manually update desktop keys, since macros not available
2017-10-30 20:57:13 +00:00
return header;
}
function getLevel(level) {
const text = LEVELS[level];
if (!text) {
return BLANK_LEVEL;
}
return text.toUpperCase();
}
function formatLine(entry) {
return `${getLevel(entry.level)} ${entry.time} ${entry.msg}`;
}
function format(entries) {
return Privacy.redactAll(entries.map(formatLine).join('\n'));
}
function fetch() {
2018-04-27 21:25:04 +00:00
return new Promise(resolve => {
ipc.send('fetch-log');
ipc.on('fetched-log', (event, text) => {
const result = `${getHeader()}\n${format(text)}`;
resolve(result);
});
2018-03-08 18:45:22 +00:00
});
}
const publish = uploadDebugLogs;
// A modern logging interface for the browser
2019-09-26 19:56:31 +00:00
const env = window.getEnvironment();
const IS_PRODUCTION = env === 'production';
const ipcBatcher = createBatcher({
wait: 500,
2021-01-14 22:40:50 +00:00
maxSize: 500,
2019-09-26 19:56:31 +00:00
processBatch: items => {
ipc.send('batch-log', items);
},
});
// The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api
function logAtLevel(level, prefix, ...args) {
2019-09-26 19:56:31 +00:00
if (!IS_PRODUCTION) {
console._log(prefix, now(), ...args);
}
const str = cleanArgsForIPC(args);
const logText = Privacy.redactAll(str);
2019-09-26 19:56:31 +00:00
ipcBatcher.add({
timestamp: Date.now(),
level,
logText,
});
}
window.log = {
fatal: _.partial(logAtLevel, 'fatal', 'FATAL'),
error: _.partial(logAtLevel, 'error', 'ERROR'),
warn: _.partial(logAtLevel, 'warn', 'WARN '),
info: _.partial(logAtLevel, 'info', 'INFO '),
debug: _.partial(logAtLevel, 'debug', 'DEBUG'),
trace: _.partial(logAtLevel, 'trace', 'TRACE'),
fetch,
publish,
};
2018-03-05 21:14:22 +00:00
window.onerror = (message, script, line, col, error) => {
const errorInfo = error && error.stack ? error.stack : JSON.stringify(error);
window.log.error(`Top-level unhandled error: ${errorInfo}`);
};
2018-04-27 21:25:04 +00:00
window.addEventListener('unhandledrejection', rejectionEvent => {
const error = rejectionEvent.reason;
const errorString =
error && error.stack ? error.stack : JSON.stringify(error);
window.log.error(`Top-level unhandled promise rejection: ${errorString}`);
});