You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
8.3 KiB
214 lines
8.3 KiB
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.detachStream = detachStream;
|
|
exports.getImageNameFromBundleIdentifierAsync = getImageNameFromBundleIdentifierAsync;
|
|
exports.getImageNameFromContainerPath = getImageNameFromContainerPath;
|
|
exports.isStreamingLogs = isStreamingLogs;
|
|
exports.onMessage = onMessage;
|
|
exports.streamLogs = streamLogs;
|
|
function _chalk() {
|
|
const data = _interopRequireDefault(require("chalk"));
|
|
_chalk = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _child_process() {
|
|
const data = require("child_process");
|
|
_child_process = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _path() {
|
|
const data = _interopRequireDefault(require("path"));
|
|
_path = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _wrapAnsi() {
|
|
const data = _interopRequireDefault(require("wrap-ansi"));
|
|
_wrapAnsi = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _internal() {
|
|
const data = require("./internal");
|
|
_internal = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
const forks = {};
|
|
function parseMessageJson(data) {
|
|
const stringData = data.toString();
|
|
try {
|
|
return JSON.parse(stringData);
|
|
} catch {
|
|
_internal().Logger.global.debug('Failed to parse simctl JSON message:\n' + stringData);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// There are a lot of networking logs in RN that aren't relevant to the user.
|
|
function isNetworkLog(simLog) {
|
|
var _simLog$source;
|
|
return simLog.subsystem === 'com.apple.network' || simLog.category === 'connection' || ((_simLog$source = simLog.source) === null || _simLog$source === void 0 ? void 0 : _simLog$source.image) === 'CFNetwork';
|
|
}
|
|
function isReactLog(simLog) {
|
|
var _simLog$source2;
|
|
return simLog.subsystem === 'com.facebook.react.log' && ((_simLog$source2 = simLog.source) === null || _simLog$source2 === void 0 ? void 0 : _simLog$source2.file) === 'RCTLog.mm';
|
|
}
|
|
|
|
// It's not clear what these are but they aren't very useful.
|
|
// (The connection to service on pid 0 named com.apple.commcenter.coretelephony.xpc was invalidated)
|
|
// We can add them later if need.
|
|
function isCoreTelephonyLog(simLog) {
|
|
// [CoreTelephony] Updating selectors failed with: Error Domain=NSCocoaErrorDomain Code=4099
|
|
// "The connection to service on pid 0 named com.apple.commcenter.coretelephony.xpc was invalidated." UserInfo={NSDebugDescription=The connection to service on pid 0 named com.apple.commcenter.coretelephony.xpc was invalidated.}
|
|
return simLog.subsystem === 'com.apple.CoreTelephony';
|
|
}
|
|
|
|
// https://stackoverflow.com/a/65313219/4047926
|
|
function isWebKitLog(simLog) {
|
|
// [WebKit] 0x1143ca500 - ProcessAssertion: Failed to acquire RBS Background assertion 'WebProcess Background Assertion' for process with PID 27084, error: Error Domain=RBSAssertionErrorDomain Code=3 "Target is not running or required target
|
|
// entitlement is missing" UserInfo={RBSAssertionAttribute=<RBSDomainAttribute| domain:"com.apple.webkit" name:"Background" sourceEnvironment:"(null)">, NSLocalizedFailureReason=Target is not running or required target entitlement is missing}
|
|
return simLog.subsystem === 'com.apple.WebKit';
|
|
}
|
|
|
|
// Similar to WebKit logs
|
|
function isRunningBoardServicesLog(simLog) {
|
|
// [RunningBoardServices] Error acquiring assertion: <Error Domain=RBSAssertionErrorDomain Code=3 "Target is not running or required target entitlement is missing" UserInfo={RBSAssertionAttribute=<RBSDomainAttribute| domain:"com.apple.webkit"
|
|
// name:"Background" sourceEnvironment:"(null)">, NSLocalizedFailureReason=Target is not running or required target entitlement is missing}>
|
|
return simLog.subsystem === 'com.apple.runningboard';
|
|
}
|
|
function formatMessage(simLog) {
|
|
var _simLog$source$image, _simLog$source3;
|
|
// TODO: Maybe change "TCC" to "Consent" or "System".
|
|
const category = _chalk().default.gray(`[${(_simLog$source$image = (_simLog$source3 = simLog.source) === null || _simLog$source3 === void 0 ? void 0 : _simLog$source3.image) !== null && _simLog$source$image !== void 0 ? _simLog$source$image : simLog.subsystem}]`);
|
|
const message = simLog.eventMessage;
|
|
return (0, _wrapAnsi().default)(category + ' ' + message, process.stdout.columns || 80);
|
|
}
|
|
function onMessage(simLog) {
|
|
let hasLogged = false;
|
|
if (simLog.messageType === 'Error') {
|
|
if (
|
|
// Hide all networking errors which are mostly useless.
|
|
!isNetworkLog(simLog) &&
|
|
// Showing React errors will result in duplicate messages.
|
|
!isReactLog(simLog) && !isCoreTelephonyLog(simLog) && !isWebKitLog(simLog) && !isRunningBoardServicesLog(simLog)) {
|
|
hasLogged = true;
|
|
// Sim: This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.
|
|
_internal().Logger.global.error(formatMessage(simLog));
|
|
}
|
|
} else if (simLog.eventMessage) {
|
|
var _simLog$source4;
|
|
// If the source has a file (i.e. not a system log).
|
|
if ((_simLog$source4 = simLog.source) !== null && _simLog$source4 !== void 0 && _simLog$source4.file || simLog.eventMessage.includes('Terminating app due to uncaught exception')) {
|
|
hasLogged = true;
|
|
_internal().Logger.global.info(formatMessage(simLog));
|
|
}
|
|
}
|
|
if (!hasLogged) {
|
|
_internal().Logger.global.debug(formatMessage(simLog));
|
|
} else {
|
|
// console.log('DATA:', JSON.stringify(simLog));
|
|
}
|
|
}
|
|
function isStreamingLogs(udid) {
|
|
return !!forks[udid];
|
|
}
|
|
|
|
// The primary purpose of this module is to surface logs related to fatal app crashes.
|
|
// Everything else should come through the native React logger.
|
|
function streamLogs({
|
|
pid,
|
|
udid
|
|
}) {
|
|
// Prevent adding a duplicate listener.
|
|
// This only works because our current usage of SimControlLogs only allows for one possible `pid` to be used.
|
|
// If in the future, you can attach logs to two different apps from the same process, then this will need to be changed.
|
|
if (forks[udid]) {
|
|
return;
|
|
}
|
|
|
|
// xcrun simctl spawn booted log stream --process --style json
|
|
const childProcess = (0, _child_process().spawn)('xcrun', ['simctl', 'spawn', udid, 'log', 'stream', '--process', pid,
|
|
// ndjson provides a better format than json.
|
|
'--style', 'ndjson',
|
|
// Provide the source so we can filter logs better
|
|
'--source',
|
|
// log, activity, trace -- activity was related to layouts, trace didn't work, so that leaves log.
|
|
// Passing nothing combines all three, but we don't use activity.
|
|
'--type', 'log',
|
|
// backtrace doesn't seem very useful in basic cases.
|
|
// TODO: Maybe we can format as a stack trace for native errors.
|
|
'--no-backtrace']);
|
|
childProcess.stdout.on('data', data => {
|
|
const simLog = parseMessageJson(data);
|
|
if (!simLog) {
|
|
return;
|
|
}
|
|
onMessage(simLog);
|
|
});
|
|
childProcess.on('error', ({
|
|
message
|
|
}) => {
|
|
_internal().Logger.global.debug('[simctl error]:', message);
|
|
});
|
|
forks[udid] = childProcess;
|
|
// Ensure the process is removed.
|
|
ensureExitHooksInstalled();
|
|
}
|
|
async function detachStream(udid) {
|
|
if (forks[udid]) {
|
|
await killProcess(forks[udid]);
|
|
delete forks[udid];
|
|
}
|
|
}
|
|
let hasInstalledExitHooks = false;
|
|
function ensureExitHooksInstalled() {
|
|
if (hasInstalledExitHooks) return;
|
|
hasInstalledExitHooks = true;
|
|
const killSignals = ['SIGINT', 'SIGTERM'];
|
|
for (const signal of killSignals) {
|
|
process.on(signal, async () => {
|
|
await Promise.all(Object.keys(forks).map(udid => detachStream(udid)));
|
|
});
|
|
}
|
|
}
|
|
async function killProcess(childProcess) {
|
|
if (childProcess) {
|
|
return new Promise(resolve => {
|
|
childProcess.on('close', resolve);
|
|
childProcess.kill();
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param udid
|
|
* @param bundleIdentifier
|
|
* @returns Image name like `Exponent` and `null` when the app is not installed on the provided simulator.
|
|
*/
|
|
async function getImageNameFromBundleIdentifierAsync(udid, bundleIdentifier) {
|
|
const containerPath = await _internal().SimControl.getContainerPathAsync({
|
|
udid,
|
|
bundleIdentifier
|
|
});
|
|
if (containerPath) {
|
|
return getImageNameFromContainerPath(containerPath);
|
|
}
|
|
return null;
|
|
}
|
|
function getImageNameFromContainerPath(binaryPath) {
|
|
return _path().default.basename(binaryPath).split('.')[0];
|
|
}
|
|
//# sourceMappingURL=SimControlLogs.js.map
|