Skip to content

Commit 8c5ef04

Browse files
feat(logs): improve android logging (#5755)
Co-authored-by: Igor Randjelovic <[email protected]>
1 parent 3307155 commit 8c5ef04

File tree

8 files changed

+182
-48
lines changed

8 files changed

+182
-48
lines changed

lib/common/definitions/mobile.d.ts

+14
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,10 @@ declare global {
179179
interface ILogcatStartOptions {
180180
deviceIdentifier: string;
181181
pid?: string;
182+
appId?: string;
182183
keepSingleProcess?: boolean;
184+
185+
onAppRestarted?: () => void;
183186
}
184187

185188
interface ILogcatHelper {
@@ -223,6 +226,13 @@ declare global {
223226
*/
224227
setApplicationPidForDevice(deviceIdentifier: string, pid: string): void;
225228

229+
/**
230+
* Sets the application id of the application on the specified device.
231+
* @param {string} deviceIdentifier The unique identifier of the device.
232+
* @param {string} appId The Process ID of the currently running application for which we need the logs.
233+
*/
234+
setApplicationIdForDevice(deviceIdentifier: string, appId: string): void;
235+
226236
/**
227237
* Sets the project name of the application on the specified device.
228238
* @param {string} deviceIdentifier The unique identifier of the device.
@@ -264,6 +274,10 @@ declare global {
264274
* The project name.
265275
*/
266276
projectName?: string;
277+
/**
278+
* The application id of the current app.
279+
*/
280+
applicationId?: string;
267281
}
268282

269283
/**

lib/common/mobile/android/android-application-manager.ts

+24-5
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ export class AndroidApplicationManager extends ApplicationManagerBase {
155155
"1",
156156
]);
157157
}
158+
await this.onAppLaunch(appData);
159+
}
160+
161+
private async onAppLaunch(appData: Mobile.IStartApplicationData) {
162+
const appIdentifier = appData.appId;
158163

159164
if (!this.$options.justlaunch && !appData.justLaunch) {
160165
const deviceIdentifier = this.identifier;
@@ -167,13 +172,26 @@ export class AndroidApplicationManager extends ApplicationManagerBase {
167172
deviceIdentifier,
168173
processIdentifier
169174
);
175+
176+
this.$deviceLogProvider.setApplicationIdForDevice(
177+
deviceIdentifier,
178+
appIdentifier
179+
);
180+
170181
this.$deviceLogProvider.setProjectDirForDevice(
171182
deviceIdentifier,
172183
appData.projectDir
173184
);
185+
174186
await this.$logcatHelper.start({
175187
deviceIdentifier: this.identifier,
176188
pid: processIdentifier,
189+
appId: appIdentifier,
190+
onAppRestarted: () => {
191+
// If the app restarts, we update the PID and
192+
// restart log helper.
193+
this.onAppLaunch(appData);
194+
},
177195
});
178196
} else {
179197
await this.$logcatHelper.dump(this.identifier);
@@ -228,11 +246,12 @@ export class AndroidApplicationManager extends ApplicationManagerBase {
228246
public async getDebuggableAppViews(
229247
appIdentifiers: string[]
230248
): Promise<IDictionary<Mobile.IDebugWebViewInfo[]>> {
231-
const mappedAppIdentifierPorts = await this.$androidProcessService.getMappedAbstractToTcpPorts(
232-
this.identifier,
233-
appIdentifiers,
234-
TARGET_FRAMEWORK_IDENTIFIERS.Cordova
235-
),
249+
const mappedAppIdentifierPorts =
250+
await this.$androidProcessService.getMappedAbstractToTcpPorts(
251+
this.identifier,
252+
appIdentifiers,
253+
TARGET_FRAMEWORK_IDENTIFIERS.Cordova
254+
),
236255
applicationViews: IDictionary<Mobile.IDebugWebViewInfo[]> = {};
237256

238257
await Promise.all(

lib/common/mobile/android/android-debug-bridge.ts

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ export class AndroidDebugBridge implements Mobile.IAndroidDebugBridge {
5555
childProcessOptions,
5656
{ throwError: false }
5757
);
58-
5958
const errors = this.$androidDebugBridgeResultHandler.checkForErrors(result);
6059

6160
if (errors && errors.length > 0) {

lib/common/mobile/android/android-log-filter.ts

+11-22
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export class AndroidLogFilter implements Mobile.IPlatformLogFilter {
77

88
// sample line is "11-23 12:39:07.310 1584 1597 I art : Background sticky concurrent mark sweep GC freed 21966(1780KB) AllocSpace objects, 4(80KB) LOS objects, 77% free, 840KB/3MB, paused 4.018ms total 158.629ms"
99
// or '12-28 10:45:08.020 3329 3329 W chromium: [WARNING:data_reduction_proxy_settings.cc(328)] SPDY proxy OFF at startup'
10-
private static API_LEVEL_23_LINE_REGEX = /.+?\s+?(?:[A-Z]\s+?)([A-Za-z \.]+?)\s*?\: (.*)/;
10+
private static API_LEVEL_23_LINE_REGEX =
11+
/.+?\s+?(?:[A-Z]\s+?)([A-Za-z \.]+?)\s*?\: (.*)/;
1112

1213
constructor(private $loggingLevels: Mobile.ILoggingLevels) {}
1314

@@ -23,7 +24,9 @@ export class AndroidLogFilter implements Mobile.IPlatformLogFilter {
2324
);
2425
if (log) {
2526
if (log.tag) {
26-
return `${log.tag}: ${log.message}` + EOL;
27+
return (
28+
`${log.tag === "JS" ? "" : `${log.tag}: `}${log.message}` + EOL
29+
);
2730
} else {
2831
return log.message + EOL;
2932
}
@@ -35,33 +38,19 @@ export class AndroidLogFilter implements Mobile.IPlatformLogFilter {
3538
return data + EOL;
3639
}
3740

38-
private getConsoleLogFromLine(lineText: string, pid: string): any {
39-
// filter log line if it does not belong to the current application process id
40-
if (pid && lineText.indexOf(pid) < 0) {
41+
getConsoleLogFromLine(lineText: string, pid: string) {
42+
if (pid && lineText.indexOf(pid) === -1) {
4143
return null;
4244
}
4345

44-
// Messages with category TNS.Native and TNS.Java will be printed by runtime to Logcat only when `__enableVerboseLogging()` is called in the application.
45-
const acceptedTags = [
46-
"chromium",
47-
"Web Console",
48-
"JS",
49-
"ActivityManager",
50-
"System.err",
51-
"TNS.Native",
52-
"TNS.Java",
53-
];
54-
55-
let consoleLogMessage: { tag?: string; message: string };
56-
46+
let consoleLogMessage;
5747
const match =
58-
lineText.match(AndroidLogFilter.LINE_REGEX) ||
59-
lineText.match(AndroidLogFilter.API_LEVEL_23_LINE_REGEX);
48+
lineText.match(AndroidLogFilter.API_LEVEL_23_LINE_REGEX) ||
49+
lineText.match(AndroidLogFilter.LINE_REGEX);
6050

61-
if (match && acceptedTags.indexOf(match[1].trim()) !== -1) {
51+
if (match) {
6252
consoleLogMessage = { tag: match[1].trim(), message: match[2] };
6353
}
64-
6554
return consoleLogMessage;
6655
}
6756
}

lib/common/mobile/android/logcat-helper.ts

+106-14
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { DeviceAndroidDebugBridge } from "./device-android-debug-bridge";
88

99
interface IDeviceLoggingData {
1010
loggingProcess: ChildProcess;
11+
appStartTrackingProcess: ChildProcess;
1112
lineStream: any;
13+
rawLineStream: any;
1214
keepSingleProcess: boolean;
1315
}
1416

@@ -32,16 +34,18 @@ export class LogcatHelper implements Mobile.ILogcatHelper {
3234
loggingProcess: null,
3335
lineStream: null,
3436
keepSingleProcess: options.keepSingleProcess,
37+
appStartTrackingProcess: null,
38+
rawLineStream: null,
3539
};
3640

3741
const logcatStream = await this.getLogcatStream(
3842
deviceIdentifier,
3943
options.pid
4044
);
45+
4146
const lineStream = byline(logcatStream.stdout);
42-
this.mapDevicesLoggingData[
43-
deviceIdentifier
44-
].loggingProcess = logcatStream;
47+
this.mapDevicesLoggingData[deviceIdentifier].loggingProcess =
48+
logcatStream;
4549
this.mapDevicesLoggingData[deviceIdentifier].lineStream = lineStream;
4650
logcatStream.stderr.on("data", (data: Buffer) => {
4751
this.$logger.trace("ADB logcat stderr: " + data.toString());
@@ -71,6 +75,41 @@ export class LogcatHelper implements Mobile.ILogcatHelper {
7175
);
7276
}
7377
});
78+
79+
const appStartTrackingStream = await this.getAppStartTrackingLogcatStream(
80+
deviceIdentifier,
81+
options.appId
82+
);
83+
84+
this.mapDevicesLoggingData[deviceIdentifier].appStartTrackingProcess =
85+
appStartTrackingStream;
86+
87+
const rawLineStream = byline(appStartTrackingStream.stdout);
88+
this.mapDevicesLoggingData[deviceIdentifier].rawLineStream =
89+
rawLineStream;
90+
91+
rawLineStream.on("data", (lineBuffer: Buffer) => {
92+
if (!this.mapDevicesLoggingData[deviceIdentifier]?.loggingProcess)
93+
return;
94+
const lines = (lineBuffer.toString() || "").split("\n");
95+
for (let line of lines) {
96+
// 09-11 17:50:26.311 598 1979 I ActivityTaskManager: START u0 {flg=0x10000000 cmp=org.nativescript.myApp/com.tns.NativeScriptActivity} from uid 2000
97+
// ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^^
98+
// action appId pid
99+
100+
if (
101+
// action
102+
line.includes("START") &&
103+
// appId
104+
line.includes(options.appId) &&
105+
// pid - only if it's not the current pid...
106+
!line.includes(options.pid)
107+
) {
108+
this.forceStop(deviceIdentifier);
109+
options.onAppRestarted?.();
110+
}
111+
}
112+
});
74113
}
75114
}
76115

@@ -108,39 +147,92 @@ export class LogcatHelper implements Mobile.ILogcatHelper {
108147
}
109148

110149
private forceStop(deviceIdentifier: string): void {
111-
this.mapDevicesLoggingData[
112-
deviceIdentifier
113-
].loggingProcess.removeAllListeners();
114-
this.mapDevicesLoggingData[deviceIdentifier].loggingProcess.kill("SIGINT");
115-
this.mapDevicesLoggingData[
116-
deviceIdentifier
117-
].lineStream.removeAllListeners();
150+
const loggingData = this.mapDevicesLoggingData[deviceIdentifier];
151+
loggingData.loggingProcess?.removeAllListeners();
152+
loggingData.loggingProcess?.kill("SIGINT");
153+
loggingData.lineStream?.removeAllListeners();
154+
155+
loggingData.appStartTrackingProcess?.kill("SIGINT");
156+
loggingData.lineStream?.removeAllListeners();
157+
118158
delete this.mapDevicesLoggingData[deviceIdentifier];
119159
}
120160

121-
private async getLogcatStream(deviceIdentifier: string, pid?: string) {
161+
/**
162+
* @deprecated - we likely don't need this anymore, and can simplify the code...
163+
*/
164+
private async isLogcatPidSupported(deviceIdentifier: string) {
122165
const device = await this.$devicesService.getDevice(deviceIdentifier);
123166
const minAndroidWithLogcatPidSupport = "7.0.0";
124-
const isLogcatPidSupported =
167+
return (
125168
!!device.deviceInfo.version &&
126169
semver.gte(
127170
semver.coerce(device.deviceInfo.version),
128171
minAndroidWithLogcatPidSupport
129-
);
172+
)
173+
);
174+
}
175+
176+
private async getLogcatStream(deviceIdentifier: string, pid?: string) {
177+
const isLogcatPidSupported = await this.isLogcatPidSupported(
178+
deviceIdentifier
179+
);
130180
const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(
131181
DeviceAndroidDebugBridge,
132182
{ identifier: deviceIdentifier }
133183
);
134-
const logcatCommand = ["logcat"];
184+
185+
// -T 1 - shows only new logs after starting adb logcat
186+
const logcatCommand = ["logcat", "-T", "1"];
187+
188+
const acceptedTags = [
189+
"chromium",
190+
'"Web Console"',
191+
"JS",
192+
"System.err",
193+
"TNS.Native",
194+
"TNS.Java",
195+
];
135196

136197
if (pid && isLogcatPidSupported) {
137198
logcatCommand.push(`--pid=${pid}`);
199+
200+
acceptedTags.forEach((tag) => {
201+
// -s <tag> - shows only logs with the specified tag
202+
logcatCommand.push("-s", tag);
203+
});
138204
}
205+
139206
const logcatStream = await adb.executeCommand(logcatCommand, {
140207
returnChildProcess: true,
141208
});
209+
142210
return logcatStream;
143211
}
212+
213+
private async getAppStartTrackingLogcatStream(
214+
deviceIdentifier: string,
215+
appId?: string
216+
) {
217+
const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(
218+
DeviceAndroidDebugBridge,
219+
{ identifier: deviceIdentifier }
220+
);
221+
222+
// -b system - shows the system buffer/logs only
223+
// -T 1 - shows only new logs after starting adb logcat
224+
const logcatCommand = [`logcat`, `-b`, `system`, `-T`, `1`];
225+
226+
if (appId) {
227+
logcatCommand.push(`--regex=START.*${appId}`);
228+
}
229+
230+
const appStartTrackingStream = await adb.executeCommand(logcatCommand, {
231+
returnChildProcess: true,
232+
});
233+
234+
return appStartTrackingStream;
235+
}
144236
}
145237

146238
injector.register("logcatHelper", LogcatHelper);

lib/common/mobile/device-log-provider-base.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { IDictionary } from "../declarations";
44

55
export abstract class DeviceLogProviderBase
66
extends EventEmitter
7-
implements Mobile.IDeviceLogProvider {
7+
implements Mobile.IDeviceLogProvider
8+
{
89
protected devicesLogOptions: IDictionary<Mobile.IDeviceLogOptions> = {};
910

1011
constructor(
@@ -50,6 +51,15 @@ export abstract class DeviceLogProviderBase
5051
);
5152
}
5253

54+
public setApplicationIdForDevice(deviceIdentifier: string, appId: string) {
55+
this.setDeviceLogOptionsProperty(
56+
deviceIdentifier,
57+
(deviceLogOptions: Mobile.IDeviceLogOptions) =>
58+
deviceLogOptions.applicationId,
59+
appId
60+
);
61+
}
62+
5363
public setProjectNameForDevice(
5464
deviceIdentifier: string,
5565
projectName: string
@@ -89,6 +99,13 @@ export abstract class DeviceLogProviderBase
8999
);
90100
}
91101

102+
getApplicationIdForDevice(deviceIdentifier: string) {
103+
return (
104+
this.devicesLogOptions[deviceIdentifier] &&
105+
this.devicesLogOptions[deviceIdentifier].applicationId
106+
);
107+
}
108+
92109
protected getDeviceLogOptionsForDevice(
93110
deviceIdentifier: string
94111
): Mobile.IDeviceLogOptions {

lib/common/mobile/device-log-provider.ts

-3
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,6 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
8686
}
8787

8888
private logDataCore(data: string, deviceIdentifier: string): void {
89-
// strip android JS: prefix
90-
data = data.replace(/^JS:\s/, "");
91-
9289
// todo: use config to set logger - --env.classicLogs is temporary!
9390
if ("classicLogs" in (this.$options.env ?? {})) {
9491
// legacy logging

0 commit comments

Comments
 (0)