Skip to content
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

feat: improved logging and server UI #628

Merged
merged 4 commits into from
Aug 13, 2019
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
4 changes: 4 additions & 0 deletions packages/haul-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@
"resolve": "^1.12.0",
"semver": "^6.3.0",
"source-map": "^0.7.3",
"strip-ansi": "5.2.0",
"terminal-kit": "^1.30.0",
"terser": "^4.1.3",
"utility-types": "^3.7.0",
"webpack": "^4.39.1",
"wrap-ansi": "^6.0.0",
"ws": "^6.2.1"
},
"devDependencies": {
Expand All @@ -68,7 +70,9 @@
"@types/node-fetch": "^2.5.0",
"@types/resolve": "^0.0.8",
"@types/semver": "^6.0.1",
"@types/strip-ansi": "^5.2.1",
"@types/terminal-kit": "^1.28.0",
"@types/wrap-ansi": "^3.0.0",
"@types/ws": "^6.0.2"
}
}
61 changes: 54 additions & 7 deletions packages/haul-core/src/runtime/Logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { inspect } from 'util';
import fs from 'fs';
import path from 'path';
import stripAnsi from 'strip-ansi';
import { LoggerEvent } from '@haul-bundler/inspector-events';
import {
container,
Expand Down Expand Up @@ -48,6 +49,11 @@ export default class Logger {

constructor(private inspectorClient?: InspectorClient) {}

/**
* Enables logging all messages to file as well as to process' STDOUT.
* If `json` is `true` each log will be in JSON format for easier processing.
* If relative `filename` is passed, it will be resolved based on process' CWD.
*/
enableFileLogging(filename: string, { json }: { json: boolean }) {
const absFilename = path.isAbsolute(filename)
? filename
Expand All @@ -56,6 +62,11 @@ export default class Logger {
this.logAsJson = json;
}

/**
* Disposes the logger by closing all open handles.
* If file logging was enabled, the file descriptor will be closed here.
* Should always be called before exiting from process.
*/
dispose() {
if (this.logFile !== undefined) {
fs.closeSync(this.logFile);
Expand All @@ -68,27 +79,45 @@ export default class Logger {
done = this.createLoggingFunction(LoggerLevel.Done);
debug = this.createLoggingFunction(LoggerLevel.Debug);

/**
* Enables proxy for all logs.
* Messages will be passed to `handler` function and __won't be logged__ to process' STDOUT.
* Returns a dispose function to disable the proxy.
*/
proxy = (handler: ProxyHandler): (() => void) => {
this.proxyHandler = handler;
return () => {
this.proxyHandler = undefined;
};
};

/**
* Prints arguments _as is_ without any processing.
*/
print = (...args: unknown[]) => {
// eslint-disable-next-line no-console
console.log(...args);
};

/**
* Enhances given arguments with ANSI color.
*/
enhanceWithColor = (enhancer: AnsiColor, ...args: unknown[]) => {
return color(enhancer, this.stringify(args).join(' ')).build();
};

/**
* Enhances given arguments with ANSI modifier, for example with `bold`, `italic` etc.
*/
enhanceWithModifier = (enhancer: AnsiModifier, ...args: unknown[]) => {
return modifier(enhancer, this.stringify(args).join(' ')).build();
};

enhance = (level: LoggerLevel, ...args: unknown[]) => {
/**
* Enhances given arguments with level prefix.
* Example: info ▶︎ argument1 argument2
*/
enhanceWithLevel = (level: LoggerLevel, ...args: unknown[]) => {
return container(
color(levelToColorMappings[level], modifier('bold', level)),
pad(1),
Expand All @@ -98,8 +127,20 @@ export default class Logger {
).build();
};

/**
* Stringify array of elements into a string array.
* Uses Node's built-in `util.inspect` function to stringify non-string elements.
*/
stringify(args: any[]) {
return args.map(item => (typeof item === 'string' ? item : inspect(item)));
return args.map(item =>
typeof item === 'string'
? item
: inspect(item, {
depth: null,
maxArrayLength: null,
breakLength: Infinity,
})
);
}

private createLoggingFunction(level: LoggerLevel) {
Expand All @@ -109,13 +150,19 @@ export default class Logger {
}

if (this.logFile !== undefined) {
const rawArgs = this.stringify(args).map(stripAnsi);
fs.appendFileSync(
this.logFile,
(this.logAsJson
? JSON.stringify({ timestamp: new Date(), level, messages: args })
: `[${new Date().toISOString()}] ${level}: ${this.stringify(
args
).join()}`) + '\n',
? stripAnsi(
JSON.stringify({
timestamp: new Date(),
level,
messages: rawArgs,
})
)
: `[${new Date().toISOString()}] ${level}: ${rawArgs.join(' ')}`) +
'\n',
'utf8'
);
}
Expand All @@ -127,7 +174,7 @@ export default class Logger {
if (this.proxyHandler) {
this.proxyHandler(level, ...args);
} else {
this.print(this.enhance(level, ...args));
this.print(this.enhanceWithLevel(level, ...args));
}
}
};
Expand Down
18 changes: 6 additions & 12 deletions packages/haul-core/src/server/InteractiveUI.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Terminal } from 'terminal-kit';
import { container, color, modifier, pad } from 'ansi-fragments';
import wrapAnsi from 'wrap-ansi';
import UserInterface from './UI';

class Logs {
Expand Down Expand Up @@ -27,17 +28,10 @@ class Logs {
addItem(item: string) {
const lines = item.split('\n').reduce(
(acc, line) => {
if (line.length > this.maxLineWidth) {
const subLines =
line.match(new RegExp(`.{1,${this.maxLineWidth}}`, 'g')) || [];
if (subLines) {
return acc.concat(...subLines);
}

return acc;
}

return acc.concat(line);
const wrappedLine = wrapAnsi(line, this.maxLineWidth, {
hard: true,
});
return acc.concat(...wrappedLine.split('\n'));
},
[] as string[]
);
Expand Down Expand Up @@ -180,7 +174,7 @@ export default class InteractiveUserInterface implements UserInterface {
start(platforms: string[]) {
this.logs.sliceStart = 0;
this.logs.sliceMaxLength = this.terminal.height - platforms.length - 6;
this.logs.maxLineWidth = this.terminal.width - 10;
this.logs.maxLineWidth = this.terminal.width - 2;

this.terminal.fullscreen(true);
this.terminal.grabInput({ mouse: 'motion' });
Expand Down
10 changes: 7 additions & 3 deletions packages/haul-core/src/server/NonInteractiveUI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ export default class NonInteractiveUserInterface implements UserInterface {
) {
if (running) {
const percent = `${Math.floor(Math.min(1, value) * 100)}%`;
this.runtime.logger.info(`Compilation - running (${percent})`);
this.runtime.logger.info(
`[${platform.toUpperCase()}] Compilation - running (${percent})`
);
} else {
this.runtime.logger.info(`Compilation - idle`);
this.runtime.logger.info(
`[${platform.toUpperCase()}] Compilation - idle`
);
}
}

addLogItem(item: string) {
// `item` should be already enhanced with ANSI escape codes
this.runtime.logger.print(item);
this.runtime.logger.print('print', item);
}

dispose(exitCode: number = 0, exit: boolean = true) {
Expand Down
17 changes: 11 additions & 6 deletions packages/haul-core/src/server/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default class Server {
value: 0,
});
this.ui.addLogItem(
this.runtime.logger.enhance(Logger.Level.Error, message)
this.runtime.logger.enhanceWithLevel(Logger.Level.Error, message)
);
}
);
Expand All @@ -107,7 +107,7 @@ export default class Server {
});
errors.forEach(error => {
this.ui.addLogItem(
this.runtime.logger.enhance(Logger.Level.Error, error)
this.runtime.logger.enhanceWithLevel(Logger.Level.Error, error)
);
});
}
Expand Down Expand Up @@ -142,7 +142,9 @@ export default class Server {
// in interactive mode.
if (!this.options.noInteractive) {
this.disposeLoggerProxy = this.runtime.logger.proxy((level, ...args) => {
this.ui.addLogItem(this.runtime.logger.enhance(level, ...args));
this.ui.addLogItem(
this.runtime.logger.enhanceWithLevel(level, ...args)
);
});
}

Expand Down Expand Up @@ -219,13 +221,13 @@ export default class Server {

console.log = (...args) => {
this.ui.addLogItem(
this.runtime.logger.enhance(Logger.Level.Info, ...args)
this.runtime.logger.enhanceWithLevel(Logger.Level.Info, ...args)
);
};

console.error = (...args) => {
this.ui.addLogItem(
this.runtime.logger.enhance(Logger.Level.Error, ...args)
this.runtime.logger.enhanceWithLevel(Logger.Level.Error, ...args)
);
};

Expand All @@ -239,12 +241,15 @@ export default class Server {
logServerEvent(request: Hapi.Request, event?: Hapi.RequestEvent) {
const { statusCode } = request.response as ResponseObject;
let logColor: AnsiColor = 'green';
let level: 'info' | 'warn' | 'error' = 'info';
if (statusCode >= 300 && statusCode < 400) {
logColor = 'yellow';
level = 'warn';
} else if (statusCode >= 400) {
logColor = 'red';
level = 'error';
}
this.ui.addLogItem(
this.runtime.logger[level](
container(
color(logColor, modifier('bold', request.method.toUpperCase())),
pad(1),
Expand Down
Loading