diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index b7437f8aeaf..1840db06311 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -299,24 +299,6 @@ jobs: - run: yarn test:plugins:ci - uses: codecov/codecov-action@v2 - fs: - runs-on: ubuntu-latest - env: - PLUGINS: fs - steps: - - uses: actions/checkout@v2 - - uses: ./.github/actions/node/setup - - run: yarn install - - uses: ./.github/actions/node/12 - - run: yarn test:plugins:ci - - uses: ./.github/actions/node/14 - - run: yarn test:plugins:ci - - uses: ./.github/actions/node/16 - - run: yarn test:plugins:ci - - uses: ./.github/actions/node/latest - - run: yarn test:plugins:ci - - uses: codecov/codecov-action@v2 - generic-pool: runs-on: ubuntu-latest env: diff --git a/docs/API.md b/docs/API.md index 34da7a5ccbe..ca2ec393801 100644 --- a/docs/API.md +++ b/docs/API.md @@ -40,7 +40,6 @@ tracer.use('pg', {
- @@ -108,7 +107,6 @@ tracer.use('pg', { * [elasticsearch](./interfaces/plugins.elasticsearch.html) * [express](./interfaces/plugins.express.html) * [fastify](./interfaces/plugins.fastify.html) -* [fs](./interfaces/plugins.fs.html) * [generic-pool](./interfaces/plugins.generic_pool.html) * [google-cloud-pubsub](./interfaces/plugins.google_cloud_pubsub.html) * [graphql](./interfaces/plugins.graphql.html) diff --git a/docs/test.ts b/docs/test.ts index fca1f29da39..caadac9b479 100644 --- a/docs/test.ts +++ b/docs/test.ts @@ -201,7 +201,6 @@ tracer.use('express'); tracer.use('express', httpServerOptions); tracer.use('fastify'); tracer.use('fastify', httpServerOptions); -tracer.use('fs'); tracer.use('generic-pool'); tracer.use('google-cloud-pubsub'); tracer.use('graphql', graphqlOptions); diff --git a/index.d.ts b/index.d.ts index b6a137b3123..c082e1a3fa4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -562,7 +562,6 @@ interface Plugins { "elasticsearch": plugins.elasticsearch; "express": plugins.express; "fastify": plugins.fastify; - "fs": plugins.fs; "generic-pool": plugins.generic_pool; "google-cloud-pubsub": plugins.google_cloud_pubsub; "graphql": plugins.graphql; @@ -903,12 +902,6 @@ declare namespace plugins { */ interface fastify extends HttpServer {} - /** - * This plugin automatically instruments the - * [fs](https://nodejs.org/api/fs.html) module. - */ - interface fs extends Instrumentation {} - /** * This plugin patches the [generic-pool](https://github.com/coopernurse/node-pool) * module to bind the callbacks the the caller context. diff --git a/packages/datadog-plugin-fs/src/index.js b/packages/datadog-plugin-fs/src/index.js deleted file mode 100644 index a90d6bfc73a..00000000000 --- a/packages/datadog-plugin-fs/src/index.js +++ /dev/null @@ -1,548 +0,0 @@ -'use strict' - -const { storage } = require('../../datadog-core') - -let kDirReadPromisified -let kDirClosePromisified -let kHandle - -const ddFhSym = Symbol('ddFileHandle') - -const tagMakers = { - open: createOpenTags, - close: createCloseTags, - readFile: createReadFileTags, - writeFile: createWriteFileTags, - appendFile: createAppendFileTags, - access: createPathTags, - copyFile: createCopyFileTags, - stat: createPathTags, - lstat: createPathTags, - fstat: createFDTags, - readdir: createPathTags, - opendir: createPathTags, - read: createFDTags, - write: createFDTags, - writev: createFDTags, - chmod: createChmodTags, - lchmod: createChmodTags, - fchmod: createFchmodTags, - chown: createChownTags, - lchown: createChownTags, - fchown: createFchownTags, - realpath: createPathTags, - readlink: createPathTags, - unlink: createPathTags, - symlink: createCopyFileTags, - link: createCopyFileTags, - rmdir: createPathTags, - rename: createCopyFileTags, - fsync: createFDTags, - fdatasync: createFDTags, - mkdir: createPathTags, - truncate: createPathTags, - ftruncate: createFDTags, - utimes: createPathTags, - futimes: createFDTags, - mkdtemp: createPathTags -} - -const promisifiable = ['read', 'readv', 'write', 'writev'] - -const orphanable = false - -function createWrapCreateReadStream (config, tracer) { - return function wrapCreateReadStream (createReadStream) { - return function createReadStreamWithTrace (path, options) { - if (!hasParent()) { - return createReadStream.apply(this, arguments) - } - const tags = makeFSFlagTags('ReadStream', path, options, 'r', config, tracer) - return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => { - const stream = createReadStream.apply(this, arguments) - stream.once('close', done) - stream.once('end', done) - stream.once('error', done) - return stream - }) - } - } -} - -function createWrapCreateWriteStream (config, tracer) { - return function wrapCreateWriteStream (createWriteStream) { - return function createWriteStreamWithTrace (path, options) { - const tags = makeFSFlagTags('WriteStream', path, options, 'w', config, tracer) - return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => { - const stream = createWriteStream.apply(this, arguments) - stream.once('close', done) - stream.once('finish', done) - stream.once('error', done) - return stream - }) - } - } -} - -function createWrapExists (config, tracer) { - return function wrapExists (exists) { - const existsWithTrace = function existsWithTrace (path, cb) { - if (typeof cb !== 'function') { - return exists.apply(this, arguments) - } - const tags = makeFSTags('exists', path, null, config, tracer) - return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => { - arguments[1] = function (result) { - done() - cb.apply(this, arguments) - } - return exists.apply(this, arguments) - }) - } - - copySymbols(exists, existsWithTrace) - - return existsWithTrace - } -} - -function createWrapDirRead (config, tracer, sync) { - const name = sync ? 'dir.readSync' : 'dir.read' - return function wrapDirRead (read) { - function options () { - const tags = makeFSTags(name, this.path, null, config, tracer) - return { tags, orphanable } - } - return tracer.wrap('fs.operation', options, read, true) - } -} - -function createWrapDirClose (config, tracer, sync) { - const name = sync ? 'dir.closeSync' : 'dir.close' - return function wrapDirClose (close) { - function options () { - const tags = makeFSTags(name, this.path, null, config, tracer) - return { tags, orphanable } - } - return tracer.wrap('fs.operation', options, close, true) - } -} - -function createWrapDirAsyncIterator (config, tracer, instrumenter) { - return function wrapDirAsyncIterator (asyncIterator) { - return function asyncIteratorWithTrace () { - if (!kDirReadPromisified) { - const keys = Reflect.ownKeys(this) - for (const key of keys) { - if (kDirReadPromisified && kDirClosePromisified) break - if (typeof key !== 'symbol') continue - if (!kDirReadPromisified && getSymbolName(key).includes('kDirReadPromisified')) { - kDirReadPromisified = key - } - if (!kDirClosePromisified && getSymbolName(key).includes('kDirClosePromisified')) { - kDirClosePromisified = key - } - } - } - instrumenter.wrap(this, kDirReadPromisified, createWrapDirRead(config, tracer)) - instrumenter.wrap(this, kDirClosePromisified, createWrapKDirClose(config, tracer, instrumenter)) - return asyncIterator.apply(this, arguments) - } - } -} - -function createWrapKDirClose (config, tracer, instrumenter) { - return function wrapKDirClose (kDirClose) { - return function kDirCloseWithTrace () { - const tags = makeFSTags('dir.close', this.path, null, config, tracer) - return tracer.trace('fs.operation', { tags, orphanable }, (span) => { - const p = kDirClose.apply(this, arguments) - const unwrapBoth = () => { - instrumenter.unwrap(this, kDirReadPromisified) - instrumenter.unwrap(this, kDirClosePromisified) - } - p.then(unwrapBoth, unwrapBoth) - return p - }) - } - } -} - -function createOpenTags (resourceName, config, tracer) { - return function openTags (path, flag, mode) { - if (!flag || typeof flag === 'function') { - flag = null - } - return makeFSFlagTags(resourceName, path, { flag }, 'r', config, tracer) - } -} - -function createCloseTags (resourceName, config, tracer) { - return function closeTags (fd) { - if (typeof fd === 'undefined' && this && this[ddFhSym]) { - fd = this[ddFhSym].fd - } - if (typeof fd !== 'number' || !Number.isInteger(fd)) { - return - } - return makeFSTags(resourceName, fd, null, config, tracer) - } -} - -function createReadFileTags (resourceName, config, tracer) { - return function readFileTags (path, options) { - return makeFSFlagTags(resourceName, path, options, 'r', config, tracer) - } -} - -function createWriteFileTags (resourceName, config, tracer) { - return function writeFileTags (path, data, options) { - return makeFSFlagTags(resourceName, path, options, 'w', config, tracer) - } -} - -function createAppendFileTags (resourceName, config, tracer) { - return function appendFileTags (path, data, options) { - return makeFSFlagTags(resourceName, path, options, 'a', config, tracer) - } -} - -function createCopyFileTags (resourceName, config, tracer) { - return function copyFileTags (src, dest, flag) { - return makeFSTags(resourceName, { src, dest }, null, config, tracer) - } -} - -function createChmodTags (resourceName, config, tracer) { - return function chmodTags (fd, mode) { - const tags = makeFSTags(resourceName, fd, null, config, tracer) - tags['file.mode'] = mode.toString(8) - return tags - } -} - -function createFchmodTags (resourceName, config, tracer) { - return function fchmodTags (fd, mode) { - if (typeof this === 'object' && this !== null && this.fd) { - mode = fd - fd = this.fd - } - - const tags = makeFSTags(resourceName, fd, null, config, tracer) - if (mode) { - tags['file.mode'] = mode.toString(8) - } - return tags - } -} - -function createPathTags (resourceName, config, tracer) { - return function pathTags (path) { - return makeFSTags(resourceName, path, null, config, tracer) - } -} - -function createFDTags (resourceName, config, tracer) { - return function fdTags (fd) { - if (typeof this === 'object' && this !== null && this.fd) { - fd = this.fd - } - return makeFSTags(resourceName, fd, null, config, tracer) - } -} - -function createChownTags (resourceName, config, tracer) { - return function chownTags (fd, uid, gid) { - const tags = makeFSTags(resourceName, fd, null, config, tracer) - if (typeof uid === 'number') { - tags['file.uid'] = uid.toString() - } - if (typeof gid === 'number') { - tags['file.gid'] = gid.toString() - } - return tags - } -} - -function createFchownTags (resourceName, config, tracer) { - return function fchownTags (fd, uid, gid) { - if (typeof this === 'object' && this !== null && this.fd) { - gid = uid - uid = fd - fd = this.fd - } - const tags = makeFSTags(resourceName, fd, null, config, tracer) - if (typeof uid === 'number') { - tags['file.uid'] = uid.toString() - } - if (typeof gid === 'number') { - tags['file.gid'] = gid.toString() - } - return tags - } -} - -function getSymbolName (sym) { - return sym.description || sym.toString() -} - -function hasParent () { - const store = storage.getStore() - - return store && store.span && !store.noop -} - -function createWrapCb (tracer, config, name, tagMaker) { - const makeTags = tagMaker(name, config, tracer) - return function wrapFunction (fn) { - return tracer.wrap('fs.operation', function () { - if (typeof arguments[arguments.length - 1] !== 'function') { - return - } - const tags = makeTags.apply(this, arguments) - return tags ? { tags, orphanable } : { orphanable } - }, fn, true) - } -} - -function createWrap (tracer, config, name, tagMaker) { - const makeTags = tagMaker(name, config, tracer) - - return function wrapSyncFunction (fn) { - return tracer.wrap('fs.operation', function () { - const tags = makeTags.apply(this, arguments) - return tags ? { tags, orphanable } : { orphanable } - }, fn, true) - } -} - -function makeFSFlagTags (resourceName, path, options, defaultFlag, config, tracer) { - const tags = makeFSTags(resourceName, path, options, config, tracer) - - if (tags) { - let flag = defaultFlag - if (typeof options === 'object' && options !== null) { - if (options.flag) { - flag = options.flag - } else if (options.flags) { - flag = options.flags - } - } - tags['file.flag'] = flag - return tags - } -} - -function makeFSTags (resourceName, path, options, config, tracer) { - path = options && typeof options === 'object' && 'fd' in options ? options.fd : path - const tags = { - 'component': 'fs', - 'span.kind': 'internal', - 'resource.name': resourceName, - 'service.name': config.service || tracer._service - } - - switch (typeof path) { - case 'object': { - if (path === null) return tags - const src = 'src' in path ? path.src : null - const dest = 'dest' in path ? path.dest : null - if (src || dest) { - tags['file.src'] = src - tags['file.dest'] = dest - } else { - tags['file.path'] = path - } - break - } - case 'string': { - tags['file.path'] = path - break - } - case 'number': { - tags['file.descriptor'] = path.toString() - break - } - } - - return tags -} - -function copySymbols (from, to) { - const props = Object.getOwnPropertyDescriptors(from) - const keys = Reflect.ownKeys(props) - - for (const key of keys) { - if (typeof key !== 'symbol' || to.hasOwnProperty(key)) continue - - Object.defineProperty(to, key, props[key]) - } -} - -function getFileHandlePrototype (fs) { - return fs.promises.open(__filename, 'r') - .then(fh => { - if (!kHandle) { - kHandle = Reflect.ownKeys(fh).find(key => typeof key === 'symbol' && key.toString().includes('kHandle')) - } - fh.close() - - return Object.getPrototypeOf(fh) - }) -} - -function patchClassicFunctions (fs, tracer, config) { - for (const name in fs) { - if (!fs[name]) continue - const tagMakerName = name.endsWith('Sync') ? name.substr(0, name.length - 4) : name - const original = fs[name] - if (tagMakerName in tagMakers) { - const tagMaker = tagMakers[tagMakerName] - if (name.endsWith('Sync')) { - this.wrap(fs, name, createWrap(tracer, config, name, tagMaker)) - } else { - this.wrap(fs, name, createWrapCb(tracer, config, name, tagMaker)) - } - if (name in promisifiable) { - copySymbols(original, fs[name]) - } - } - } -} - -function patchFileHandle (fs, tracer, config) { - getFileHandlePrototype(fs).then((fileHandlePrototype) => { - for (const name of Reflect.ownKeys(fileHandlePrototype)) { - if (typeof name !== 'string' || name === 'constructor' || name === 'fd' || name === 'getAsyncId') { - continue - } - let tagMaker - const fName = 'f' + name - if (fName in tagMakers) { - tagMaker = tagMakers[fName] - } else { - tagMaker = createFDTags - } - - const instrumenter = this - - const desc = Reflect.getOwnPropertyDescriptor(fileHandlePrototype, kHandle) - if (!desc || !desc.get) { - Reflect.defineProperty(fileHandlePrototype, kHandle, { - get () { - return this[ddFhSym] - }, - set (h) { - this[ddFhSym] = h - instrumenter.wrap(this, 'close', createWrap(tracer, config, 'filehandle.close', tagMakers.close)) - }, - configurable: true - }) - } - - this.wrap(fileHandlePrototype, name, createWrap(tracer, config, 'filehandle.' + name, tagMaker)) - } - }) -} - -function patchPromiseFunctions (fs, tracer, config) { - for (const name in fs.promises) { - if (name in tagMakers) { - const tagMaker = tagMakers[name] - this.wrap(fs.promises, name, createWrap(tracer, config, 'promises.' + name, tagMaker)) - } - } -} - -function patchDirFunctions (fs, tracer, config) { - this.wrap(fs.Dir.prototype, 'close', createWrapDirClose(config, tracer)) - this.wrap(fs.Dir.prototype, 'closeSync', createWrapDirClose(config, tracer, true)) - this.wrap(fs.Dir.prototype, 'read', createWrapDirRead(config, tracer)) - this.wrap(fs.Dir.prototype, 'readSync', createWrapDirRead(config, tracer, true)) - this.wrap(fs.Dir.prototype, Symbol.asyncIterator, createWrapDirAsyncIterator(config, tracer, this)) -} - -function unpatchClassicFunctions (fs) { - for (const name in fs) { - if (!fs[name]) continue - const tagMakerName = name.endsWith('Sync') ? name.substr(0, name.length - 4) : name - if (tagMakerName in tagMakers) { - this.unwrap(fs, name) - } - } -} - -function unpatchFileHandle (fs) { - getFileHandlePrototype(fs).then(fileHandlePrototype => { - for (const name of Reflect.ownKeys(fileHandlePrototype)) { - if (typeof name !== 'string' || name === 'constructor' || name === 'fd' || name === 'getAsyncId') { - continue - } - this.unwrap(fileHandlePrototype, name) - } - delete fileHandlePrototype[kHandle] - }) -} - -function unpatchPromiseFunctions (fs) { - for (const name in fs.promises) { - if (name in tagMakers) { - this.unwrap(fs.promises, name) - } - } -} - -function unpatchDirFunctions (fs) { - this.unwrap(fs.Dir.prototype, 'close') - this.unwrap(fs.Dir.prototype, 'closeSync') - this.unwrap(fs.Dir.prototype, 'read') - this.unwrap(fs.Dir.prototype, 'readSync') - this.unwrap(fs.Dir.prototype, Symbol.asyncIterator) -} - -module.exports = { - name: 'fs', - patch (fs, tracer, config) { - const realpathNative = fs.realpath.native - const realpathSyncNative = fs.realpathSync.native - patchClassicFunctions.call(this, fs, tracer, config) - if (fs.promises) { - patchFileHandle.call(this, fs, tracer, config) - patchPromiseFunctions.call(this, fs, tracer, config) - } - if (fs.Dir) { - patchDirFunctions.call(this, fs, tracer, config) - } - this.wrap(fs, 'createReadStream', createWrapCreateReadStream(config, tracer)) - this.wrap(fs, 'createWriteStream', createWrapCreateWriteStream(config, tracer)) - this.wrap(fs, 'existsSync', createWrap(tracer, config, 'existsSync', createPathTags)) - this.wrap(fs, 'exists', createWrapExists(config, tracer)) - if (realpathNative) { - fs.realpath.native = createWrapCb(tracer, config, 'realpath.native', createPathTags)(realpathNative) - } - if (realpathSyncNative) { - fs.realpathSync.native = createWrap(tracer, config, 'realpath.native', createPathTags)(realpathSyncNative) - } - }, - unpatch (fs) { - unpatchClassicFunctions.call(this, fs) - if (fs.promises) { - unpatchFileHandle.call(this, fs) - unpatchPromiseFunctions.call(this, fs) - } - if (fs.Dir) { - unpatchDirFunctions.call(this, fs) - } - this.unwrap(fs, 'createReadStream') - this.unwrap(fs, 'createWriteStream') - this.unwrap(fs, 'existsSync') - this.unwrap(fs, 'exists') - } -} - -/** TODO fs functions: - -unwatchFile -watch -watchFile -*/ diff --git a/packages/datadog-plugin-fs/test/index.spec.js b/packages/datadog-plugin-fs/test/index.spec.js deleted file mode 100644 index 4f2280a9003..00000000000 --- a/packages/datadog-plugin-fs/test/index.spec.js +++ /dev/null @@ -1,1834 +0,0 @@ -'use strict' - -const agent = require('../../dd-trace/test/plugins/agent') -const { expectSomeSpan } = require('../../dd-trace/test/plugins/helpers') - -const realFS = Object.assign({}, require('fs')) -const os = require('os') -const path = require('path') -const semver = require('semver') -const rimraf = require('rimraf') -const util = require('util') - -const hasWritev = semver.satisfies(process.versions.node, '>=12.9.0') -const hasOSymlink = realFS.constants.O_SYMLINK - -// TODO remove skips - -describe('Plugin', () => { - describe('fs', () => { - let fs - let tmpdir - let tracer - afterEach(() => agent.close()) - beforeEach(() => agent.load('fs').then(() => { - tracer = require('../../dd-trace') - fs = require('fs') - })) - before(() => { - tmpdir = realFS.mkdtempSync(path.join(os.tmpdir(), 'dd-trace-js-test')) - }) - after((done) => { - rimraf(tmpdir, realFS, done) - }) - - describe('without parent span', () => { - describe('open', () => { - it('should not be instrumented', (done) => { - agent.use(() => { - expect.fail('should not have been any traces') - }).catch(done) - - setTimeout(done, 1500) // allow enough time to ensure no traces happened - - fs.open(__filename, 'r+', (err, fd) => { - if (err) { - done(err) - } else { - realFS.closeSync(fd) - } - }) - }) - }) - }) - - describe('with parent span', () => { - beforeEach((done) => { - const parentSpan = tracer.startSpan('parent') - parentSpan.finish() - tracer.scope().activate(parentSpan, done) - }) - - describe('open', () => { - let fd - afterEach(() => { - if (typeof fd === 'number') { - realFS.closeSync(fd) - fd = undefined - } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'open', - meta: { - 'file.flag': 'r', - 'file.path': __filename - } - }) - - fs.open(__filename, (err, _fd) => { - fd = _fd - if (err) done(err) - }) - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource: 'open', - meta: { - 'file.flag': 'r+', - 'file.path': __filename - } - }) - - fs.open(__filename, 'r+', (err, _fd) => { - fd = _fd - if (err) done(err) - }) - }) - - it('should handle errors', (done) => { - const filename = path.join(__filename, Math.random().toString()) - fs.open(filename, 'r', (err) => { - expectOneSpan(agent, done, { - resource: 'open', - error: 0, - meta: { - 'file.flag': 'r', - 'file.path': filename - } - }) - }) - }) - }) - - if (realFS.promises) { - describe('promises.open', () => { - let fd - afterEach(() => { - if (typeof fd === 'number') { - realFS.closeSync(fd) - fd = undefined - } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'promises.open', - meta: { - 'file.flag': 'r', - 'file.path': __filename - } - }) - - fs.promises.open(__filename).then(_fd => { - fd = _fd - }, done) - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource: 'promises.open', - meta: { - 'file.flag': 'r+', - 'file.path': __filename - } - }) - - fs.promises.open(__filename, 'r+').then(_fd => { - fd = _fd - }, done) - }) - - it('should handle errors', (done) => { - const filename = path.join(__filename, Math.random().toString()) - fs.promises.open(filename, 'r').catch((err) => { - expectOneSpan(agent, done, { - resource: 'promises.open', - error: 0, - meta: { - 'file.flag': 'r', - 'file.path': filename - } - }) - }) - }) - }) - } - - describe('openSync', () => { - let fd - afterEach(() => { - if (typeof fd === 'number') { - realFS.closeSync(fd) - fd = undefined - } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'openSync', - meta: { - 'file.flag': 'r', - 'file.path': __filename - } - }) - - fd = fs.openSync(__filename) - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource: 'openSync', - meta: { - 'file.flag': 'r+', - 'file.path': __filename - } - }) - - fd = fs.openSync(__filename, 'r+') - }) - - it('should handle errors', (done) => { - const filename = path.join(__filename, Math.random().toString()) - try { - fs.openSync(filename, 'r') - } catch (err) { - expectOneSpan(agent, done, { - resource: 'openSync', - error: 0, - meta: { - 'file.flag': 'r', - 'file.path': filename - } - }) - } - }) - }) - - describeThreeWays('close', (resource, tested) => { - it('should be instrumented', (done) => { - const fd = realFS.openSync(__filename, 'r') - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - - tested(fs, [fd], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309], agent)) - }) - - describeThreeWays('readFile', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.flag': 'r', - 'file.path': __filename - } - }) - - tested(fs, [__filename], done) - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.flag': 'r+', - 'file.path': __filename - } - }) - - tested(fs, [__filename, { flag: 'r+' }], done) - }) - - it('should not fail if options is a string', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.flag': 'r', - 'file.path': __filename - } - }) - - tested(fs, [__filename, 'utf8'], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', { flag: 'r' }], agent)) - }) - - describeThreeWays('writeFile', (resource, tested) => { - let filename - beforeEach(() => { - filename = path.join(tmpdir, 'writeFile') - }) - afterEach(() => { - try { - realFS.unlinkSync(filename) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.flag': 'w', - 'file.path': filename - } - }) - - tested(fs, [filename, 'test'], done) - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.flag': 'w+', - 'file.path': filename - } - }) - - tested(fs, [filename, 'test', { flag: 'w+' }], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [filename, 'test', { flag: 'r' }], agent)) - }) - - describeThreeWays('appendFile', (resource, tested) => { - let filename - beforeEach(() => { - filename = path.join(tmpdir, 'appendFile') - }) - afterEach(() => { - try { - realFS.unlinkSync(filename) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.flag': 'a', - 'file.path': filename - } - }) - - tested(fs, [filename, 'test'], done) - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.flag': 'a+', - 'file.path': filename - } - }) - - tested(fs, [filename, 'test', { flag: 'a+' }], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [filename, 'test', { flag: 'r' }], agent)) - }) - - describeThreeWays('access', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename - } - }) - - tested(fs, [__filename], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - - describeThreeWays('copyFile', (resource, tested) => { - const dest = `${__filename}copy` - afterEach(() => { - try { - realFS.unlinkSync(dest) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.src': __filename, - 'file.dest': dest - } - }) - - tested(fs, [__filename, dest], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [__filename, __filename, fs.constants.COPYFILE_EXCL], agent)) - }) - - describeThreeWays('stat', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename - } - }) - - tested(fs, [__filename], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - - describeThreeWays('lstat', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename - } - }) - - tested(fs, [__filename], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - - describeThreeWays('fstat', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': '1' - } - }) - - tested(fs, [1], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309], agent)) - }) - - describeThreeWays('readdir', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __dirname - } - }) - - tested(fs, [__dirname], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/baddirname'], agent)) - }) - - describeThreeWays('opendir', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __dirname - } - }) - - tested(fs, [__dirname], (err, dir) => { - if (err) done(err) - else dir.close(done) - }) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/baddirname'], agent)) - }) - - describeThreeWays('read', (resource, tested) => { - let fd - beforeEach(() => { - fd = realFS.openSync(__filename, 'r') - }) - afterEach(() => { - realFS.closeSync(fd) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - tested(fs, [fd, Buffer.alloc(5), 0, 5, 0], done) - }) - - if (resource === 'read') { - it('should support promisification', () => { - const read = util.promisify(fs.read) - - return read(fd, Buffer.alloc(5), 0, 5, 0) - }) - } - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309, Buffer.alloc(5), 0, 5, 0], agent)) - }) - - describeThreeWays('write', (resource, tested) => { - let fd - let filename - beforeEach(() => { - filename = path.join(tmpdir, 'write') - fd = realFS.openSync(filename, 'w') - }) - afterEach(() => { - realFS.closeSync(fd) - realFS.unlinkSync(filename) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - tested(fs, [fd, Buffer.from('hello'), 0, 5, 0], done) - }) - - if (resource === 'write') { - it('should support promisification', () => { - const write = util.promisify(fs.write) - - return write(fd, Buffer.from('hello'), 0, 5, 0) - }) - } - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309, Buffer.alloc(5), 0, 5, 0], agent)) - }) - - if (hasWritev) { - describeThreeWays('writev', (resource, tested) => { - let fd - let filename - beforeEach(() => { - filename = path.join(tmpdir, 'writev') - fd = realFS.openSync(filename, 'w') - }) - afterEach(() => { - realFS.closeSync(fd) - realFS.unlinkSync(filename) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - tested(fs, [fd, [Buffer.from('hello')], 0], done) - }) - - if (resource === 'writev') { - it('should support promisification', () => { - const writev = util.promisify(fs.writev) - - return writev(fd, [Buffer.from('hello')], 0) - }) - } - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309, [Buffer.alloc(5)], 0], agent)) - }) - } - - describe('createReadStream', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'ReadStream', - meta: { - 'file.path': __filename, - 'file.flag': 'r' - } - }) - fs.createReadStream(__filename).on('error', done).resume() - }) - - it('should be instrumented when closed', (done) => { - expectOneSpan(agent, done, { - resource: 'ReadStream', - meta: { - 'file.path': __filename, - 'file.flag': 'r+' - } - }) - fs.createReadStream(__filename, { flags: 'r+' }).on('error', done).destroy() - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource: 'ReadStream', - meta: { - 'file.path': __filename, - 'file.flag': 'r+' - } - }) - fs.createReadStream(__filename, { flags: 'r+' }).on('error', done).resume() - }) - - it('should handle errors', () => { - testHandleErrors(fs, 'ReadStream', (fs, args, _, cb) => { - fs.createReadStream(...args).on('error', cb).emit('error', new Error('bad')) - }, [__filename], agent) - }) - }) - - describe('createWriteStream', () => { - let filename - beforeEach(() => { - filename = path.join(tmpdir, 'createWriteStream') - }) - afterEach(done => { - // swallow errors since we're causing a race condition in one of the tests - realFS.unlink(filename, () => done()) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'WriteStream', - meta: { - 'file.path': filename, - 'file.flag': 'w' - } - }) - - fs.createWriteStream(filename).on('error', done).end() - }) - - it('should be instrumented when closed', (done) => { - expectOneSpan(agent, done, { - resource: 'WriteStream', - meta: { - 'file.path': filename, - 'file.flag': 'w' - } - }) - - fs.createWriteStream(filename).on('error', done).destroy() - }) - - it('should be instrumented with flags', (done) => { - expectOneSpan(agent, done, { - resource: 'WriteStream', - meta: { - 'file.path': filename, - 'file.flag': 'w+' - } - }) - - fs.createWriteStream(filename, { flags: 'w+' }).on('error', done).end() - }) - - it('should handle errors', () => { - testHandleErrors(fs, 'WriteStream', (fs, args, _, cb) => { - fs.createWriteStream(...args).on('error', cb).emit('error', new Error('bad')) - }, [filename], agent) - }) - }) - - describeThreeWays('chmod', (resource, tested) => { - let mode - beforeEach(() => { - mode = realFS.statSync(__filename).mode % 0o100000 - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename, - 'file.mode': mode.toString(8) - } - }) - - tested(fs, [__filename, mode], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', mode], agent)) - }) - - if (hasOSymlink) { - describeThreeWays('lchmod', (resource, tested) => { - let mode - beforeEach(() => { - mode = realFS.statSync(__filename).mode % 0o100000 - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename, - 'file.mode': mode.toString(8) - } - }) - - tested(fs, [__filename, mode], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', mode], agent)) - }) - } - - describeThreeWays('fchmod', (resource, tested) => { - let mode - let fd - beforeEach(() => { - mode = realFS.statSync(__filename).mode % 0o100000 - fd = realFS.openSync(__filename, 'r') - }) - afterEach(() => { - realFS.closeSync(fd) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: resource, - meta: { - 'file.descriptor': fd.toString(), - 'file.mode': mode.toString(8) - } - }) - - tested(fs, [fd, mode], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309, mode], agent)) - }) - - describeThreeWays('chown', (resource, tested) => { - let uid - let gid - beforeEach(() => { - const stats = realFS.statSync(__filename) - uid = stats.uid - gid = stats.gid - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename, - 'file.uid': uid.toString(), - 'file.gid': gid.toString() - } - }) - - tested(fs, [__filename, uid, gid], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', uid, gid], agent)) - }) - - if (hasOSymlink) { - describeThreeWays('lchown', (resource, tested) => { - let uid - let gid - beforeEach(() => { - const stats = realFS.statSync(__filename) - uid = stats.uid - gid = stats.gid - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename, - 'file.uid': uid.toString(), - 'file.gid': gid.toString() - } - }) - - tested(fs, [__filename, uid, gid], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', uid, gid], agent)) - }) - } - - describeThreeWays('fchown', (resource, tested) => { - let uid - let gid - let fd - beforeEach(() => { - const stats = realFS.statSync(__filename) - uid = stats.uid - gid = stats.gid - fd = realFS.openSync(__filename, 'r') - }) - afterEach(() => { - realFS.closeSync(fd) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString(), - 'file.uid': uid.toString(), - 'file.gid': gid.toString() - } - }) - - tested(fs, [fd, uid, gid], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309, uid, gid], agent)) - }) - - describeThreeWays('realpath', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename - } - }) - tested(fs, [__filename], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - - if (realFS.realpath.native) { - describeThreeWays('realpath.native', (resource, tested) => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': __filename - } - }) - tested(fs, [__filename], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - } - - describeThreeWays('readlink', (resource, tested) => { - let link - beforeEach(() => { - link = path.join(tmpdir, 'link') - realFS.symlinkSync(__filename, link) - }) - afterEach(() => { - realFS.unlinkSync(link) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': link - } - }) - tested(fs, [link], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - - describeThreeWays('unlink', (resource, tested) => { - let link - beforeEach(() => { - link = path.join(tmpdir, 'link') - realFS.symlinkSync(__filename, link) - }) - afterEach(() => { - try { - realFS.unlinkSync(link) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': link - } - }) - tested(fs, [link], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - - describeThreeWays('symlink', (resource, tested) => { - let link - beforeEach(() => { - link = path.join(tmpdir, 'link') - }) - afterEach(() => { - try { - realFS.unlinkSync(link) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.src': __filename, - 'file.dest': link - } - }) - tested(fs, [__filename, link], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [__filename, '/baddir/badfilename'], agent)) - }) - - describeThreeWays('link', (resource, tested) => { - let link - let sourceFile - beforeEach(() => { - sourceFile = path.join(tmpdir, 'source') - realFS.writeFileSync(sourceFile, '') - link = path.join(tmpdir, 'link') - }) - afterEach(() => { - try { - realFS.unlinkSync(sourceFile) - } catch (e) { /* */ } - try { - realFS.unlinkSync(link) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.src': sourceFile, - 'file.dest': link - } - }) - tested(fs, [sourceFile, link], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', link], agent)) - }) - - describeThreeWays('rmdir', (resource, tested) => { - let dir - beforeEach(() => { - dir = path.join(tmpdir, 'dir') - realFS.mkdirSync(dir) - }) - afterEach(() => { - try { - realFS.rmdirSync(dir) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': dir - } - }) - tested(fs, [dir], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename'], agent)) - }) - - describeThreeWays('rename', (resource, tested) => { - let src - let dest - beforeEach(() => { - src = path.join(tmpdir, 'src') - dest = path.join(tmpdir, 'dest') - realFS.writeFileSync(src, '') - }) - afterEach(() => { - try { - realFS.unlinkSync(dest) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.src': src, - 'file.dest': dest - } - }) - tested(fs, [src, dest], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', dest], agent)) - }) - - describeThreeWays('fsync', (resource, tested) => { - let fd - let tmpfile - beforeEach(() => { - tmpfile = path.join(tmpdir, 'fsync') - fd = realFS.openSync(tmpfile, 'w') - }) - afterEach(() => { - realFS.closeSync(fd) - realFS.unlinkSync(tmpfile) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - tested(fs, [fd], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309], agent)) - }) - - describeThreeWays('fdatasync', (resource, tested) => { - let fd - let tmpfile - beforeEach(() => { - tmpfile = path.join(tmpdir, 'fdatasync') - fd = realFS.openSync(tmpfile, 'w') - }) - afterEach(() => { - realFS.closeSync(fd) - realFS.unlinkSync(tmpfile) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - tested(fs, [fd], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309], agent)) - }) - - describeThreeWays('mkdir', (resource, tested) => { - let dir - beforeEach(() => { - dir = path.join(tmpdir, 'mkdir') - }) - afterEach(() => { - try { - realFS.rmdirSync(dir) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': dir - } - }) - tested(fs, [dir], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/baddir/baddir'], agent)) - }) - - describeThreeWays('truncate', (resource, tested) => { - let filename - beforeEach(() => { - filename = path.join(tmpdir, 'truncate') - realFS.writeFileSync(filename, Buffer.alloc(10)) - }) - afterEach(() => { - realFS.unlinkSync(filename) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': filename - } - }) - tested(fs, [filename, 5], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', 5], agent)) - }) - - describeThreeWays('ftruncate', (resource, tested) => { - let filename - let fd - beforeEach(() => { - filename = path.join(tmpdir, 'truncate') - realFS.writeFileSync(filename, Buffer.alloc(10)) - fd = realFS.openSync(filename, 'w+') - }) - afterEach(() => { - realFS.closeSync(fd) - realFS.unlinkSync(filename) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - tested(fs, [fd, 5], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309, 5], agent)) - }) - - describeThreeWays('utimes', (resource, tested) => { - let filename - beforeEach(() => { - filename = path.join(tmpdir, 'truncate') - realFS.writeFileSync(filename, '') - }) - afterEach(() => { - realFS.unlinkSync(filename) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.path': filename - } - }) - tested(fs, [filename, Date.now(), Date.now()], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, ['/badfilename', Date.now(), Date.now()], agent)) - }) - - describeThreeWays('futimes', (resource, tested) => { - let filename - let fd - beforeEach(() => { - filename = path.join(tmpdir, 'truncate') - realFS.writeFileSync(filename, '') - fd = realFS.openSync(filename, 'w') - }) - afterEach(() => { - realFS.closeSync(fd) - realFS.unlinkSync(filename) - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource, - meta: { - 'file.descriptor': fd.toString() - } - }) - tested(fs, [fd, Date.now(), Date.now()], done) - }) - - it('should handle errors', () => - testHandleErrors(fs, resource, tested, [8675309, Date.now(), Date.now()], agent)) - }) - - describe('mkdtemp', () => { - let tmpdir - afterEach(() => { - try { - realFS.rmdirSync(tmpdir) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - const inputDir = path.join(os.tmpdir(), 'mkdtemp-') - expectOneSpan(agent, done, { - resource: 'mkdtemp', - meta: { - 'file.path': inputDir - } - }) - fs.mkdtemp(inputDir, (err, result) => { - if (err) { - done(err) - return - } - tmpdir = result - }) - }) - - it('should handle errors', () => - testHandleErrors(fs, 'mkdtemp', (fs, args, _, cb) => { - fs.mkdtemp(...args, cb) - }, ['/baddir/baddir'], agent)) - }) - - describe('mkdtempSync', () => { - let tmpdir - afterEach(() => { - try { - realFS.rmdirSync(tmpdir) - } catch (e) { /* */ } - }) - - it('should be instrumented', (done) => { - const inputDir = path.join(os.tmpdir(), 'mkdtemp-') - expectOneSpan(agent, done, { - resource: 'mkdtempSync', - meta: { - 'file.path': inputDir - } - }) - tmpdir = fs.mkdtempSync(inputDir) - }) - - it('should handle errors', () => - testHandleErrors(fs, 'mkdtempSync', (fs, args, _, cb) => { - try { - fs.mkdtempSync(...args) - } catch (e) { - cb(e) - } - }, ['/baddir/baddir'], agent)) - }) - - describe('exists', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'exists', - meta: { - 'file.path': __filename - } - }) - fs.exists(__filename, () => {}) // eslint-disable-line node/no-deprecated-api - }) - - it('should support promisification', () => { - const exists = util.promisify(fs.exists) // eslint-disable-line node/no-deprecated-api - - return exists(__filename) - }) - }) - - describe('existsSync', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'existsSync', - meta: { - 'file.path': __filename - } - }) - fs.existsSync(__filename) - }) - }) - - if (realFS.Dir) { - describe('Dir', () => { - let dirname - let dir - beforeEach(async () => { - dirname = path.join(tmpdir, 'dir') - fs.mkdirSync(dirname) - fs.writeFileSync(path.join(dirname, '1'), '1') - fs.writeFileSync(path.join(dirname, '2'), '2') - fs.writeFileSync(path.join(dirname, '3'), '3') - dir = await fs.promises.opendir(dirname) - }) - afterEach(async () => { - try { - await dir.close() - } catch (e) { - if (e.code !== 'ERR_DIR_CLOSED') { - throw e - } - } - fs.unlinkSync(path.join(dirname, '1')) - fs.unlinkSync(path.join(dirname, '2')) - fs.unlinkSync(path.join(dirname, '3')) - fs.rmdirSync(dirname) - }) - - describe('close', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.close', - meta: { - 'file.path': dirname - } - }) - dir.close().catch(done) - }) - - it('should handle errors', () => - testHandleErrors(fs, 'dir.close', async (_1, _2, _3, cb) => { - dir.closeSync() - try { - // await for Node >=15.4 that returns and rejects a promise instead of throwing - await dir.close() - } catch (e) { - cb(e) - } - }, [], agent)) - - it('should be instrumented with callback', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.close', - meta: { - 'file.path': dirname - } - }) - dir.close(err => err && done(err)) - }) - - it('should handle errors with callback', () => - testHandleErrors(fs, 'dir.close', (_1, _2, _3, cb) => { - dir.closeSync() - try { - dir.close(cb) - } catch (e) { - cb(e) - } - }, [], agent)) - - it('Sync should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.closeSync', - meta: { - 'file.path': dirname - } - }) - dir.closeSync() - }) - - it('Sync should handle errors', () => - testHandleErrors(fs, 'dir.closeSync', (_1, _2, _3, cb) => { - dir.closeSync() - try { - dir.closeSync() - } catch (e) { - cb(e) - } - }, [], agent)) - }) - - describe('read', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.read', - meta: { - 'file.path': dirname - } - }) - dir.read().catch(done) - }) - - it('should handle errors', () => - testHandleErrors(fs, 'dir.read', (_1, _2, _3, cb) => { - dir.closeSync() - try { - dir.read() - } catch (e) { - cb(e) - } - }, [], agent)) - - it('should be instrumented with callback', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.read', - meta: { - 'file.path': dirname - } - }) - dir.read(err => err && done(err)) - }) - - it('should handle errors with callback', () => - testHandleErrors(fs, 'dir.read', (_1, _2, _3, cb) => { - dir.closeSync() - try { - dir.read(cb) - } catch (e) { - cb(e) - } - }, [], agent)) - - it('Sync should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.readSync', - meta: { - 'file.path': dirname - } - }) - dir.readSync() - }) - - it('Sync should handle errors', () => - testHandleErrors(fs, 'dir.readSync', (_1, _2, _3, cb) => { - dir.closeSync() - try { - dir.readSync() - } catch (e) { - cb(e) - } - }, [], agent)) - }) - - describe('Symbol.asyncIterator', () => { - it('should be instrumented for reads', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.read', - meta: { - 'file.path': dirname - } - }) - ;(async () => { - const iterator = dir[Symbol.asyncIterator]() - while (!(await iterator.next()).done) { /* noop */ } - })().catch(done) - }) - - it('should be instrumented for close', (done) => { - expectOneSpan(agent, done, { - resource: 'dir.close', - meta: { - 'file.path': dirname - } - }) - ;(async () => { - const iterator = dir[Symbol.asyncIterator]() - while (!(await iterator.next()).done) { /* noop */ } - })().catch(done) - }) - }) - }) - } - - if (realFS.promises) { - describe('FileHandle', () => { - let filehandle - let filename - beforeEach(async () => { - filename = path.join(os.tmpdir(), 'filehandle') - fs.writeFileSync(filename, 'some data') - filehandle = await fs.promises.open(filename, 'w+') - }) - afterEach(async () => { - try { - await filehandle.close() - realFS.closeSync(filehandle.fd) - } catch (e) { /* */ } - await fs.promises.unlink(filename) - }) - - describe('appendFile', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.appendFile', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.appendFile('some more data').catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'appendFile', ['some more data'], filehandle, agent)) - }) - - describe('writeFile', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.writeFile', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.writeFile('some more data').catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'writeFile', ['some more data'], filehandle, agent)) - }) - - describe('readFile', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.readFile', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.readFile().catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'readFile', [], filehandle, agent)) - }) - - describe('write', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.write', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.write('some more data').catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'write', ['some more data'], filehandle, agent)) - }) - - if (hasWritev) { - describe('writev', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.writev', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.writev([Buffer.from('some more data')]).catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'writev', [[Buffer.from('some more data')]], filehandle, agent)) - }) - } - - describe('read', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.read', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.read(Buffer.alloc(5), 0, 5, 0).catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'read', [Buffer.alloc(5), 0, 5, 0], filehandle, agent)) - }) - - describe('chmod', () => { - let mode - beforeEach(() => { - mode = realFS.statSync(__filename).mode % 0o100000 - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.chmod', - meta: { - 'file.descriptor': filehandle.fd.toString(), - 'file.mode': mode.toString(8) - } - }) - filehandle.chmod(mode).catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'chmod', [mode], filehandle, agent)) - }) - - describe('chown', () => { - let uid - let gid - beforeEach(() => { - const stats = realFS.statSync(filename) - uid = stats.uid - gid = stats.gid - }) - - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.chown', - meta: { - 'file.descriptor': filehandle.fd.toString(), - 'file.uid': uid.toString(), - 'file.gid': gid.toString() - } - }) - filehandle.chown(uid, gid).catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'chown', [uid, gid], filehandle, agent)) - }) - - describe('stat', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.stat', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.stat().catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testHandleErrors(fs, 'stat', [], filehandle, agent)) - }) - - describe('sync', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.sync', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.sync().catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testHandleErrors(fs, 'sync', [], filehandle, agent)) - }) - - describe('datasync', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.datasync', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.datasync().catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testHandleErrors(fs, 'datasync', [], filehandle, agent)) - }) - - describe('truncate', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.truncate', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.truncate(5).catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testHandleErrors(fs, 'truncate', [5], filehandle, agent)) - }) - - describe('utimes', () => { - it('should be instrumented', (done) => { - expectOneSpan(agent, done, { - resource: 'filehandle.utimes', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.utimes(Date.now(), Date.now()).catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testHandleErrors(fs, 'utimes', [Date.now(), Date.now()], filehandle, agent)) - }) - - describe('close', () => { - it('should be instrumented', function (done) { - expectOneSpan(agent, done, { - resource: 'filehandle.close', - meta: { - 'file.descriptor': filehandle.fd.toString() - } - }) - filehandle.close().catch(done) - }) - - // https://github.com/nodejs/node/issues/31361 - it.skip('should handle errors', () => - testFileHandleErrors(fs, 'close', [], filehandle, agent)) - }) - }) - } - - function describeThreeWays (name, fn) { - const reducer = (acc, cur) => acc[cur] - if (name.split('.').reduce(reducer, realFS)) { - describe(name, () => { - fn(name, (fs, args, done, withError) => { - const span = {} - return tracer.scope().activate(span, () => { - args.push((err) => { - expect(tracer.scope().active()).to.equal(span) - if (err) { - if (withError) withError(err) - else done(err) - } - }) - const func = name.split('.').reduce((acc, cur) => acc[cur], fs) - return func.apply(fs, args) - }) - }) - }) - } - - if (realFS.promises && name in realFS.promises) { - describe('promises.' + name, () => { - fn('promises.' + name, (fs, args, done, withError) => { - const span = {} - return tracer.scope().activate(span, () => { - return fs.promises[name].apply(fs.promises, args) - .then(() => { - expect(tracer.scope().active()).to.equal(span) - }) - .catch((err) => { - if (withError) withError(err) - else done(err) - }) - }) - }) - }) - } - - const nameSync = name + 'Sync' - - if (nameSync in realFS) { - describe(nameSync, () => { - fn(nameSync, (fs, args, _, withError) => { - try { - return fs[nameSync].apply(fs, args) - } catch (err) { - if (withError) withError(err) - else throw err - } - }) - }) - } - } - }) - }) -}) - -function mkExpected (props) { - const meta = Object.assign({ component: 'fs', 'span.kind': 'internal' }, props.meta) - const expected = Object.assign({ - name: 'fs.operation', - error: 0, - service: 'test' - }, props) - expected.meta = meta - return expected -} - -function expectOneSpan (agent, done, expected, timeout) { - expected = mkExpected(expected) - expectSomeSpan(agent, expected, timeout).then(done, done) -} - -function testHandleErrors (fs, name, tested, args, agent) { - return new Promise((resolve, reject) => { - function done (err) { - if (err) reject(err) - else resolve() - } - tested(fs, args, null, err => { - expectOneSpan(agent, done, { - resource: name, - error: 0 - }) - }) - }) -} - -function testFileHandleErrors (fs, method, args, filehandle, agent) { - const name = 'filehandle.' + method - return testHandleErrors(fs, name, (fs, args, _, cb) => { - filehandle.close() - .then(() => filehandle[method](...args)) - .catch(cb) - }, args, agent) -} diff --git a/packages/dd-trace/src/plugins/index.js b/packages/dd-trace/src/plugins/index.js index 4c3316da679..0fa71cd7301 100644 --- a/packages/dd-trace/src/plugins/index.js +++ b/packages/dd-trace/src/plugins/index.js @@ -15,7 +15,6 @@ module.exports = { 'express': require('../../../datadog-plugin-express/src'), 'fastify': require('../../../datadog-plugin-fastify/src'), 'find-my-way': require('../../../datadog-plugin-find-my-way/src'), - 'fs': require('../../../datadog-plugin-fs/src'), 'google-cloud-pubsub': require('../../../datadog-plugin-google-cloud-pubsub/src'), 'graphql': require('../../../datadog-plugin-graphql/src'), 'grpc': require('../../../datadog-plugin-grpc/src'),