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

fix: RedwoodLogger -- Mask errors, but include stack trace in formatter #5704

Merged
merged 9 commits into from
Jun 15, 2022
27 changes: 27 additions & 0 deletions packages/api-server/src/__tests__/logFormatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,31 @@ describe('LogFormatter', () => {
})
).toMatch('"foo": "bar"')
})

test('Should format error stack traces', () => {
expect(
logFormatter({
level: 50,
err: {
message: 'This error has a stack traces',
stack:
'A stack trace \n will have \n several lines \n at some line number \n at some code',
},
})
).toMatch(/at some line number/)
})

test('Should format error and include the error type', () => {
expect(
logFormatter({
level: 50,
err: {
type: 'GraphQL Error',
message: 'This error has a stack traces',
stack:
'A stack trace \n will have \n several lines \n at some line number \n at some code',
},
})
).toMatch(/GraphQL Error Info/)
})
})
29 changes: 23 additions & 6 deletions packages/api-server/src/logFormatter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,14 @@ export const LogFormatter = () => {
output.push(formatTracing(tracing))
}

if (stack != null) {
output.push(formatStack(stack))
}

if (err != null) {
output.push(formatErrorProp(err))
}

if (stack != null) {
output.push(formatStack(stack))
}

return output.filter(noEmpty).join(' ')
}

Expand Down Expand Up @@ -223,7 +223,20 @@ export const LogFormatter = () => {
}

const formatErrorProp = (errorPropValue: any) => {
return newline + JSON.stringify({ err: errorPropValue }, null, 2)
const errorType = errorPropValue['type'] || 'Error'
delete errorPropValue['message']
delete errorPropValue['stack']
delete errorPropValue['type']

return chalk.redBright(
newline +
newline +
`🚨 ${errorType} Info` +
newline +
newline +
JSON.stringify(errorPropValue, null, 2) +
newline
)
}

const formatLevel = (level: any) => {
Expand Down Expand Up @@ -321,7 +334,11 @@ export const LogFormatter = () => {
}

const formatStack = (stack: any) => {
return stack ? newline + stack : ''
return chalk.redBright(
stack
? newline + '🥞 Error Stack' + newline + newline + stack + newline
: ''
)
}

const formatTracing = (data: any) => {
Expand Down
10 changes: 6 additions & 4 deletions packages/graphql-server/src/functions/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
EnvelopError,
FormatErrorHandler,
GraphQLYogaError,
useMaskedErrors,
} from '@graphql-yoga/common'
import type { PluginOrDisabledPlugin } from '@graphql-yoga/common'

Expand Down Expand Up @@ -163,14 +162,17 @@ export const createGraphQLHandler = ({
plugins.push(...extraPlugins)
}

// Must be "last" in plugin chain so can process any data added to results and extensions
// Must be "last" in plugin chain, but before error masking
// so can process any data added to results and extensions
plugins.push(useRedwoodLogger(loggerConfig))

plugins.push(useMaskedErrors({ formatError, errorMessage: defaultError }))
const yoga = createServer({
schema,
plugins,
maskedErrors: false,
maskedErrors: {
formatError,
errorMessage: defaultError,
},
logging: logger,
graphiql: isDevEnv
? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,36 @@ describe('Populates context', () => {
expect(errorLogStatement).toHaveProperty('level')
expect(errorLogStatement).toHaveProperty('time')
expect(errorLogStatement).toHaveProperty('msg')
expect(errorLogStatement).toHaveProperty('error')
expect(errorLogStatement).toHaveProperty('err')

expect(errorLogStatement.name).toEqual('graphql-server')
expect(errorLogStatement.level).toEqual(50)
expect(errorLogStatement.msg).toEqual('You are forbidden')
})

it('Should log an error with type and stack trace info when the resolver raises an exception', async () => {
const loggerConfig = {
logger,
options: {},
} as LoggerConfig

const testkit = createTestkit([useRedwoodLogger(loggerConfig)], testSchema)

await testkit.execute(testErrorQuery, {}, {})

await watchFileCreated(logFile)

const logStatements = parseLogFile(logFile)

const errorLogStatement = logStatements.pop()

expect(errorLogStatement).toHaveProperty('err')
expect(errorLogStatement.err).toHaveProperty('stack')
expect(errorLogStatement.err.type).toEqual('GraphQLError')
expect(errorLogStatement.err.path).toContain('forbiddenUser')
expect(errorLogStatement.err.message).toEqual('You are forbidden')
})

it('Should not log filtered graphql operations', async () => {
const loggerConfig = {
logger,
Expand Down
14 changes: 7 additions & 7 deletions packages/graphql-server/src/plugins/useRedwoodLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,19 @@ const logResult =
error.originalError instanceof ForbiddenError)
) {
envelopLogger.warn(
{
error,
},
error,

`'${error?.extensions?.code || 'authentication'}' error '${
error.message
}' occurred in ${operationName}`
)
} else {
envelopLogger.error(
{
error,
},
error.message || `Error in GraphQL execution: ${operationName}`
error,

error?.originalError?.message ||
error.message ||
`Error in GraphQL execution: ${operationName}`
)
}
})
Expand Down