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

Making log statements a single line #133

Merged
merged 1 commit into from
May 6, 2020
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
83 changes: 60 additions & 23 deletions packages/core-utils/src/app/log.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
import debug from 'debug'
import { Logger } from '../types'

export const LOG_CR_STRING = '<\\r>'
export const LOG_NEWLINE_STRING = '<\\n>'
export const joinNewlinesAndDebug = (...logs: any[]) => {
const stringifiedLogs = []
for (const l of logs) {
if (typeof l !== 'string') {
stringifiedLogs.push(JSON.stringify(l))
} else {
stringifiedLogs.push(l)
}
}
return debug(
stringifiedLogs
.join(' ')
.replace(/\n/g, LOG_NEWLINE_STRING)
.replace(/\r/g, LOG_CR_STRING)
)
}
export const LOG_NEWLINE_STRING = ' <\\n> '

/**
* Gets a logger specific to the provided identifier.
*
* @param identifier The identifier to use to tag log statements from this logger.
* @param isTest Whether or not this is a test logger.
* @param debugToUseTestOnly The debug instance to use *should only be used for tests*
* @returns a Logger instance.
*/
export const getLogger = (
identifier: string,
isTest: boolean = false
isTest: boolean = false,
debugToUseTestOnly?: debug
): Logger => {
const testString = isTest ? 'test:' : ''
return {
debug: joinNewlinesAndDebug(`${testString}debug:${identifier}`),
info: joinNewlinesAndDebug(`${testString}info:${identifier}`),
warn: joinNewlinesAndDebug(`${testString}warn:${identifier}`),
error: joinNewlinesAndDebug(`${testString}error:${identifier}`),
debug: getLogFunction(
`${testString}debug:${identifier}`,
debugToUseTestOnly
),
info: getLogFunction(`${testString}info:${identifier}`, debugToUseTestOnly),
warn: getLogFunction(`${testString}warn:${identifier}`, debugToUseTestOnly),
error: getLogFunction(
`${testString}error:${identifier}`,
debugToUseTestOnly
),
}
}

Expand All @@ -38,3 +36,42 @@ export const logError = (logger: Logger, message: string, e: Error): void => {
Error: ${e.message}.
Stack: ${e.stack}`)
}

/**
* Converts one or more items to log into a single line string.
*
* @param logs The array of items to log
* @returns The single-line string.
*/
const joinNewLines = (...logs: any[]): string => {
const stringifiedLogs = []
for (const l of logs) {
if (typeof l !== 'string') {
stringifiedLogs.push(JSON.stringify(l))
} else {
stringifiedLogs.push(l)
}
}

return stringifiedLogs.join(' ').replace(/\n/g, LOG_NEWLINE_STRING)
}

/**
* Creates a debug instance with the provided identifier and wraps
* its only function in a function that makes the strings to be logged a single
* line before calling debug(identifier)(log).
*
* @param identifier The identifier used to prepend this log
* @param debugToUseTestOnly The debug instance to use *should only be used for tests*
* @returns The log function for the provided identifier
*/
const getLogFunction = (
identifier: string,
debugToUseTestOnly: debug = debug
): any => {
const d = debugToUseTestOnly(identifier)
return (...logs: any[]): any => {
const singleLine = joinNewLines(...logs)
return d(singleLine)
}
}
67 changes: 67 additions & 0 deletions packages/core-utils/test/app/log.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { getLogger, logError } from '../../src/app'
import { Logger } from '../../src/types'

let lastDebug
const FakeDebug = (identifier) => {
return (...logs: any[]) => {
lastDebug = `${identifier} ${logs.join(' ')}`
}
}

describe('Logger Tests', () => {
let log: Logger
beforeEach(() => {
log = getLogger('derp', true, FakeDebug)
lastDebug = undefined
})

it('logs single line non-error', () => {
const logString = '\n test \n'
logString
.indexOf('\n')
.should.eq(0, 'Cannot find newline when it should be at pos 0')

lastDebug = undefined
log.debug(logString)
lastDebug.indexOf('\n').should.equal(-1, 'Log line has multiple lines!')
})

it('logs single line error', () => {
const e = new Error()
e.stack.indexOf('\n').should.be.gt(-1, 'No new line in stack trace!')

lastDebug = undefined
logError(log, `some error`, e)
lastDebug.indexOf('\n').should.eq(-1, 'Stack trace has new line!')
})

it('logs objects in single line', () => {
const obj = {
test: 'yes',
question: 'answer',
bools: true,
numbers: 735,
somethingVeryLong:
'ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok',
}
lastDebug = undefined
log.debug(obj)
lastDebug.indexOf('\n').should.equal(-1, 'Log line has multiple lines!')
})

it('logs many different things in single line', () => {
const obj = {
test: 'yes',
question: 'answer',
bools: true,
numbers: 735,
somethingVeryLong:
'ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok',
}
const e = new Error('some error here')
const string = '\n test \n'
lastDebug = undefined
log.debug(obj, e, string)
lastDebug.indexOf('\n').should.equal(-1, 'Log line has multiple lines!')
})
})