Skip to content

Commit

Permalink
feat(logging): selective error message logging by error type
Browse files Browse the repository at this point in the history
Previously, all errors were logged as generic errors. This enhancement introduces selective logging of warnings
and errors based on their error type. Additionally, message interpolation has been introduced to resolve error message
placeholders into human-readable text.
  • Loading branch information
arleytm committed Nov 8, 2023
1 parent 959beb6 commit 524520d
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 4 deletions.
40 changes: 38 additions & 2 deletions lib/resolvers/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const cds = require('@sap/cds')
const LOG_CDS = cds.log()
const LOG_GRAPHQL = cds.log('graphql')
const { normalizeError } = require('@sap/cds/libx/_runtime/common/error/frontend')
const { getErrorMessage } = require('@sap/cds/libx/_runtime/common/error/utils')
const { GraphQLError } = require('graphql')
const { IS_PRODUCTION } = require('../utils')

Expand Down Expand Up @@ -31,14 +32,49 @@ const _cdsToGraphQLError = (context, err) => {
}

const _ensureError = error => (error instanceof Error ? error : new Error(error))
const _clone = obj => Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))

const handleCDSError = (context, error) => {
error = _ensureError(error)
// TODO: choose log level depending on type of error analogous to OData adapter
if (LOG_CDS._error) LOG_CDS.error(error)
_log(context.req, error)
return _cdsToGraphQLError(context, error)
}

const _log = (req, error) => {
// do not log standard errors, e.g. TypeError
if (error.code === undefined && error.details === undefined) return

// log errors and warnings only
if (LOG_CDS.level <= cds.log.levels.WARN) return

// Clone of the original error object to prevent mutation and unintended side-effects.
// Notice that the cloned error is logged to standard output in its default language,
// whether the original error message is locale-dependent as it is usually sent in the
// HTTP response to HTTP Clients to be displayed in the user interface.
const error2log = _clone(error)

// interpolate message placeholder to default language
error2log.message = getErrorMessage(error)
if (error.details) {
error2log.details = error.details.map(error => {
const clone = _clone(error)
clone.message = getErrorMessage(error)
return clone
})
}

const reqClone = _clone(req)
const statusCode = normalizeError(error2log, reqClone).statusCode

if (statusCode >= 400 && statusCode < 500) {
// client error
if (LOG_CDS._warn) LOG_CDS.warn(error2log)
} else {
// server error
if (LOG_CDS._error) LOG_CDS.error(error2log)
}
}

const formatError = error => {
// CDS errors have already been logged and already have a stacktrace in extensions
if (error.originalError?._cdsError) return error
Expand Down
4 changes: 2 additions & 2 deletions lib/resolvers/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ const { handleCDSError } = require('./error')
const setResponse = async (context, response, key, value) => {
try {
response[key] = await value
} catch (e) {
response[key] = handleCDSError(context, e)
} catch (error) {
response[key] = handleCDSError(context, error)
}
}

Expand Down

0 comments on commit 524520d

Please sign in to comment.