diff --git a/README.md b/README.md index e8822ce0..6c4f1034 100644 --- a/README.md +++ b/README.md @@ -649,6 +649,7 @@ Epsagon provides out-of-the-box instrumentation (tracing) for many popular frame |https |Fully supported | |http2 |Fully supported | |dns |Fully supported | +|fs |Fully supported | |aws-sdk |`>=2.2.0` | |amazon-dax-client |`>=1.0.2` | |@google-cloud |`>=2.0.0` | @@ -700,6 +701,7 @@ Advanced options can be configured as a parameter to the init() method or as env |- |EPSAGON_ADD_NODE_PATH |String |- |List of folders to looks for node_modules when patching libraries. Separated by `:`| |- |EPSAGON_AUTO_ADD_NODE_PATHS|Boolean |`false` |Auto add node_modules sub folders to look when patching libraries. | |- |EPSAGON_DNS_INSTRUMENTATION|Boolean|`false` |Whether to capture `dns` calls into the trace | +|- |EPSAGON_FS_INSTRUMENTATION |Boolean|`false` |Whether to capture node `file system` calls into the trace | |- |EPSAGON_LOGGING_TRACING_ENABLED|Boolean|`true` |whether to add an Epsagon ID to the logs in order to correlate traces to logs in the dashboard| |- |EPSAGON_STEPS_ID |String|- |The Epsagon step id from the ECS step functions state input | |- |EPSAGON_STEPS_NUM |String|`0` |The step number of the ECS step functions state | diff --git a/src/events/fs.js b/src/events/fs.js new file mode 100644 index 00000000..13626f68 --- /dev/null +++ b/src/events/fs.js @@ -0,0 +1,88 @@ +const fs = require('fs'); +const shimmer = require('shimmer'); +const tracer = require('../tracer.js'); +const eventInterface = require('../event.js'); + +/** + * Calling to the fs writeFileSync function without callback, And record the error if thrown. + * @param {Function} original The node fs function. + * @param {number} startTime Event start time. + * @param {serverlessEvent.Event} fsEvent FS event. + * @param {Array} args Array of function arguments. + * @returns {Object} original function response. + */ +function handleFunctionWithoutCallback(original, startTime, fsEvent, args) { + try { + tracer.addEvent(fsEvent); + return original.apply(this, args); + } catch (err) { + eventInterface.finalizeEvent(fsEvent, startTime, err); + throw err; + } +} + +/** + * Wrap node fs requset. + * @param {Function} original The node fs function. + * @param {Function} originalName The node fs function name. + * @returns {Function} The wrapped function + */ +function wrapFsWriteFileFunction(original, originalName) { + return function internalWrapFsWriteFileFunction(file, data, options, callback) { + const fileName = typeof file === 'object' ? file.toString() : file; + const fsCallback = typeof (callback || options) === 'function' && (callback || options); + const { slsEvent: fsEvent, startTime } = eventInterface.initializeEvent('file_system', fileName, originalName, 'file_system'); + + eventInterface.addToMetadata(fsEvent, { 'fs.file': fileName }); + if (!!options && typeof options === 'object') { + eventInterface.addToMetadata(fsEvent, { options }); + } + if (!fsCallback) { + return handleFunctionWithoutCallback(original, startTime, fsEvent, [ + fileName, + data, + options, + ]); + } + let patchedCallback; + let clientRequest; + let clientRequestHasBeenCalled; + try { + const responsePromise = new Promise((resolve) => { + patchedCallback = (err) => { + eventInterface.finalizeEvent(fsEvent, startTime, err); + resolve(); + fsCallback(err); + }; + }); + if (typeof callback === 'function') { + clientRequestHasBeenCalled = true; + clientRequest = original.apply(this, [file, data, options, patchedCallback]); + } else if (typeof options === 'function') { + clientRequestHasBeenCalled = true; + clientRequest = original.apply(this, [file, data, patchedCallback]); + } + tracer.addEvent(fsEvent, responsePromise); + } catch (err) { + tracer.addException(err); + } + + return clientRequest || clientRequestHasBeenCalled ? + clientRequest : + original.apply(this, [file, data, options, callback]); + }; +} + + +module.exports = { + /** + * Patch Node fs methods. + * process.env.EPSAGON_FS_INSTRUMENTATION=true is requird. + */ + init() { + if ((process.env.EPSAGON_FS_INSTRUMENTATION || '').toUpperCase() === 'TRUE') { + shimmer.wrap(fs, 'writeFile', () => wrapFsWriteFileFunction(fs.writeFile, 'writeFile')); + shimmer.wrap(fs, 'writeFileSync', () => wrapFsWriteFileFunction(fs.writeFileSync, 'writeFileSync')); + } + }, +};