Skip to content

Commit 414d0d3

Browse files
Merge pull request #3389 from NativeScript/ginev/ios-filter-revamp
update(cli): Improve filtering of iOS logs
2 parents 24b4467 + 605df4a commit 414d0d3

13 files changed

+202
-116
lines changed

lib/definitions/platform.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ interface IPlatformService extends IBuildPlatformAction, NodeJS.EventEmitter {
125125
* @param {IProjectData} projectData DTO with information about the project.
126126
* @returns {void}
127127
*/
128-
startApplication(platform: string, runOptions: IRunPlatformOptions, projectId: string): Promise<void>;
128+
startApplication(platform: string, runOptions: IRunPlatformOptions, appData: Mobile.IApplicationData): Promise<void>;
129129

130130
cleanDestinationApp(platformInfo: IPreparePlatformInfo): Promise<void>;
131131
validatePlatformInstalled(platform: string, projectData: IProjectData): void;

lib/helpers/livesync-command-helper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper {
151151
};
152152

153153
await this.$platformService.deployPlatform(deployPlatformInfo);
154-
await this.$platformService.startApplication(currentPlatform, runPlatformOptions, this.$projectData.projectId);
154+
await this.$platformService.startApplication(currentPlatform, runPlatformOptions, { appId: this.$projectData.projectId, projectName: this.$projectData.projectName });
155155
this.$platformService.trackProjectType(this.$projectData);
156156
}
157157
}

lib/services/android-debug-service.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export class AndroidDebugService extends DebugServiceBase implements IPlatformDe
1313
private $logger: ILogger,
1414
private $androidDeviceDiscovery: Mobile.IDeviceDiscovery,
1515
private $androidProcessService: Mobile.IAndroidProcessService,
16-
private $net: INet) {
16+
private $net: INet,
17+
private $projectDataService: IProjectDataService) {
1718
super(device, $devicesService);
1819
}
1920

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

2728
public async debugStart(debugData: IDebugData, debugOptions: IDebugOptions): Promise<void> {
2829
await this.$devicesService.initialize({ platform: this.platform, deviceId: debugData.deviceIdentifier });
29-
const action = (device: Mobile.IAndroidDevice): Promise<void> => this.debugStartCore(debugData.applicationIdentifier, debugOptions);
30+
const projectData = this.$projectDataService.getProjectData(debugData.projectDir);
31+
const appData: Mobile.IApplicationData = {
32+
appId: debugData.applicationIdentifier,
33+
projectName: projectData.projectName
34+
};
35+
36+
const action = (device: Mobile.IAndroidDevice): Promise<void> => this.debugStartCore(appData, debugOptions);
3037

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

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

94-
const action = (device: Mobile.IAndroidDevice): Promise<string> => this.debugCore(device, packageFile, debugData.applicationIdentifier, debugOptions);
101+
const projectName = this.$projectDataService.getProjectData(debugData.projectDir).projectName;
102+
const appData: Mobile.IApplicationData = {
103+
appId: debugData.applicationIdentifier,
104+
projectName
105+
};
106+
107+
const action = (device: Mobile.IAndroidDevice): Promise<string> => this.debugCore(device, packageFile, appData, debugOptions);
95108

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

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

103116
if (debugOptions.start) {
104-
return await this.attachDebugger(device.deviceInfo.identifier, packageName, debugOptions);
117+
return await this.attachDebugger(device.deviceInfo.identifier, appData.appId, debugOptions);
105118
} else if (debugOptions.stop) {
106119
await this.removePortForwarding();
107120
return null;
108121
} else {
109-
await this.debugStartCore(packageName, debugOptions);
110-
return await this.attachDebugger(device.deviceInfo.identifier, packageName, debugOptions);
122+
await this.debugStartCore(appData, debugOptions);
123+
return await this.attachDebugger(device.deviceInfo.identifier, appData.appId, debugOptions);
111124
}
112125
}
113126

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

129-
private async debugStartCore(packageName: string, debugOptions: IDebugOptions): Promise<void> {
142+
private async debugStartCore(appData: Mobile.IApplicationData, debugOptions: IDebugOptions): Promise<void> {
130143
// Arguments passed to executeShellCommand must be in array ([]), but it turned out adb shell "arg with intervals" still works correctly.
131144
// As we need to redirect output of a command on the device, keep using only one argument.
132145
// 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.
133-
134-
await this.device.applicationManager.stopApplication(packageName);
146+
await this.device.applicationManager.stopApplication(appData);
135147

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

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

142-
await this.device.applicationManager.startApplication(packageName);
154+
await this.device.applicationManager.startApplication(appData);
143155

144-
await this.waitForDebugger(packageName);
156+
await this.waitForDebugger(appData.appId);
145157
}
146158

147159
private async waitForDebugger(packageName: String): Promise<void> {

lib/services/ios-debug-service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS
3333
private $iOSSocketRequestExecutor: IiOSSocketRequestExecutor,
3434
private $processService: IProcessService,
3535
private $socketProxyFactory: ISocketProxyFactory,
36-
private $net: INet) {
36+
private $net: INet,
37+
private $projectDataService: IProjectDataService) {
3738
super(device, $devicesService);
3839
this.$processService.attachToProcessExitSignals(this, this.debugStop);
3940
this.$socketProxyFactory.on(CONNECTION_ERROR_EVENT_NAME, (e: Error) => this.emit(CONNECTION_ERROR_EVENT_NAME, e));
@@ -173,6 +174,7 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS
173174

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

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

lib/services/ios-log-filter.ts

Lines changed: 68 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,88 @@
11
const sourcemap = require("source-map");
22
import * as path from "path";
33
import { cache } from "../common/decorators";
4-
import * as iOSLogFilterBase from "../common/mobile/ios/ios-log-filter";
54

6-
export class IOSLogFilter extends iOSLogFilterBase.IOSLogFilter implements Mobile.IPlatformLogFilter {
7-
protected infoFilterRegex = /^.*?((?:<Notice>:)?.*?(((?:CONSOLE|JS) (?:LOG|ERROR)).*?))$/im;
5+
export class IOSLogFilter implements Mobile.IPlatformLogFilter {
6+
// Used to recognize output related to the current project
7+
// This looks for artifacts like: AppName[22432] or AppName(SomeTextHere)[23123]
8+
private appOutputRegex: RegExp = /([^\s\(\)]+)(?:\([^\s]+\))?\[[0-9]+\]/;
9+
10+
// Used to trim the passed messages to a simpler output
11+
// Example:
12+
// 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:"
13+
// Becomes: CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:3477:36: ORIGINAL STACKTRACE:
14+
protected infoFilterRegex = new RegExp(`^.*(?:<Notice>:|<Error>:|<Warning>:|\\(NativeScript\\)|${this.appOutputRegex.source}:){1}`);
15+
16+
private filterActive: boolean = true;
817

918
private partialLine: string = null;
1019

11-
constructor($loggingLevels: Mobile.ILoggingLevels,
20+
constructor(private $logger: ILogger,
21+
private $loggingLevels: Mobile.ILoggingLevels,
1222
private $fs: IFileSystem,
1323
private $projectData: IProjectData) {
14-
super($loggingLevels);
1524
}
1625

17-
public filterData(data: string, logLevel: string, pid?: string): string {
18-
data = super.filterData(data, logLevel, pid);
19-
if (pid && data && data.indexOf(`[${pid}]`) === -1) {
20-
return null;
26+
public filterData(data: string, loggingOptions: Mobile.IDeviceLogOptions = <any>{}): string {
27+
const specifiedLogLevel = (loggingOptions.logLevel || '').toUpperCase();
28+
this.$logger.trace("Logging options", loggingOptions);
29+
30+
if (specifiedLogLevel !== this.$loggingLevels.info || !data) {
31+
return data;
2132
}
2233

23-
if (data) {
24-
const skipLastLine = data[data.length - 1] !== "\n";
25-
const lines = data.split("\n");
26-
let result = "";
27-
for (let i = 0; i < lines.length; i++) {
28-
let line = lines[i];
29-
if (i === 0 && this.partialLine) {
30-
line = this.partialLine + line;
31-
this.partialLine = null;
32-
}
33-
if (line.length < 1 ||
34-
line.indexOf("SecTaskCopyDebugDescription") !== -1 ||
35-
line.indexOf("NativeScript loaded bundle") !== -1 ||
36-
(line.indexOf("assertion failed:") !== -1 && data.indexOf("libxpc.dylib") !== -1)) {
37-
continue;
38-
}
39-
// CONSOLE LOG messages comme in the following form:
40-
// <date> <domain> <app>[pid] CONSOLE LOG file:///location:row:column: <actual message goes here>
41-
// This code removes unnecessary information from log messages. The output looks like:
42-
// CONSOLE LOG file:///location:row:column: <actual message goes here>
43-
if (pid) {
44-
if (line.indexOf(`[${pid}]: `) !== -1) {
45-
const pidRegex = new RegExp(`^.*\\[${pid}\\]:\\s(?:\\(NativeScript\\)\\s)?`);
46-
line = line.replace(pidRegex, "").trim();
47-
this.getOriginalFileLocation(line);
48-
result += this.getOriginalFileLocation(line) + "\n";
49-
}
34+
const chunkLines = data.split('\n');
35+
const skipLastLine = chunkLines.length > 0 ? data[data.length - 1] !== "\n" : false;
36+
let output = "";
37+
for (let i = 0; i < chunkLines.length; i++) {
38+
let currentLine = chunkLines[i];
5039

51-
continue;
52-
}
53-
if (skipLastLine && i === lines.length - 1 && lines.length > 1) {
54-
this.partialLine = line;
55-
} else {
56-
result += this.getOriginalFileLocation(line) + "\n";
57-
}
40+
if (this.partialLine) {
41+
currentLine = this.partialLine + currentLine;
42+
this.partialLine = undefined;
43+
}
44+
45+
if (i === chunkLines.length - 1 && skipLastLine) {
46+
this.partialLine = currentLine;
47+
break;
48+
}
49+
50+
// Legacy filter moved to preFilter
51+
if (this.preFilter(data, currentLine)) {
52+
continue;
53+
}
54+
55+
const matchResult = this.appOutputRegex.exec(currentLine);
56+
57+
if (matchResult && matchResult.length > 1) {
58+
// Check if the name of the app equals the name of the CLI project and turn on the filter if not.
59+
// We call initializeProjectData in order to obtain the current project name as the instance
60+
// of this filter may be used accross multiple projects.
61+
const projectName = loggingOptions && loggingOptions.projectName;
62+
this.filterActive = matchResult[1] !== projectName;
63+
}
64+
65+
if (this.filterActive) {
66+
continue;
5867
}
59-
return result;
68+
69+
const filteredLineInfo = currentLine.match(this.infoFilterRegex);
70+
if (filteredLineInfo && filteredLineInfo.length > 0) {
71+
currentLine = currentLine.replace(filteredLineInfo[0], "");
72+
}
73+
74+
currentLine = currentLine.trim();
75+
output += this.getOriginalFileLocation(currentLine) + '\n';
6076
}
6177

62-
return data;
78+
return output.length === 0 ? null : output;
79+
}
80+
81+
private preFilter(data: string, currentLine: string): boolean {
82+
return currentLine.length < 1 ||
83+
currentLine.indexOf("SecTaskCopyDebugDescription") !== -1 ||
84+
currentLine.indexOf("NativeScript loaded bundle") !== -1 ||
85+
(currentLine.indexOf("assertion failed:") !== -1 && data.indexOf("libxpc.dylib") !== -1);
6386
}
6487

6588
private getOriginalFileLocation(data: string): string {

lib/services/livesync/android-device-livesync-service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class AndroidDeviceLiveSyncService extends DeviceLiveSyncServiceBase impl
4242
(localToDevicePath: Mobile.ILocalToDevicePathData) => !this.canExecuteFastSync(localToDevicePath.getLocalPath(), projectData, this.device.deviceInfo.platform));
4343

4444
if (!canExecuteFastSync) {
45-
return this.restartApplication(deviceAppData);
45+
return this.restartApplication(deviceAppData, projectData.projectName);
4646
}
4747
}
4848

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

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

65-
await this.device.applicationManager.restartApplication(deviceAppData.appIdentifier);
65+
await this.device.applicationManager.restartApplication({ appId: deviceAppData.appIdentifier, projectName });
6666
}
6767

6868
public async beforeLiveSyncAction(deviceAppData: Mobile.IDeviceAppData): Promise<void> {

lib/services/livesync/ios-device-livesync-service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ export class IOSDeviceLiveSyncService extends DeviceLiveSyncServiceBase implemen
7575
}
7676
}
7777

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

8282
private async reloadPage(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<void> {

lib/services/livesync/livesync-service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
165165
};
166166

167167
try {
168-
await deviceAppData.device.applicationManager.stopApplication(applicationId, projectData.projectName);
168+
await deviceAppData.device.applicationManager.stopApplication({ appId: applicationId, projectName: projectData.projectName });
169169
// Now that we've stopped the application we know it isn't started, so set debugOptions.start to false
170170
// so that it doesn't default to true in attachDebugger method
171171
debugOptions = debugOptions || {};

lib/services/platform-service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,11 +543,11 @@ export class PlatformService extends EventEmitter implements IPlatformService {
543543
await this.$devicesService.execute(action, this.getCanExecuteAction(deployInfo.platform, deployInfo.deployOptions));
544544
}
545545

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

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

test/services/android-debug-service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ class AndroidDebugServiceInheritor extends AndroidDebugService {
1111
$logger: ILogger,
1212
$androidDeviceDiscovery: Mobile.IDeviceDiscovery,
1313
$androidProcessService: Mobile.IAndroidProcessService,
14-
$net: INet) {
15-
super(<any>{}, $devicesService, $errors, $logger, $androidDeviceDiscovery, $androidProcessService, $net);
14+
$net: INet,
15+
$projectDataService: IProjectDataService) {
16+
super(<any>{}, $devicesService, $errors, $logger, $androidDeviceDiscovery, $androidProcessService, $net, $projectDataService);
1617
}
1718

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

3234
return testInjector;
3335
};

test/services/ios-debug-service.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ class IOSDebugServiceInheritor extends IOSDebugService {
1818
$iOSSocketRequestExecutor: IiOSSocketRequestExecutor,
1919
$processService: IProcessService,
2020
$socketProxyFactory: ISocketProxyFactory,
21-
$net: INet) {
21+
$net: INet,
22+
$projectDataService: IProjectDataService) {
2223
super(<any>{}, $devicesService, $platformService, $iOSEmulatorServices, $childProcess, $hostInfo, $logger, $errors,
23-
$npmInstallationManager, $iOSNotification, $iOSSocketRequestExecutor, $processService, $socketProxyFactory, $net);
24+
$npmInstallationManager, $iOSNotification, $iOSSocketRequestExecutor, $processService, $socketProxyFactory, $net, $projectDataService);
2425
}
2526

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

58+
testInjector.register("projectDataService", {});
59+
5760
return testInjector;
5861
};
5962

0 commit comments

Comments
 (0)