Skip to content

Commit

Permalink
feat: cleaner log output (#5680)
Browse files Browse the repository at this point in the history
* feat: cleaner log output
* feat: break logs wider than the terminal width into new lines
* fix: reduce ios log predicate scope
* chore: add todo notes
* feat: allow using classicLogs with a temporary --env.classicLogs flag
  • Loading branch information
rigor789 authored Jul 25, 2022
1 parent e958766 commit 5330207
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 7 deletions.
3 changes: 1 addition & 2 deletions lib/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ export class EmulatorDiscoveryNames {
}

export const DEVICE_LOG_EVENT_NAME = "deviceLogData";
export const IOS_LOG_PREDICATE =
'senderImagePath contains "NativeScript" || eventMessage contains[c] "NativeScript"';
export const IOS_LOG_PREDICATE = 'senderImagePath contains "NativeScript"';
export const IOS_APP_CRASH_LOG_REG_EXP = /Fatal JavaScript exception \- application has been terminated/;
export const FAIL_LIVESYNC_LOG_REGEX = /Failed to refresh the application with RefreshRequest./;

Expand Down
137 changes: 132 additions & 5 deletions lib/common/mobile/device-log-provider.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { DeviceLogProviderBase } from "./device-log-provider-base";
import { DEVICE_LOG_EVENT_NAME } from "../constants";
import { LoggerConfigData } from "../../constants";
import { injector } from "../yok";

import * as chalk from "chalk";
import { LoggerConfigData } from "../../constants";
import { IOptions } from "../../declarations";

export class DeviceLogProvider extends DeviceLogProviderBase {
constructor(
protected $logFilter: Mobile.ILogFilter,
protected $logger: ILogger,
protected $logSourceMapService: Mobile.ILogSourceMapService
protected $logSourceMapService: Mobile.ILogSourceMapService,
protected $options: IOptions
) {
super($logFilter, $logger, $logSourceMapService);
}
Expand All @@ -17,6 +21,7 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
platform: string,
deviceIdentifier: string
): void {
// console.log(lineText)
const loggingOptions = this.getDeviceLogOptionsForDevice(deviceIdentifier);
let data = this.$logFilter.filterData(platform, lineText, loggingOptions);
data = this.$logSourceMapService.replaceWithOriginalFileLocations(
Expand All @@ -25,7 +30,7 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
loggingOptions
);
if (data) {
this.logDataCore(data);
this.logDataCore(data, deviceIdentifier);
this.emit(DEVICE_LOG_EVENT_NAME, lineText, deviceIdentifier, platform);
}
}
Expand All @@ -34,8 +39,130 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
this.$logFilter.loggingLevel = logLevel.toUpperCase();
}

private logDataCore(data: string): void {
this.$logger.info(data, { [LoggerConfigData.skipNewLine]: true });
private consoleLogLevelRegex: RegExp = /^CONSOLE (LOG|INFO|WARN|ERROR|TRACE|INFO( .+)):\s/;
private consoleLevelColor: Record<string, (line: string) => string> = {
log: (line) => line,
info: chalk.cyanBright,
warn: chalk.yellowBright,
error: chalk.redBright,
trace: chalk.grey,
time: chalk.greenBright,
};

private deviceColorMap = new Map<string, typeof chalk.BackgroundColor>();

private colorPool: typeof chalk.BackgroundColor[] = [
"bgGray",
"bgMagentaBright",
"bgBlueBright",
"bgWhiteBright",
"bgCyanBright",
"bgYellowBright",
"bgGreenBright",
];
private colorPoolIndex = 0;

private getDeviceColor(deviceIdentifier: string) {
if (this.deviceColorMap.has(deviceIdentifier)) {
return this.deviceColorMap.get(deviceIdentifier);
}

const color = this.colorPool[this.colorPoolIndex];
// wrap around if we have no more colors in the pool
this.colorPoolIndex =
this.colorPoolIndex === this.colorPool.length - 1
? 0
: this.colorPoolIndex + 1;

this.deviceColorMap.set(deviceIdentifier, color);

return color;
}

private logDataCore(data: string, deviceIdentifier: string): void {
// todo: use config to set logger - --env.classicLogs is temporary!
if ("classicLogs" in this.$options.env) {
// legacy logging
this.$logger.info(data, { [LoggerConfigData.skipNewLine]: true });
return;
}

// todo: extract into an injectable printer/logger service
let shouldPrepend = false;
let splitIndexes: number[] = [];
const lines = data
.split(/\n(CONSOLE)/)
.map((line, index, lines) => {
if (line === "CONSOLE") {
shouldPrepend = true;

if (lines[index - 1]) {
splitIndexes.push(index - 1);
}

return null;
}

if (shouldPrepend) {
shouldPrepend = false;
return `CONSOLE${line}`;
}

const suffix = line.endsWith("\n") ? "" : "\n";
return line + suffix;
})
.map((line, index) => {
if (splitIndexes.includes(index)) {
return line + "\n";
}
return line;
})
.filter((line) => {
return line !== null;
});

if (!lines.length && data.length) {
lines.push(data);
}

for (const line of lines) {
let [match, level, timeLabel] =
this.consoleLogLevelRegex.exec(line) ?? [];

if (timeLabel) {
level = "time";
timeLabel = timeLabel.replace("INFO ", "").trim() + ": ";
} else {
level = level?.toLowerCase() ?? "log";
}

const toLog = [timeLabel ?? "", match ? line.replace(match, "") : line]
.join("")
.trim();

toLog.split("\n").forEach((actualLine) => {
this.printLine(
chalk[this.getDeviceColor(deviceIdentifier)](" "),
this.consoleLevelColor[level](actualLine)
);
});
}
}

private printLine(prefix: string, ...parts: string[]) {
const maxWidth = process.stdout.columns - 2;
const fullLine = parts.join(" ");

// console.log(prefix, fullLine);
// return;
if (fullLine.length < maxWidth) {
console.log(prefix, fullLine);
} else {
for (let i = 0; i < fullLine.length; i += maxWidth) {
const part = fullLine.substring(i, i + maxWidth);
console.log(prefix, part);
}
}
}
}
injector.register("deviceLogProvider", DeviceLogProvider);

0 comments on commit 5330207

Please sign in to comment.