Skip to content

Commit

Permalink
feat(logger): add the ton logger
Browse files Browse the repository at this point in the history
  • Loading branch information
trylovetom committed Apr 26, 2020
1 parent 8d98f40 commit fd2ebda
Show file tree
Hide file tree
Showing 2 changed files with 311 additions and 0 deletions.
176 changes: 176 additions & 0 deletions packages/logger/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import {
formatContext,
printWithColor,
print,
LogLevel,
printWith,
defaultColors
} from './index'

let originalStdoutWrite = process.stdout.write
const originalEnvLogLevel = process.env.LOG_LEVEL

beforeEach(() => {
originalStdoutWrite = process.stdout.write
process.stdout.write = jest.fn()
})

afterEach(() => {
process.stdout.write = originalStdoutWrite
delete process.env.LOG_LEVEL
if (originalEnvLogLevel) {
process.env.LOG_LEVEL = originalEnvLogLevel
}
})

describe('formatContext', () => {
it(`should just add break line, \
if type of context is not object.`, () => {
const input = 'text'
const output = `${input}\n`
expect(formatContext(input)).toBe(output)
})

it(`should stringify the object and add break line, \
if type of context is an object.`, () => {
const input = { sub: { text: 'text' } }
const output = `${JSON.stringify(input)}\n`
expect(formatContext(input)).toBe(output)
})

it(`should stringify the object with 2 spaces and add break line, \
if type of context is an object.`, () => {
const input = { sub: { text: 'text' } }
const output = `${JSON.stringify(input, undefined, 2)}\n`
expect(formatContext(input, true)).toBe(output)
})
})

describe('printWithColor', () => {
it(`should write the process.stdout without color, \
if color is not setup.`, () => {
const input = 'text'
const output = input
printWithColor(input)
expect(process.stdout.write).toBeCalledWith(output)
})

it(`should write the process.stdout with color, \
if color is setup.`, () => {
const input = 'text'
const color = '90'
const output = `\u001B[${color}m${input}\u001B[m`
printWithColor(input, color)
expect(process.stdout.write).toBeCalledWith(output)
})
})

describe('print', () => {
it(`should write the process.stdout, \
if level is higher or equal to default level(debug).`, () => {
const input = 'text'
const output = `${input}\n`
print({}, input, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
expect(process.stdout.write).toHaveBeenNthCalledWith(2, output)
})

it(`should not write the process.stdout, \
if level is smaller than process.env.LOG_LEVEL = error.`, () => {
const input = 'text'
process.env.LOG_LEVEL = 'error'
print({ level: LogLevel.warn }, input)
expect(process.stdout.write).toHaveBeenCalledTimes(0)
})

it(`should write the stack to process.stdout, \
if context is error.`, () => {
const input = new Error('Hi There!')
const outputStack = `${input.stack}\n`
process.env.LOG_LEVEL = 'error'
print({ level: LogLevel.error }, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, outputStack)
})

it(`should write the message to process.stdout, \
if context is error and stack is missing.`, () => {
const input = new Error('Hi There!')
const outputMessage = `${input.message}\n`
input.stack = ''
process.env.LOG_LEVEL = 'error'
print({ level: LogLevel.error }, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, outputMessage)
})
})

describe('printWith', () => {
it('should create custom logger', () => {
const input = 'text'
const color = '96'
const output = `\u001B[${color}m${input}\n\u001B[m`
const customLogger = printWith({ level: LogLevel.warn, color })
customLogger(input, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
expect(process.stdout.write).toHaveBeenNthCalledWith(2, output)
})

it('should print break line, if argument is empty', () => {
const color = '96'
const output = `\u001B[${color}m\n\u001B[m`
const customLogger = printWith({ level: LogLevel.warn, color })
customLogger()
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
})
})

describe('logger', () => {
it('error', () => {
const input = 'text'
const color = defaultColors.error
const output = `\u001B[${color}m${input}\n\u001B[m`
const customLogger = printWith({ level: LogLevel.warn, color })
customLogger(input, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
expect(process.stdout.write).toHaveBeenNthCalledWith(2, output)
})

it('warn', () => {
const input = 'text'
const color = defaultColors.warn
const output = `\u001B[${color}m${input}\n\u001B[m`
const customLogger = printWith({ level: LogLevel.warn, color })
customLogger(input, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
expect(process.stdout.write).toHaveBeenNthCalledWith(2, output)
})

it('info', () => {
const input = 'text'
const color = defaultColors.info
const output = `\u001B[${color}m${input}\n\u001B[m`
const customLogger = printWith({ level: LogLevel.warn, color })
customLogger(input, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
expect(process.stdout.write).toHaveBeenNthCalledWith(2, output)
})

it('debug', () => {
const input = 'text'
const color = defaultColors.debug
const output = `\u001B[${color}m${input}\n\u001B[m`
const customLogger = printWith({ level: LogLevel.warn, color })
customLogger(input, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
expect(process.stdout.write).toHaveBeenNthCalledWith(2, output)
})

it('verbose', () => {
const input = 'text'
const color = defaultColors.verbose
const output = `\u001B[${color}m${input}\n\u001B[m`
const customLogger = printWith({ level: LogLevel.warn, color })
customLogger(input, input)
expect(process.stdout.write).toHaveBeenNthCalledWith(1, output)
expect(process.stdout.write).toHaveBeenNthCalledWith(2, output)
})
})
135 changes: 135 additions & 0 deletions packages/logger/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
function isObject(target: any): target is object {
return target !== null && typeof target === 'object'
}

function isError(target: any): target is Error {
return target instanceof Error
}

export enum LogLevel {
error = 0,
warn = 1,
info = 2,
debug = 3,
verbose = 4
}

export type LogColor = {
error: string
warn: string
info: string
debug: string
verbose: string
}

/**
* ref: https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit
*/
export const defaultColors: LogColor = {
error: '91',
warn: '93',
info: '92',
debug: '97',
verbose: '90'
}

/**
* format the context
* @param context something you want to format
* @param format should beautify the object?
*/
export function formatContext(context: any, format?: boolean) {
if (!isObject(context)) {
return `${context}\n`
}

if (!format) {
return `${JSON.stringify(context)}\n`
}

return `${JSON.stringify(context, undefined, 2)}\n`
}

/**
* print the context with color
* @param context something you want to print
* @param color ansi code
* @see https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit
*/
export function printWithColor(context: any, color?: string) {
if (!color) {
process.stdout.write(context)
return
}

process.stdout.write(`\u001B[${color}m${context}\u001B[m`)
}

export type LogOptions = {
level?: LogLevel
color?: string
format?: boolean
}

/**
* print it, suggest use the `printWith` to create customer logger
* @param param0 the options of logger
* @param args something you want to print
*/
export function print(
{ level = LogLevel.debug, color, format }: LogOptions,
...args: any[]
) {
if (level <= LogLevel[process.env.LOG_LEVEL || 'debug']) {
args.forEach(context => {
if (isError(context)) {
printWithColor(
formatContext(context.stack || context.message, format),
color
)
return
}
printWithColor(formatContext(context, format), color)
})
}
}

/**
* create the custom logger function
* @param options the options of your custom logger
* @example
* const error = printWith({
* level: LogLevel.error,
* color: defaultColors.error
* })
* error('Hello World!')
*
*/
export function printWith(options: LogOptions) {
return (message: any = '', ...args: any[]) => print(options, message, ...args)
}

export const error = printWith({
level: LogLevel.error,
color: defaultColors.error
})

export const warn = printWith({
level: LogLevel.warn,
color: defaultColors.warn
})

export const info = printWith({
level: LogLevel.info,
color: defaultColors.info
})

export const debug = printWith({
level: LogLevel.debug,
color: defaultColors.debug
})

export const verbose = printWith({
level: LogLevel.verbose,
color: defaultColors.verbose
})

0 comments on commit fd2ebda

Please sign in to comment.