Skip to content

update(cli): Improve filtering of iOS logs #3389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/definitions/platform.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ interface IPlatformService extends IBuildPlatformAction, NodeJS.EventEmitter {
* @param {IProjectData} projectData DTO with information about the project.
* @returns {void}
*/
startApplication(platform: string, runOptions: IRunPlatformOptions, projectId: string): Promise<void>;
startApplication(platform: string, runOptions: IRunPlatformOptions, appData: Mobile.IApplicationData): Promise<void>;

cleanDestinationApp(platformInfo: IPreparePlatformInfo): Promise<void>;
validatePlatformInstalled(platform: string, projectData: IProjectData): void;
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/livesync-command-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper {
};

await this.$platformService.deployPlatform(deployPlatformInfo);
await this.$platformService.startApplication(currentPlatform, runPlatformOptions, this.$projectData.projectId);
await this.$platformService.startApplication(currentPlatform, runPlatformOptions, { appId: this.$projectData.projectId, projectName: this.$projectData.projectName });
this.$platformService.trackProjectType(this.$projectData);
}
}
Expand Down
42 changes: 27 additions & 15 deletions lib/services/android-debug-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe
private $logger: ILogger,
private $androidDeviceDiscovery: Mobile.IDeviceDiscovery,
private $androidProcessService: Mobile.IAndroidProcessService,
private $net: INet) {
private $net: INet,
private $projectDataService: IProjectDataService) {
super(device, $devicesService);
}

Expand All @@ -26,7 +27,13 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe

public async debugStart(debugData: IDebugData, debugOptions: IDebugOptions): Promise<void> {
await this.$devicesService.initialize({ platform: this.platform, deviceId: debugData.deviceIdentifier });
const action = (device: Mobile.IAndroidDevice): Promise<void> => this.debugStartCore(debugData.applicationIdentifier, debugOptions);
const projectData = this.$projectDataService.getProjectData(debugData.projectDir);
const appData: Mobile.IApplicationData = {
appId: debugData.applicationIdentifier,
projectName: projectData.projectName
};

const action = (device: Mobile.IAndroidDevice): Promise<void> => this.debugStartCore(appData, debugOptions);

await this.$devicesService.execute(action, this.getCanExecuteAction(debugData.deviceIdentifier));
}
Expand Down Expand Up @@ -91,23 +98,29 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe

await this.$devicesService.initialize({ platform: this.platform, deviceId: debugData.deviceIdentifier });

const action = (device: Mobile.IAndroidDevice): Promise<string> => this.debugCore(device, packageFile, debugData.applicationIdentifier, debugOptions);
const projectName = this.$projectDataService.getProjectData(debugData.projectDir).projectName;
const appData: Mobile.IApplicationData = {
appId: debugData.applicationIdentifier,
projectName
};

const action = (device: Mobile.IAndroidDevice): Promise<string> => this.debugCore(device, packageFile, appData, debugOptions);

const deviceActionResult = await this.$devicesService.execute(action, this.getCanExecuteAction(debugData.deviceIdentifier));
return deviceActionResult[0].result;
}

private async debugCore(device: Mobile.IAndroidDevice, packageFile: string, packageName: string, debugOptions: IDebugOptions): Promise<string> {
await this.printDebugPort(device.deviceInfo.identifier, packageName);
private async debugCore(device: Mobile.IAndroidDevice, packageFile: string, appData: Mobile.IApplicationData, debugOptions: IDebugOptions): Promise<string> {
await this.printDebugPort(device.deviceInfo.identifier, appData.appId);

if (debugOptions.start) {
return await this.attachDebugger(device.deviceInfo.identifier, packageName, debugOptions);
return await this.attachDebugger(device.deviceInfo.identifier, appData.appId, debugOptions);
} else if (debugOptions.stop) {
await this.removePortForwarding();
return null;
} else {
await this.debugStartCore(packageName, debugOptions);
return await this.attachDebugger(device.deviceInfo.identifier, packageName, debugOptions);
await this.debugStartCore(appData, debugOptions);
return await this.attachDebugger(device.deviceInfo.identifier, appData.appId, debugOptions);
}
}

Expand All @@ -126,22 +139,21 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe
return this.getChromeDebugUrl(debugOptions, port);
}

private async debugStartCore(packageName: string, debugOptions: IDebugOptions): Promise<void> {
private async debugStartCore(appData: Mobile.IApplicationData, debugOptions: IDebugOptions): Promise<void> {
// Arguments passed to executeShellCommand must be in array ([]), but it turned out adb shell "arg with intervals" still works correctly.
// As we need to redirect output of a command on the device, keep using only one argument.
// We could rewrite this with two calls - touch and rm -f , but -f flag is not available on old Android, so rm call will fail when file does not exist.

await this.device.applicationManager.stopApplication(packageName);
await this.device.applicationManager.stopApplication(appData);

if (debugOptions.debugBrk) {
await this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${packageName}-debugbreak`]);
await this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${appData.appId}-debugbreak`]);
}

await this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${packageName}-debugger-started`]);
await this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${appData.appId}-debugger-started`]);

await this.device.applicationManager.startApplication(packageName);
await this.device.applicationManager.startApplication(appData);

await this.waitForDebugger(packageName);
await this.waitForDebugger(appData.appId);
}

private async waitForDebugger(packageName: String): Promise<void> {
Expand Down
6 changes: 4 additions & 2 deletions lib/services/ios-debug-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS
private $iOSSocketRequestExecutor: IiOSSocketRequestExecutor,
private $processService: IProcessService,
private $socketProxyFactory: ISocketProxyFactory,
private $net: INet) {
private $net: INet,
private $projectDataService: IProjectDataService) {
super(device, $devicesService);
this.$processService.attachToProcessExitSignals(this, this.debugStop);
this.$socketProxyFactory.on(CONNECTION_ERROR_EVENT_NAME, (e: Error) => this.emit(CONNECTION_ERROR_EVENT_NAME, e));
Expand Down Expand Up @@ -173,6 +174,7 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS

private async deviceDebugBrk(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string> {
await this.$devicesService.initialize({ platform: this.platform, deviceId: debugData.deviceIdentifier });
const projectData = this.$projectDataService.getProjectData(debugData.projectDir);
const action = async (device: iOSDevice.IOSDevice): Promise<string> => {
if (device.isEmulator) {
return await this.emulatorDebugBrk(debugData, debugOptions);
Expand All @@ -185,7 +187,7 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS
};

const promisesResults = await Promise.all<any>([
this.$platformService.startApplication(this.platform, runOptions, debugData.applicationIdentifier),
this.$platformService.startApplication(this.platform, runOptions, { appId: debugData.applicationIdentifier, projectName: projectData.projectName }),
this.debugBrkCore(device, debugData, debugOptions)
]);

Expand Down
113 changes: 68 additions & 45 deletions lib/services/ios-log-filter.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,88 @@
const sourcemap = require("source-map");
import * as path from "path";
import { cache } from "../common/decorators";
import * as iOSLogFilterBase from "../common/mobile/ios/ios-log-filter";

export class IOSLogFilter extends iOSLogFilterBase.IOSLogFilter implements Mobile.IPlatformLogFilter {
protected infoFilterRegex = /^.*?((?:<Notice>:)?.*?(((?:CONSOLE|JS) (?:LOG|ERROR)).*?))$/im;
export class IOSLogFilter implements Mobile.IPlatformLogFilter {
// Used to recognize output related to the current project
// This looks for artifacts like: AppName[22432] or AppName(SomeTextHere)[23123]
private appOutputRegex: RegExp = /([^\s\(\)]+)(?:\([^\s]+\))?\[[0-9]+\]/;

// Used to trim the passed messages to a simpler output
// Example:
// This: "May 24 15:54:52 Dragons-iPhone NativeScript250(NativeScript)[356] <Notice>: CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:3477:36: ORIGINAL STACKTRACE:"
// Becomes: CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:3477:36: ORIGINAL STACKTRACE:
protected infoFilterRegex = new RegExp(`^.*(?:<Notice>:|<Error>:|<Warning>:|\\(NativeScript\\)|${this.appOutputRegex.source}:){1}`);

private filterActive: boolean = true;

private partialLine: string = null;

constructor($loggingLevels: Mobile.ILoggingLevels,
constructor(private $logger: ILogger,
private $loggingLevels: Mobile.ILoggingLevels,
private $fs: IFileSystem,
private $projectData: IProjectData) {
super($loggingLevels);
}

public filterData(data: string, logLevel: string, pid?: string): string {
data = super.filterData(data, logLevel, pid);
if (pid && data && data.indexOf(`[${pid}]`) === -1) {
return null;
public filterData(data: string, loggingOptions: Mobile.IDeviceLogOptions = <any>{}): string {
const specifiedLogLevel = (loggingOptions.logLevel || '').toUpperCase();
this.$logger.trace("Logging options", loggingOptions);

if (specifiedLogLevel !== this.$loggingLevels.info || !data) {
return data;
}

if (data) {
const skipLastLine = data[data.length - 1] !== "\n";
const lines = data.split("\n");
let result = "";
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
if (i === 0 && this.partialLine) {
line = this.partialLine + line;
this.partialLine = null;
}
if (line.length < 1 ||
line.indexOf("SecTaskCopyDebugDescription") !== -1 ||
line.indexOf("NativeScript loaded bundle") !== -1 ||
(line.indexOf("assertion failed:") !== -1 && data.indexOf("libxpc.dylib") !== -1)) {
continue;
}
// CONSOLE LOG messages comme in the following form:
// <date> <domain> <app>[pid] CONSOLE LOG file:///location:row:column: <actual message goes here>
// This code removes unnecessary information from log messages. The output looks like:
// CONSOLE LOG file:///location:row:column: <actual message goes here>
if (pid) {
if (line.indexOf(`[${pid}]: `) !== -1) {
const pidRegex = new RegExp(`^.*\\[${pid}\\]:\\s(?:\\(NativeScript\\)\\s)?`);
line = line.replace(pidRegex, "").trim();
this.getOriginalFileLocation(line);
result += this.getOriginalFileLocation(line) + "\n";
}
const chunkLines = data.split('\n');
const skipLastLine = chunkLines.length > 0 ? data[data.length - 1] !== "\n" : false;
let output = "";
for (let i = 0; i < chunkLines.length; i++) {
let currentLine = chunkLines[i];

continue;
}
if (skipLastLine && i === lines.length - 1 && lines.length > 1) {
this.partialLine = line;
} else {
result += this.getOriginalFileLocation(line) + "\n";
}
if (this.partialLine) {
currentLine = this.partialLine + currentLine;
this.partialLine = undefined;
}

if (i === chunkLines.length - 1 && skipLastLine) {
this.partialLine = currentLine;
break;
}

// Legacy filter moved to preFilter
if (this.preFilter(data, currentLine)) {
continue;
}

const matchResult = this.appOutputRegex.exec(currentLine);

if (matchResult && matchResult.length > 1) {
// Check if the name of the app equals the name of the CLI project and turn on the filter if not.
// We call initializeProjectData in order to obtain the current project name as the instance
// of this filter may be used accross multiple projects.
const projectName = loggingOptions && loggingOptions.projectName;
this.filterActive = matchResult[1] !== projectName;
}

if (this.filterActive) {
continue;
}
return result;

const filteredLineInfo = currentLine.match(this.infoFilterRegex);
if (filteredLineInfo && filteredLineInfo.length > 0) {
currentLine = currentLine.replace(filteredLineInfo[0], "");
}

currentLine = currentLine.trim();
output += this.getOriginalFileLocation(currentLine) + '\n';
}

return data;
return output.length === 0 ? null : output;
}

private preFilter(data: string, currentLine: string): boolean {
return currentLine.length < 1 ||
currentLine.indexOf("SecTaskCopyDebugDescription") !== -1 ||
currentLine.indexOf("NativeScript loaded bundle") !== -1 ||
(currentLine.indexOf("assertion failed:") !== -1 && data.indexOf("libxpc.dylib") !== -1);
}

private getOriginalFileLocation(data: string): string {
Expand Down
6 changes: 3 additions & 3 deletions lib/services/livesync/android-device-livesync-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class AndroidDeviceLiveSyncService extends DeviceLiveSyncServiceBase impl
(localToDevicePath: Mobile.ILocalToDevicePathData) => !this.canExecuteFastSync(localToDevicePath.getLocalPath(), projectData, this.device.deviceInfo.platform));

if (!canExecuteFastSync) {
return this.restartApplication(deviceAppData);
return this.restartApplication(deviceAppData, projectData.projectName);
}
}

Expand All @@ -57,12 +57,12 @@ export class AndroidDeviceLiveSyncService extends DeviceLiveSyncServiceBase impl
await this.$mobileHelper.buildDevicePath(deviceRootPath, LiveSyncPaths.REMOVEDSYNC_DIR_NAME)]);
}

private async restartApplication(deviceAppData: Mobile.IDeviceAppData): Promise<void> {
private async restartApplication(deviceAppData: Mobile.IDeviceAppData, projectName: string): Promise<void> {
const devicePathRoot = `/data/data/${deviceAppData.appIdentifier}/files`;
const devicePath = this.$mobileHelper.buildDevicePath(devicePathRoot, "code_cache", "secondary_dexes", "proxyThumb");
await this.device.adb.executeShellCommand(["rm", "-rf", devicePath]);

await this.device.applicationManager.restartApplication(deviceAppData.appIdentifier);
await this.device.applicationManager.restartApplication({ appId: deviceAppData.appIdentifier, projectName });
}

public async beforeLiveSyncAction(deviceAppData: Mobile.IDeviceAppData): Promise<void> {
Expand Down
4 changes: 2 additions & 2 deletions lib/services/livesync/ios-device-livesync-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ export class IOSDeviceLiveSyncService extends DeviceLiveSyncServiceBase implemen
}
}

private async restartApplication(deviceAppData: Mobile.IDeviceAppData, appName: string): Promise<void> {
return this.device.applicationManager.restartApplication(deviceAppData.appIdentifier, appName);
private async restartApplication(deviceAppData: Mobile.IDeviceAppData, projectName: string): Promise<void> {
return this.device.applicationManager.restartApplication({ appId: deviceAppData.appIdentifier, projectName });
}

private async reloadPage(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion lib/services/livesync/livesync-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
};

try {
await deviceAppData.device.applicationManager.stopApplication(applicationId, projectData.projectName);
await deviceAppData.device.applicationManager.stopApplication({ appId: applicationId, projectName: projectData.projectName });
// Now that we've stopped the application we know it isn't started, so set debugOptions.start to false
// so that it doesn't default to true in attachDebugger method
debugOptions = debugOptions || {};
Expand Down
4 changes: 2 additions & 2 deletions lib/services/platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,11 @@ export class PlatformService extends EventEmitter implements IPlatformService {
await this.$devicesService.execute(action, this.getCanExecuteAction(deployInfo.platform, deployInfo.deployOptions));
}

public async startApplication(platform: string, runOptions: IRunPlatformOptions, projectId: string): Promise<void> {
public async startApplication(platform: string, runOptions: IRunPlatformOptions, appData: Mobile.IApplicationData): Promise<void> {
this.$logger.out("Starting...");

const action = async (device: Mobile.IDevice) => {
await device.applicationManager.startApplication(projectId);
await device.applicationManager.startApplication(appData);
this.$logger.out(`Successfully started on device with identifier '${device.deviceInfo.identifier}'.`);
};

Expand Down
6 changes: 4 additions & 2 deletions test/services/android-debug-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ class AndroidDebugServiceInheritor extends AndroidDebugService {
$logger: ILogger,
$androidDeviceDiscovery: Mobile.IDeviceDiscovery,
$androidProcessService: Mobile.IAndroidProcessService,
$net: INet) {
super(<any>{}, $devicesService, $errors, $logger, $androidDeviceDiscovery, $androidProcessService, $net);
$net: INet,
$projectDataService: IProjectDataService) {
super(<any>{}, $devicesService, $errors, $logger, $androidDeviceDiscovery, $androidProcessService, $net, $projectDataService);
}

public getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string {
Expand All @@ -28,6 +29,7 @@ const createTestInjector = (): IInjector => {
testInjector.register("androidDeviceDiscovery", {});
testInjector.register("androidProcessService", {});
testInjector.register("net", {});
testInjector.register("projectDataService", {});

return testInjector;
};
Expand Down
7 changes: 5 additions & 2 deletions test/services/ios-debug-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ class IOSDebugServiceInheritor extends IOSDebugService {
$iOSSocketRequestExecutor: IiOSSocketRequestExecutor,
$processService: IProcessService,
$socketProxyFactory: ISocketProxyFactory,
$net: INet) {
$net: INet,
$projectDataService: IProjectDataService) {
super(<any>{}, $devicesService, $platformService, $iOSEmulatorServices, $childProcess, $hostInfo, $logger, $errors,
$npmInstallationManager, $iOSNotification, $iOSSocketRequestExecutor, $processService, $socketProxyFactory, $net);
$npmInstallationManager, $iOSNotification, $iOSSocketRequestExecutor, $processService, $socketProxyFactory, $net, $projectDataService);
}

public getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string {
Expand Down Expand Up @@ -54,6 +55,8 @@ const createTestInjector = (): IInjector => {
waitForPortToListen: async (opts: { port: number, timeout: number, interval?: number }): Promise<boolean> => true
});

testInjector.register("projectDataService", {});

return testInjector;
};

Expand Down
Loading