-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8d98f40
commit fd2ebda
Showing
2 changed files
with
311 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}) |