dd-trace
Version:
Datadog APM tracing client for JavaScript
199 lines (150 loc) • 4.98 kB
JavaScript
'use strict'
const methods = require('methods').concat('all')
const web = require('../../dd-trace/src/plugins/util/web')
function createWrapFastify (tracer, config) {
config = web.normalizeConfig(config)
return function wrapFastify (fastify) {
if (typeof fastify !== 'function') return fastify
return function fastifyWithTrace () {
const app = fastify.apply(this, arguments)
if (!app) return app
if (typeof app.addHook === 'function') {
app.addHook('onRequest', createOnRequest(tracer, config))
app.addHook('preHandler', preHandler)
app.addHook = createWrapAddHook(tracer, config)(app.addHook)
}
methods.forEach(method => {
app[method] = wrapMethod(app[method])
})
app.route = wrapRoute(app.route)
return app
}
}
}
function createWrapAddHook (tracer, config) {
return function wrapAddHook (addHook) {
return function addHookWithTrace (name, fn) {
fn = arguments[arguments.length - 1]
if (typeof fn !== 'function') return addHook.apply(this, arguments)
arguments[arguments.length - 1] = safeWrap(fn, function (request, reply, done) {
const req = getReq(request)
if (!req) return fn.apply(this, arguments)
done = arguments[arguments.length - 1]
try {
if (typeof done === 'function') {
arguments[arguments.length - 1] = function (err) {
web.addError(req, err)
return done.apply(this, arguments)
}
return fn.apply(this, arguments)
} else {
const promise = fn.apply(this, arguments)
if (promise && typeof promise.catch === 'function') {
return promise.catch(err => {
web.addError(req, err)
throw err
})
}
return promise
}
} catch (e) {
web.addError(req, e)
throw e
}
})
return addHook.apply(this, arguments)
}
}
}
function createOnRequest (tracer, config) {
return function onRequest (request, reply, next) {
if (typeof next !== 'function') return
const req = getReq(request)
const res = getRes(reply)
const name = 'fastify.request'
return web.instrument(tracer, config, req, res, name, () => next())
}
}
function preHandler (request, reply, next) {
if (typeof next !== 'function') return
if (!reply || typeof reply.send !== 'function') return next()
reply.send = wrapSend(reply.send)
next()
}
function wrapSend (send) {
return function sendWithTrace (payload) {
const req = getReq(this && this.request)
web.addError(req, payload)
return send.apply(this, arguments)
}
}
function wrapRoute (route) {
if (typeof route !== 'function') return route
return function routeWithTrace (opts) {
opts.handler = wrapHandler(opts.handler)
return route.apply(this, arguments)
}
}
function wrapMethod (method) {
if (typeof method !== 'function') return method
return function methodWithTrace (url, opts, handler) {
const lastIndex = arguments.length - 1
handler = arguments[lastIndex]
if (typeof handler === 'function') {
arguments[lastIndex] = wrapHandler(handler)
} else if (handler) {
arguments[lastIndex].handler = wrapHandler(handler.handler)
}
return method.apply(this, arguments)
}
}
function wrapHandler (handler) {
if (!handler || typeof handler !== 'function' || handler.name === 'handlerWithTrace') {
return handler
}
return function handlerWithTrace (request, reply) {
const req = getReq(request)
return web.reactivate(req, () => handler.apply(this, arguments))
}
}
// TODO: move this to a common util
function safeWrap (fn, wrapper) {
Object.defineProperty(wrapper, 'length', Object.getOwnPropertyDescriptor(fn, 'length'))
return wrapper
}
function getReq (request) {
return request && (request.raw || request.req || request)
}
function getRes (reply) {
return reply && (reply.raw || reply.res || reply)
}
module.exports = [
{
name: 'fastify',
versions: ['>=3'],
patch (fastify, tracer, config) {
// `fastify` is a function so we return a wrapper that will replace its export.
const wrapped = this.wrapExport(fastify, createWrapFastify(tracer, config)(fastify))
wrapped.fastify = wrapped
wrapped.default = wrapped
return wrapped
},
unpatch (fastify) {
const unwrapped = this.unwrapExport(fastify)
unwrapped.fastify = unwrapped
unwrapped.default = unwrapped
return unwrapped
}
},
{
name: 'fastify',
versions: ['1 - 2'],
patch (fastify, tracer, config) {
// `fastify` is a function so we return a wrapper that will replace its export.
return this.wrapExport(fastify, createWrapFastify(tracer, config)(fastify))
},
unpatch (fastify) {
this.unwrapExport(fastify)
}
}
]