lib/instrumentation/modules/fastify.js (122 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and other contributors where applicable. * Licensed under the BSD 2-Clause License; you may not use this file except in * compliance with the BSD 2-Clause License. */ 'use strict'; // Instrumentation of fastify. // https://www.fastify.io/docs/latest/LTS/ const semver = require('semver'); // Return the fastify route or request method, without hitting a Fastify // deprecation warning. // https://fastify.dev/docs/latest/Reference/Warnings/#FSTDEP018 function fastifyRouteMethod(req) { if (req.routeOptions) { // `.routeOptions` was added in fastify@4.10.0. return req.routeOptions.method; } else { return req.routerMethod || req.raw.method; // Fallback for fastify >3 <3.3.0 } } // Return the fastify route or request URL, without hitting a Fastify // deprecation warning. // https://fastify.dev/docs/latest/Reference/Warnings/#FSTDEP017 function fastifyRouteUrl(req, reply) { if (req.routeOptions) { // `.routeOptions` was added in fastify@4.10.0. // Note that `.routeOptions.url` might be undefined for a 404 route. return req.routeOptions.url; } else { return req.routerPath || reply.context.config.url; // Fallback for fastify >3 <3.3.0 } } module.exports = function ( modExports, agent, { version, enabled, isImportMod }, ) { if (!enabled) { return modExports; } if (isImportMod && !semver.satisfies(version, '>=3.5.0')) { // https://github.com/fastify/fastify/pull/2590 agent.logger.debug( 'ESM instrumentation of fastify requires fastify >=3.5.0, skipping instrumentation', ); return modExports; } agent.setFramework({ name: 'fastify', version, overwrite: false }); agent.logger.debug('wrapping fastify build function'); const origFastify = isImportMod ? modExports.default : modExports; var wrapper; if (semver.gte(version, '3.0.0')) { wrapper = wrappedFastify; } else if (semver.gte(version, '2.0.0-rc')) { wrapper = wrappedFastify2; } else { wrapper = wrappedFastify1; } // Assign all enumerable properties to the wrapper. // - 'fastify' and 'default' are intentionally circular references to // support various import/require styles. See: // https://github.com/fastify/fastify/blob/v4.17.0/fastify.js#L814-L827 for (var prop in origFastify) { switch (prop) { case 'fastify': case 'default': wrapper[prop] = wrapper; break; default: wrapper[prop] = origFastify[prop]; break; } } if (isImportMod) { modExports.fastify = wrapper; modExports.default = wrapper; return modExports; } else { return wrapper; } function wrappedFastify() { const _fastify = origFastify.apply(null, arguments); agent.logger.debug('adding onRequest hook to fastify'); _fastify.addHook('onRequest', (req, reply, next) => { const method = fastifyRouteMethod(req); const url = fastifyRouteUrl(req, reply); const name = method + ' ' + url; agent._instrumentation.setDefaultTransactionName(name); next(); }); agent.logger.debug('adding preHandler hook to fastify'); _fastify.addHook('preHandler', (req, reply, next) => { // Save the parsed req body to be picked up by getContextFromRequest(). req.raw.body = req.body; next(); }); agent.logger.debug('adding onError hook to fastify'); _fastify.addHook('onError', (req, reply, err, next) => { agent.captureError(err, { request: req.raw }); next(); }); return _fastify; } function wrappedFastify2() { const _fastify = origFastify.apply(null, arguments); agent.logger.debug('adding onRequest hook to fastify'); _fastify.addHook('onRequest', (req, reply, next) => { const context = reply.context; const name = req.raw.method + ' ' + context.config.url; agent._instrumentation.setDefaultTransactionName(name); next(); }); agent.logger.debug('adding preHandler hook to fastify'); _fastify.addHook('preHandler', (req, reply, next) => { // Save the parsed req body to be picked up by getContextFromRequest(). req.raw.body = req.body; next(); }); agent.logger.debug('adding onError hook to fastify'); _fastify.addHook('onError', (req, reply, err, next) => { agent.captureError(err, { request: req.raw }); next(); }); return _fastify; } function wrappedFastify1() { const _fastify = origFastify.apply(null, arguments); agent.logger.debug('adding onRequest hook to fastify'); _fastify.addHook('onRequest', (req, reply, next) => { const context = reply._context; const name = req.method + ' ' + context.config.url; agent._instrumentation.setDefaultTransactionName(name); next(); }); agent.logger.debug('adding preHandler hook to fastify'); _fastify.addHook('preHandler', (req, reply, next) => { // Save the parsed req body to be picked up by getContextFromRequest(). req.raw.body = req.body; next(); }); agent.logger.warn( 'Elastic APM cannot automaticaly capture errors on this verison of Fastify. Upgrade to version 2.0.0 or later.', ); return _fastify; } };