ffbcb4ecb5
An immediate response to the user request to see the log, and then we show the real data as soon as we've loaded it from disk. Changes: - the IPC exchange to get the log data is now async - the API to fetch the log on the client side now returns a Promise - in the main process, the only disk access done synchronoously is reading the contents of the log directory. The JSON parsing of the resultant log data is now split up into three chunks. - We only send three keys from each log item to the renderer process: msg, time, level. Previously we sent the entire log entry with extra keys: hostname, pid, name. FREEBIE
166 lines
3.8 KiB
JavaScript
166 lines
3.8 KiB
JavaScript
const electron = require('electron');
|
|
const bunyan = require('bunyan');
|
|
const _ = require('lodash');
|
|
|
|
|
|
const ipc = electron.ipcRenderer;
|
|
const PHONE_REGEX = /\+\d{7,12}(\d{3})/g;
|
|
const GROUP_REGEX = /(group\()([^)]+)(\))/g;
|
|
|
|
// 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 redactPhone(text) {
|
|
return text.replace(PHONE_REGEX, "+[REDACTED]$1");
|
|
}
|
|
|
|
function redactGroup(text) {
|
|
return text.replace(GROUP_REGEX, function(match, before, id, after) {
|
|
return before + '[REDACTED]' + id.slice(-3) + after;
|
|
});
|
|
}
|
|
|
|
function now() {
|
|
const date = new Date();
|
|
return date.toJSON();
|
|
}
|
|
|
|
function log() {
|
|
const args = Array.prototype.slice.call(arguments, 0);
|
|
|
|
const consoleArgs = ['INFO ', now()].concat(args);
|
|
console._log.apply(console, consoleArgs);
|
|
|
|
// To avoid [Object object] in our log since console.log handles non-strings smoothly
|
|
const str = args.map(function(item) {
|
|
if (typeof item !== 'string') {
|
|
try {
|
|
return JSON.stringify(item);
|
|
}
|
|
catch (e) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return item;
|
|
});
|
|
const toSend = redactGroup(redactPhone(str.join(' ')));
|
|
ipc.send('log-info', toSend);
|
|
}
|
|
|
|
if (window.console) {
|
|
console._log = console.log;
|
|
console.log = log;
|
|
}
|
|
|
|
|
|
// The mechanics of preparing a log for publish
|
|
|
|
function getHeader() {
|
|
return window.navigator.userAgent + ' node/' + window.config.node_version;
|
|
}
|
|
|
|
function getLevel(level) {
|
|
var 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 redactGroup(redactPhone(entries.map(formatLine).join('\n')));
|
|
}
|
|
|
|
function fetch() {
|
|
return new Promise(function(resolve) {
|
|
ipc.send('fetch-log');
|
|
|
|
ipc.on('fetched-log', function(event, text) {
|
|
var result = getHeader() + '\n' + format(text);
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
|
|
function publish(log) {
|
|
log = log || fetch();
|
|
|
|
return new Promise(function(resolve) {
|
|
const payload = textsecure.utils.jsonThing({
|
|
files: {
|
|
'debugLog.txt': {
|
|
content: log
|
|
}
|
|
}
|
|
});
|
|
|
|
$.post('https://api.github.com/gists', payload)
|
|
.then(function(response) {
|
|
console._log('Posted debug log to ', response.html_url);
|
|
resolve(response.html_url);
|
|
})
|
|
.fail(resolve);
|
|
});
|
|
}
|
|
|
|
|
|
// A modern logging interface for the browser
|
|
|
|
// We create our own stream because we don't want to output JSON to the devtools console.
|
|
// Anyway, the default process.stdout stream goes to the command-line, not the devtools.
|
|
const logger = bunyan.createLogger({
|
|
name: 'log',
|
|
streams: [{
|
|
level: 'debug',
|
|
stream: {
|
|
write: function(entry) {
|
|
console._log(formatLine(JSON.parse(entry)));
|
|
}
|
|
}
|
|
}]
|
|
});
|
|
|
|
// The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api
|
|
function logAtLevel() {
|
|
const level = arguments[0];
|
|
const args = Array.prototype.slice.call(arguments, 1);
|
|
|
|
const ipcArgs = ['log-' + level].concat(args);
|
|
ipc.send.apply(ipc, ipcArgs);
|
|
|
|
logger[level].apply(logger, args);
|
|
}
|
|
|
|
window.log = {
|
|
fatal: _.partial(logAtLevel, 'fatal'),
|
|
error: _.partial(logAtLevel, 'error'),
|
|
warn: _.partial(logAtLevel, 'warn'),
|
|
info: _.partial(logAtLevel, 'info'),
|
|
debug: _.partial(logAtLevel, 'debug'),
|
|
trace: _.partial(logAtLevel, 'trace'),
|
|
fetch,
|
|
publish,
|
|
};
|
|
|
|
window.onerror = function(message, script, line, col, error) {
|
|
window.log.error(error.stack);
|
|
};
|
|
|