Skip to main content

Profiling

Inside of @middy/core we've added some hook before and after every middleware called, the handler and from start to end of it's execution.

Time


const defaults = {
logger: console.log,
enabled: true
}

const timePlugin = (opts = {}) => {
const { logger, enabled } = { ...defaults, ...opts }
const store = {}

const start = (id) => {
store[id] = process.hrtime.bigint()
}
const stop = (id) => {
if (!enabled) return
logger(id, Number.parseInt((process.hrtime.bigint() - store[id]).toString()) / 1000000, 'ms')
}

// Only run during cold start
const beforePrefetch = () => start('total')
const requestStart = () => {
if (!store.init) {
store.init = store.total
stop('init')
} else {
start('total')
}
}
const beforeMiddleware = start
const afterMiddleware = stop
const beforeHandler = () => start('handler')
const afterHandler = () => stop('handler')
const requestEnd = () => stop('total')

return {
beforePrefetch,
requestStart,
beforeMiddleware,
afterMiddleware,
beforeHandler,
afterHandler,
requestEnd
}
}

export const handler = middy(timePlugin())
.use(eventLogger())
.use(errorLogger())
.use(httpEventNormalizer())
.use(httpHeaderNormalizer())
.use(httpUrlencodePathParametersParser())
.use(httpUrlencodeBodyParser())
.use(httpJsonBodyParser())
.use(httpCors())
.use(httpSecurityHeaders())
.use(validator({eventSchema}))
.handler(()=>{})

await handler()

This will log out something this:

inputOutputLoggerMiddlewareBefore 0.156033 ms
httpEventNormalizerMiddlewareBefore 0.073921 ms
httpHeaderNormalizerMiddlewareBefore 0.095098 ms
httpUrlencodePathParserMiddlewareBefore 0.036255 ms
httpUrlencodeBodyParserMiddlewareBefore 0.038809 ms
httpJsonBodyParserMiddlewareBefore 0.048383 ms
httpContentNegotiationMiddlewareBefore 0.042311 ms
validatorMiddlewareBefore 0.083366 ms
handler 0.094875 ms
validatorMiddlewareAfter 0.083601 ms
httpSecurityHeadersMiddlewareAfter 0.19702 ms
httpCorsMiddlewareAfter 0.080532 ms
inputOutputLoggerMiddlewareAfter 0.066886 ms
lambda 66.141835 ms

From this everything looks good. Sub 1ms for every middleware and the handler. But wait, that total doesn't look right. You're correct, total includes the initial setup time (or cold start time) for all middlewares. In this case validator is the culprit. The Ajv constructor and compiler do a lot of magic when they first run to get ready for later schema validations. This is why in the validator middleware we now support passing in complied schema and expose the default compiler in case you want to use it in a build step. We hope this feature will help to you in identify slow middlewares and improve your development experience.

There is also a beforeRequest hook, but was left out of the example for dramatic effect.

Additionally, you'll notice that each middleware shows a descriptive name. This is printing out the function name passed into middy core. If you've looked at the code for some the supported middlewares, you'll see these long descriptive variable names being set, then returned. This is why.

Memory

import memwatch from '@airbnb/node-memwatch'

const defaults = {
logger: console.log
}

const memoryPlugin = (opts = {}) => {
const { logger } = { ...defaults, ...opts }
const store = {}

const start = (id) => {
store[id] = new memwatch.HeapDiff()
}
const stop = (id) => {
logger(id, store[id].end())
}

const beforePrefetch = () => start('total')
const requestStart = () => {
store.init = store.total
stop('init')
}
const beforeMiddleware = start
const afterMiddleware = stop
const beforeHandler = () => start('handler')
const afterHandler = () => stop('handler')
const requestEnd = () => stop('total')

return {
beforePrefetch,
requestStart,
beforeMiddleware,
afterMiddleware,
beforeHandler,
afterHandler,
requestEnd
}
}

export const handler = middy(memoryPlugin())
.use(eventLogger())
.use(errorLogger())
.use(httpEventNormalizer())
.use(httpHeaderNormalizer())
.use(httpUrlencodePathParametersParser())
.use(httpUrlencodeBodyParser())
.use(httpJsonBodyParser())
.use(httpCors())
.use(httpSecurityHeaders())
.use(validator({eventSchema}))
.handler(()=>{})

await handler()