Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Commit 0598c9c

Browse files
Merge pull request #577 from telerik/vladimirov/filter-device-logs
Add device log filtering
2 parents ed96903 + 6ba4e5a commit 0598c9c

17 files changed

+732
-7
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ Contains common infrastructure for CLIs - mainly AppBuilder and NativeScript.
77
Installation
88
===
99

10-
Latest version: 0.3.0
10+
Latest version: 0.4.0
1111

12-
Release date: 2015, December 21
12+
Release date: 2015, December 29
1313

1414
### System Requirements
1515

appbuilder/device-log-provider.ts

+26-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,33 @@
44
import { EventEmitter } from "events";
55

66
export class DeviceLogProvider extends EventEmitter implements Mobile.IDeviceLogProvider {
7+
private devicesLogLevel: IStringDictionary = {};
8+
9+
constructor(private $logFilter: Mobile.ILogFilter) {
10+
super();
11+
}
12+
713
public logData(line: string, platform: string, deviceIdentifier?: string): void {
8-
this.emit('data', deviceIdentifier, line);
14+
let logLevel = this.$logFilter.loggingLevel;
15+
if(deviceIdentifier) {
16+
logLevel = this.devicesLogLevel[deviceIdentifier] = this.devicesLogLevel[deviceIdentifier] || this.$logFilter.loggingLevel;
17+
}
18+
19+
let data = this.$logFilter.filterData(platform, line, logLevel);
20+
if(data) {
21+
this.emit('data', deviceIdentifier, data);
22+
}
23+
}
24+
25+
public setLogLevel(logLevel: string, deviceIdentifier?: string): void {
26+
if(deviceIdentifier) {
27+
this.devicesLogLevel[deviceIdentifier] = logLevel.toUpperCase();
28+
} else {
29+
this.$logFilter.loggingLevel = logLevel.toUpperCase();
30+
_.each(this.devicesLogLevel, (deviceLogLevel: string, deviceId: string) => {
31+
this.devicesLogLevel[deviceId] = this.$logFilter.loggingLevel;
32+
});
33+
}
934
}
1035
}
1136
$injector.register("deviceLogProvider", DeviceLogProvider);

bootstrap.ts

+5
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,8 @@ $injector.requireCommand("doctor", "./commands/doctor");
104104
$injector.require("utils", "./utils");
105105
$injector.require("bplistParser", "./bplist-parser");
106106
$injector.require("winreg", "./winreg");
107+
108+
$injector.require("loggingLevels", "./mobile/logging-levels");
109+
$injector.require("logFilter", "./mobile/log-filter");
110+
$injector.require("androidLogFilter", "./mobile/android/android-log-filter");
111+
$injector.require("iOSLogFilter", "./mobile/ios/ios-log-filter");

commands/device/device-log-stream.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ export class OpenDeviceLogStreamCommand implements ICommand {
77
constructor(private $devicesService: Mobile.IDevicesService,
88
private $errors: IErrors,
99
private $commandsService: ICommandsService,
10-
private $options: ICommonOptions) { }
10+
private $options: ICommonOptions,
11+
private $deviceLogProvider: Mobile.IDeviceLogProvider,
12+
private $loggingLevels: Mobile.ILoggingLevels) { }
1113

1214
allowedParameters: ICommandParameter[] = [];
1315

1416
public execute(args: string[]): IFuture<void> {
1517
return (() => {
18+
this.$deviceLogProvider.setLogLevel(this.$loggingLevels.full);
19+
1620
this.$devicesService.initialize({ deviceId: this.$options.device, skipInferPlatform: true }).wait();
1721

1822
if (this.$devicesService.deviceCount > 1) {

definitions/mobile.d.ts

+39
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,45 @@ declare module Mobile {
6969

7070
interface IDeviceLogProvider {
7171
logData(line: string, platform: string, deviceIdentifier: string): void;
72+
setLogLevel(level: string, deviceIdentifier?: string): void;
73+
}
74+
75+
/**
76+
* Describes common filtering rules for device logs.
77+
*/
78+
interface ILogFilter {
79+
/**
80+
* The logging level that will be used for filtering in case logLevel is not passed to filterData method.
81+
* Defaults to INFO.
82+
*/
83+
loggingLevel: string;
84+
85+
/**
86+
* Filters data for specified platform.
87+
* @param {string} platform The platform for which is the device log.
88+
* @param {string} data The input data for filtering.
89+
* @param {string} logLevel @optional The logging level based on which input data will be filtered.
90+
* @return {string} The filtered result based on the input or null when the input data shouldn't be shown.
91+
*/
92+
filterData(platform: string, data: string, logLevel?: string): string;
93+
}
94+
95+
/**
96+
* Describes filtering logic for specific platform (Android, iOS).
97+
*/
98+
interface IPlatformLogFilter {
99+
/**
100+
* Filters passed string data based on the passed logging level.
101+
* @param {string} data The string data that will be checked based on the logging level.
102+
* @param {string} logLevel Selected logging level.
103+
* @return {string} The filtered result based on the input or null when the input data shouldn't be shown.
104+
*/
105+
filterData(data: string, logLevel: string): string;
106+
}
107+
108+
interface ILoggingLevels {
109+
info: string;
110+
full: string;
72111
}
73112

74113
interface IDeviceApplicationManager {

helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ export function hook(commandName: string) {
237237
}
238238

239239
export function isFuture(candidateFuture: any): boolean {
240-
return candidateFuture && typeof(candidateFuture.wait) === "function";
240+
return !!(candidateFuture && typeof(candidateFuture.wait) === "function");
241241
}
242242

243243
export function whenAny<T>(...futures: IFuture<T>[]): IFuture<IFuture<T>> {

mobile/android/android-log-filter.ts

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
///<reference path="../../.d.ts"/>
2+
"use strict";
3+
4+
export class AndroidLogFilter implements Mobile.IPlatformLogFilter {
5+
6+
//sample line is "I/Web Console( 4438): Received Event: deviceready at file:///storage/emulated/0/Icenium/com.telerik.TestApp/js/index.js:48"
7+
private static LINE_REGEX = /.\/(.+?)\s*\(\s*\d+?\): (.*)/;
8+
9+
// 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"
10+
// or '12-28 10:45:08.020 3329 3329 W chromium: [WARNING:data_reduction_proxy_settings.cc(328)] SPDY proxy OFF at startup'
11+
private static API_LEVEL_23_LINE_REGEX = /.+?\s+?(?:[A-Z]\s+?)([A-Za-z ]+?)\s*?\: (.*)/;
12+
13+
constructor(private $loggingLevels: Mobile.ILoggingLevels) {}
14+
15+
public filterData(data: string, logLevel: string): string {
16+
let specifiedLogLevel = (logLevel || '').toUpperCase();
17+
if(specifiedLogLevel === this.$loggingLevels.info) {
18+
let log = this.getConsoleLogFromLine(data);
19+
if(log) {
20+
if(log.tag) {
21+
return `${log.tag}: ${log.message}`;
22+
} else {
23+
return log.message;
24+
}
25+
}
26+
27+
return null;
28+
}
29+
30+
return data;
31+
}
32+
33+
private getConsoleLogFromLine(lineText: string): any {
34+
let acceptedTags = ["chromium", "Web Console", "JS"];
35+
let match = lineText.match(AndroidLogFilter.LINE_REGEX) || lineText.match(AndroidLogFilter.API_LEVEL_23_LINE_REGEX);
36+
37+
if (match && acceptedTags.indexOf(match[1].trim()) !== -1) {
38+
return { tag: match[1].trim(), message: match[2] };
39+
}
40+
let matchingTag = _.any(acceptedTags, (tag: string) => { return lineText.indexOf(tag) !== -1; });
41+
return matchingTag ? { message: lineText } : null;
42+
}
43+
}
44+
$injector.register("androidLogFilter", AndroidLogFilter);

mobile/device-log-provider.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
///<reference path="../.d.ts"/>
2+
"use strict";
3+
4+
export class DeviceLogProvider implements Mobile.IDeviceLogProvider {
5+
constructor(private $logFilter: Mobile.ILogFilter,
6+
private $logger: ILogger) { }
7+
8+
public logData(lineText: string, platform: string, deviceIdentifier: string): void {
9+
let data = this.$logFilter.filterData(platform, lineText);
10+
if(data) {
11+
this.$logger.out(data);
12+
}
13+
}
14+
15+
public setLogLevel(logLevel: string, deviceIdentifier?: string): void {
16+
this.$logFilter.loggingLevel = logLevel.toUpperCase();
17+
}
18+
}
19+
$injector.register("deviceLogProvider", DeviceLogProvider);

mobile/ios/ios-log-filter.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
///<reference path="../../.d.ts"/>
2+
"use strict";
3+
4+
export class IOSLogFilter implements Mobile.IPlatformLogFilter {
5+
private static INFO_FILTER_REGEX = /^.*?(AppBuilder|Cordova|NativeScript).*?(<Notice>:.*?(CONSOLE LOG|JS ERROR).*?|<Warning>:.*?|<Error>:.*?)$/im;
6+
7+
constructor(private $loggingLevels: Mobile.ILoggingLevels) {}
8+
9+
public filterData(data: string, logLevel: string): string {
10+
let specifiedLogLevel = (logLevel || '').toUpperCase();
11+
12+
if(specifiedLogLevel === this.$loggingLevels.info) {
13+
let matchingInfoMessage = data.match(IOSLogFilter.INFO_FILTER_REGEX);
14+
return matchingInfoMessage ? matchingInfoMessage[2] : null;
15+
}
16+
17+
return data;
18+
}
19+
}
20+
$injector.register("iOSLogFilter", IOSLogFilter);

mobile/log-filter.ts

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
///<reference path="../.d.ts"/>
2+
"use strict";
3+
4+
export class LogFilter implements Mobile.ILogFilter {
5+
private _loggingLevel: string = this.$loggingLevels.info;
6+
7+
constructor(private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
8+
private $injector: IInjector,
9+
private $loggingLevels: Mobile.ILoggingLevels) {}
10+
11+
public get loggingLevel(): string {
12+
return this._loggingLevel;
13+
}
14+
15+
public set loggingLevel(logLevel: string) {
16+
if(this.verifyLogLevel(logLevel)) {
17+
this._loggingLevel = logLevel;
18+
}
19+
}
20+
21+
public filterData(platform: string, data: string, logLevel?: string): string {
22+
let deviceLogFilter = this.getDeviceLogFilterInstance(platform);
23+
if(deviceLogFilter) {
24+
return deviceLogFilter.filterData(data, logLevel || this.loggingLevel);
25+
}
26+
27+
// In case the platform is not valid, just return the data without filtering.
28+
return data;
29+
}
30+
31+
private getDeviceLogFilterInstance(platform: string): Mobile.IPlatformLogFilter {
32+
if(platform) {
33+
if(platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) {
34+
return this.$injector.resolve("iOSLogFilter");
35+
} else if(platform.toLowerCase() === this.$devicePlatformsConstants.Android.toLowerCase()) {
36+
return this.$injector.resolve("androidLogFilter");
37+
}
38+
}
39+
return null;
40+
}
41+
42+
private verifyLogLevel(logLevel: string): boolean {
43+
let upperCaseLogLevel = (logLevel || '').toUpperCase();
44+
return upperCaseLogLevel === this.$loggingLevels.info.toUpperCase() || upperCaseLogLevel === this.$loggingLevels.full.toUpperCase();
45+
}
46+
47+
}
48+
$injector.register("logFilter", LogFilter);

mobile/logging-levels.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
///<reference path="../.d.ts"/>
2+
"use strict";
3+
4+
export class LoggingLevels implements Mobile.ILoggingLevels {
5+
public info = "INFO";
6+
public full = "FULL";
7+
};
8+
$injector.register("loggingLevels", LoggingLevels);

mobile/mobile-core/devices-service.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export class DevicesService implements Mobile.IDevicesService {
2323
private $androidDeviceDiscovery: Mobile.IDeviceDiscovery,
2424
private $staticConfig: Config.IStaticConfig,
2525
private $messages: IMessages,
26-
private $mobileHelper: Mobile.IMobileHelper) {
26+
private $mobileHelper: Mobile.IMobileHelper,
27+
private $deviceLogProvider: Mobile.IDeviceLogProvider) {
2728
this.attachToDeviceDiscoveryEvents();
2829
}
2930

@@ -40,6 +41,13 @@ export class DevicesService implements Mobile.IDevicesService {
4041
return this.getDeviceInstances().map(deviceInstance => deviceInstance.deviceInfo);
4142
}
4243

44+
/* tslint:disable:no-unused-variable */
45+
@exported("devicesService")
46+
private setLogLevel(logLevel: string, deviceIdentifier?: string): void {
47+
this.$deviceLogProvider.setLogLevel(logLevel, deviceIdentifier);
48+
}
49+
/* tslint:enable:no-unused-variable */
50+
4351
public getDeviceInstances(): Mobile.IDevice[] {
4452
return _.values(this._devices);
4553
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "mobile-cli-lib",
33
"preferGlobal": false,
4-
"version": "0.3.0",
4+
"version": "0.4.0",
55
"author": "Telerik <[email protected]>",
66
"description": "common lib used by different CLI",
77
"bin": {

0 commit comments

Comments
 (0)