signal-desktop/js/logging.js
Scott Nonnenberg c94d4efd18
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 13:57:13 -07:00

171 lines
3.9 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() {
let header = window.navigator.userAgent;
header += ' node/' + window.config.node_version;
header += ' env/' + window.config.environment;
return header;
}
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);
};