Skip to content

Commit

Permalink
fix: error logging (#1290)
Browse files Browse the repository at this point in the history
  • Loading branch information
kellymears committed Mar 22, 2022
1 parent b854f95 commit f9c5302
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 97 deletions.
11 changes: 4 additions & 7 deletions sources/@roots/bud-compiler/src/Compiler/compiler.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,13 @@ export class Compiler extends Service implements Contract {
chalk.blue(`[${stage}]`),
...message,
)
: this.logger.log(
: this.stats?.errorsCount > 0 &&
this.logger.log(
statusColor(`[${percent}%]`),
statusColor(
this.stats?.errorsCount > 0
? `Compiled with errors`
: `Compiled`,
),
statusColor(`Compiled with errors`),
)
} catch (error) {
this.app.error(error)
this.app.warn(error)
}
}
}
98 changes: 61 additions & 37 deletions sources/@roots/bud-framework/src/Framework/framework.process.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
import readline from 'node:readline'
import {boxen, fs} from '@roots/bud-support'

import {Framework} from '.'

const {removeSync} = fs

/**
* Render error
*/
const renderError = (msg: string, name?: string) => {
global.process.stderr.write(
boxen(msg, {
title: name ?? 'error',
borderStyle: 'bold',
borderColor: 'red',
}),
)
}

/**
* Create an error handler
*/
const curryHandler = function (code: number) {
const ERROR = code !== 0

const close = () => {
if (ERROR) removeSync(this.path('@storage/cache'))

global.process.exitCode = code
global.process.exit()
}

return (exitMessage: string | Error) => {
const exit = () => setTimeout(close, 100).unref()

if (!ERROR) return exit()

if (exitMessage instanceof Error) {
renderError(exitMessage.message, exitMessage.name)
} else {
renderError(exitMessage, 'error')
}

return exit()
}
}

/**
* Registers a callback for all kinds of application shutdown events.
*
Expand All @@ -14,46 +57,27 @@ import {Framework} from '.'
* @public
*/
export const initialize = (app: Framework) => {
const keypressListener = (_chunk, key) => {
key?.name == 'q' && app.close(process.exit)
}
const makeHandler = curryHandler.bind(app)

const exit = (code: number) => {
process.exitCode = code
process.exit(process.exitCode)
}

const makeHandle = (code: number) => msg => {
process.exitCode = code
code === 0 && app.logger.instance.info(msg)

if (code !== 0) {
typeof msg == 'string'
? app.logger.instance.error(msg)
: Array.isArray(msg)
? msg.map(app.logger.instance.error)
: Object.entries(msg).map(app.logger.instance.error)
}
global.process
// only works when there is no task running
// because we have a server always listening port, this handler will NEVER execute
.on('beforeExit', makeHandler(0))

setTimeout(() => exit(code), 500).unref()
}
// only works when the global.process normally exits
// on windows, ctrl-c will not trigger this handler (it is unnormal)
// unless you listen on 'SIGINT'
.on('exit', makeHandler(0))

const beforeExit = () => {
process.stdin.removeAllListeners()
}
// catch ctrl-c, so that event 'exit' always works
.on('SIGINT', makeHandler(0))

process.exitCode = 0
process
.on('beforeExit', beforeExit)
.on('SIGTERM', makeHandle(0))
.on('SIGINT', makeHandle(0))
.on('uncaughtException', makeHandle(1))
.on('unhandledRejection', makeHandle(1))
// kill-9
.on('SIGTERM', makeHandler(0))

readline.emitKeypressEvents(process.stdin)
// exit with errors
.on('uncaughtException', makeHandler(1))

process.stdin.isTTY &&
!process.env.JEST_WORKER_ID &&
!process.env.CI &&
process.stdin.setRawMode(true).on('keypress', keypressListener)
// exit with errors
.on('unhandledRejection', makeHandler(1))
}
43 changes: 26 additions & 17 deletions sources/@roots/bud-framework/src/Framework/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
import {
boxen,
HighlightOptions,
PrettyFormatOptions,
} from '@roots/bud-support'
Expand Down Expand Up @@ -715,8 +716,6 @@ export abstract class Framework {
}),
)}`,
)

process.exit(1)
}

/**
Expand All @@ -731,11 +730,15 @@ export abstract class Framework {
@bind
public error(...messages: any[]) {
this.logger.instance.error(...messages)

process.exitCode = 1
process.exit()
if (this.isProduction) {
process.exitCode = 1
process.exit()
}
}

/**
* Dump object and return Framework
*/
@bind
public dump(
obj: any,
Expand All @@ -751,19 +754,25 @@ export abstract class Framework {

// eslint-disable-next-line no-console
process.stdout.write(
`${options?.prefix ? `\n${options.prefix}\n` : `\n`}${highlight(
format(obj, {
callToJSON: false,
maxDepth: 8,
printFunctionName: false,
escapeString: false,
...prettyFormatOptions,
}),
boxen(
highlight(
format(obj, {
callToJSON: false,
maxDepth: 8,
printFunctionName: false,
escapeString: false,
...prettyFormatOptions,
}),
{
language: options?.language ?? 'typescript',
ignoreIllegals: options?.ignoreIllegals ?? true,
},
),
{
language: options?.language ?? 'typescript',
ignoreIllegals: options?.ignoreIllegals ?? true,
title: options.prefix ?? 'object dump',
borderStyle: 'round',
},
)}`,
),
)

return this
Expand Down
15 changes: 0 additions & 15 deletions sources/@roots/bud-framework/src/Framework/methods/close.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {chalk, fs} from '@roots/bud-support'

import {Framework} from '..'

/**
Expand All @@ -24,19 +22,6 @@ export interface close {
export function close(callback?: any) {
const ctx = this as Framework

process.stdin.removeAllListeners()

if (process.exitCode !== 0) {
ctx.logger.instance.error(
chalk.red(`\nClearing cache due to non-zero exit code.\n`),
)
fs.removeSync(ctx.path('@storage/cache'))
}

process.exitCode === 0
? ctx.context.stdout.write(chalk.green(`\n✔ that's a wrap\n`))
: ctx.context.stderr.write(chalk.red(`\n✖ Error\n`))

ctx.hooks.fire('event.app.close')

callback ? callback() : process.exit()
Expand Down
19 changes: 13 additions & 6 deletions sources/@roots/bud-framework/src/Logger/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {types} from './logger.constants'
import {LEVEL, types} from './logger.constants'
import {Signale} from './logger.dependencies'
import type {Framework} from './logger.interface'

Expand All @@ -15,20 +15,27 @@ export class Logger {
*/
public instance: Signale

public get level() {
if (!this.app.context.args.log) return LEVEL.ERROR
if (!this.app.context.args.verbose) return LEVEL.STANDARD
return LEVEL.VERBOSE
}

public get interactive() {
return !this.app.context.args.log ? true : false
}

/**
* Class constructor
*
* @public
*/
public constructor(private app: Framework) {
this.instance = new Signale({
disabled: this.app.context.args.log !== true,
interactive: false,
interactive: this.interactive,
secrets: [this.app.context.projectDir, this.app.context.cwd],
logLevel: this.app.context.args.verbose ? 'vvvv' : 'vvv',
logLevel: this.level,
types: types(app),
// @ts-ignore
stream: [this.app.context.stdout],
})

this.instance.config({
Expand Down
23 changes: 10 additions & 13 deletions sources/@roots/bud-framework/src/Logger/logger.constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {figures} from './figures'
import {Framework} from './logger.interface'

/**
* Instance configuration
Expand All @@ -25,43 +26,39 @@ export const INSTANCE_CONFIG: any = {
interface Type {
/** The icon corresponding to the logger. */
badge: string

/**
* The color of the label, can be any of the foreground colors supported by
* [chalk](https://github.com/chalk/chalk#colors).
*/
color: string

/** The label used to identify the type of the logger. */
label: string

logLevel?: string | undefined
stream?: NodeJS.WriteStream | NodeJS.WriteStream[] | undefined
}

/**
* @internal
*/
export interface types {
[key: string]: Type
stream?: NodeJS.WriteStream | NodeJS.WriteStream[] | undefined
}

export const enum LEVEL {
VERBOSE = 'log',
STANDARD = 'timer',
ERROR = 'error',
}

export const types = app => ({
export const types: (app: Framework) => Record<string, Type> = app => ({
error: {
badge: figures.cross,
color: 'red',
label: 'error',
logLevel: LEVEL.STANDARD,
process: [app.context.stderr, app.context.stdout],
logLevel: LEVEL.ERROR,
},
fatal: {
badge: figures.cross,
color: 'red',
label: 'fatal',
logLevel: LEVEL.STANDARD,
process: [app.context.stderr],
logLevel: LEVEL.ERROR,
},
star: {
badge: figures.star,
Expand All @@ -73,7 +70,7 @@ export const types = app => ({
badge: figures.info,
color: 'magenta',
label: 'log',
logLevel: LEVEL.STANDARD,
logLevel: LEVEL.VERBOSE,
},
success: {
badge: figures.tick,
Expand Down
2 changes: 1 addition & 1 deletion sources/@roots/bud/src/cli/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ export class DoctorCommand extends BaseCommand {
this.app.error(error)
}

this.app.close(process.exit)
this.app.close()
}
}
2 changes: 1 addition & 1 deletion sources/@roots/bud/src/cli/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export class InstallCommand extends BaseCommand {
`This command was deprecated in 5.3.0. In the future this command will throw an error.\n`,
)

this.app.close(process.exit)
this.app.close()
}
}

0 comments on commit f9c5302

Please sign in to comment.