dd-trace
Advanced tools
Comparing version 0.3.0 to 0.3.1
@@ -1,1 +0,1 @@ | ||
module.exports = '0.3.0' | ||
module.exports = '0.3.1' |
{ | ||
"name": "dd-trace", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "Datadog APM tracing client for JavaScript (experimental)", | ||
@@ -41,2 +41,3 @@ "main": "index.js", | ||
"lodash.kebabcase": "^4.1.1", | ||
"lodash.uniq": "^4.5.0", | ||
"methods": "^1.1.2", | ||
@@ -49,3 +50,2 @@ "msgpack-lite": "^0.1.26", | ||
"read-pkg-up": "^3.0.0", | ||
"require-dir": "^1.0.0", | ||
"require-in-the-middle": "^2.2.2", | ||
@@ -52,0 +52,0 @@ "safe-buffer": "^5.1.1", |
@@ -8,8 +8,11 @@ 'use strict' | ||
const pkg = require('../package.json') | ||
exec('npm whoami') | ||
exec('git checkout master') | ||
exec('git pull') | ||
const pkg = require('../package.json') | ||
exec(`git tag v${pkg.version}`) | ||
exec(`git push origin refs/tags/v${pkg.version}`) | ||
exec('npm publish') | ||
exec(`node scripts/publish_docs.js "v${pkg.version}"`) |
'use strict' | ||
const requireDir = require('require-dir') | ||
const path = require('path') | ||
const semver = require('semver') | ||
const hook = require('require-in-the-middle') | ||
const shimmer = require('shimmer') | ||
const uniq = require('lodash.uniq') | ||
const log = require('./log') | ||
// TODO: lazy load built-in plugins | ||
shimmer({ logger: () => {} }) | ||
@@ -14,3 +14,3 @@ class Instrumenter { | ||
this._tracer = tracer | ||
this._integrations = loadIntegrations() | ||
this._names = [] | ||
this._plugins = new Map() | ||
@@ -21,9 +21,8 @@ this._instrumented = new Map() | ||
use (name, config) { | ||
if (typeof name === 'string') { | ||
this._integrations | ||
.filter(plugin => plugin.name === name) | ||
.forEach(plugin => this._set(plugin, config)) | ||
} else if (name) { | ||
[].concat(name) | ||
.forEach(plugin => this._set(plugin, config)) | ||
config = config || {} | ||
try { | ||
this._set(require(`./plugins/${name}`), { name, config }) | ||
} catch (e) { | ||
log.debug(`Could not find a plugin named "${name}".`) | ||
} | ||
@@ -38,5 +37,8 @@ | ||
if (config.plugins !== false) { | ||
this._integrations.forEach(integration => { | ||
this._plugins.has(integration) || this._set(integration, {}) | ||
}) | ||
const plugins = require('./plugins') | ||
Object.keys(plugins) | ||
.forEach(name => { | ||
this._plugins.has(plugins[name]) || this._set(plugins[name], { name, config: {} }) | ||
}) | ||
} | ||
@@ -48,29 +50,69 @@ | ||
unpatch () { | ||
this._instrumented.forEach((instrumentation, moduleExports) => { | ||
instrumentation.unpatch(moduleExports) | ||
this._instrumented.forEach((moduleExports, instrumentation) => { | ||
this._unpatch(instrumentation) | ||
}) | ||
this._plugins.clear() | ||
} | ||
reload () { | ||
try { | ||
const instrumentedModules = Array.from(this._plugins.keys()).map(plugin => plugin.name) | ||
hook(instrumentedModules, this.hookModule.bind(this)) | ||
} catch (e) { | ||
log.error(e) | ||
const instrumentations = Array.from(this._plugins.keys()) | ||
.reduce((prev, current) => prev.concat(current), []) | ||
const instrumentedModules = uniq(instrumentations | ||
.map(instrumentation => instrumentation.name)) | ||
this._names = instrumentations | ||
.map(instrumentation => filename(instrumentation)) | ||
hook(instrumentedModules, { internals: true }, this.hookModule.bind(this)) | ||
} | ||
wrap (nodule, name) { | ||
if (typeof nodule[name] !== 'function') { | ||
throw new Error(`Expected object ${nodule} to contain method ${name}.`) | ||
} | ||
return shimmer.wrap.apply(this, arguments) | ||
} | ||
unwrap () { | ||
return shimmer.unwrap.apply(this, arguments) | ||
} | ||
hookModule (moduleExports, moduleName, moduleBaseDir) { | ||
if (this._names.indexOf(moduleName) === -1) { | ||
return moduleExports | ||
} | ||
const moduleVersion = getVersion(moduleBaseDir) | ||
Array.from(this._plugins.keys()) | ||
.filter(plugin => plugin.name === moduleName) | ||
.filter(plugin => matchVersion(moduleVersion, plugin.versions)) | ||
.forEach(plugin => { | ||
let moduleToPatch = moduleExports | ||
if (plugin.file) { | ||
moduleToPatch = require(path.join(moduleBaseDir, plugin.file)) | ||
.filter(plugin => [].concat(plugin).some(instrumentation => filename(instrumentation) === moduleName)) | ||
.forEach(plugin => this._validate(plugin, moduleBaseDir)) | ||
this._plugins | ||
.forEach((meta, plugin) => { | ||
try { | ||
[].concat(plugin) | ||
.filter(instrumentation => moduleName === filename(instrumentation)) | ||
.filter(instrumentation => matchVersion(moduleVersion, instrumentation.versions)) | ||
.forEach(instrumentation => { | ||
const modulePath = [moduleBaseDir, instrumentation.file].filter(val => val).join('/') | ||
if (require.cache[modulePath]) { | ||
log.debug([ | ||
`Instrumented module "${moduleName}" was imported before calling tracer.init().`, | ||
`Please make sure to initialize the tracer before importing any instrumented module.` | ||
].join(' ')) | ||
} | ||
this._instrumented.set(instrumentation, moduleExports) | ||
instrumentation.patch.call(this, moduleExports, this._tracer._tracer, this._plugins.get(plugin).config) | ||
}) | ||
} catch (e) { | ||
log.error(e) | ||
this._fail(plugin) | ||
log.debug(`Error while trying to patch ${meta.name}. The plugin has been disabled.`) | ||
} | ||
plugin.patch(moduleToPatch, this._tracer._tracer, this._plugins.get(plugin)) | ||
this._instrumented.set(moduleToPatch, plugin) | ||
}) | ||
@@ -81,22 +123,39 @@ | ||
_set (plugin, config) { | ||
config = config || {} | ||
_set (plugin, meta) { | ||
this._plugins.set(plugin, Object.assign({ config: {} }, meta)) | ||
} | ||
this._plugins.set(plugin, config) | ||
_validate (plugin, moduleBaseDir) { | ||
const meta = this._plugins.get(plugin) | ||
const instrumentations = [].concat(plugin) | ||
if (require.cache[plugin.name]) { | ||
log.debug([ | ||
`Instrumented module "${plugin.name}" was imported before calling tracer.init().`, | ||
`Please make sure to initialize the tracer before importing any instrumented module.` | ||
].join(' ')) | ||
for (let i = 0; i < instrumentations.length; i++) { | ||
if (instrumentations[i].file && !exists(moduleBaseDir, instrumentations[i].file)) { | ||
this._fail(plugin) | ||
log.debug([ | ||
`Plugin "${meta.name}" requires "${instrumentations[i].file}" which was not found.`, | ||
`The plugin was disabled.` | ||
].join(' ')) | ||
break | ||
} | ||
} | ||
} | ||
} | ||
function loadIntegrations () { | ||
const integrations = requireDir(path.join(__dirname, './plugins')) | ||
_fail (plugin) { | ||
[].concat(plugin) | ||
.forEach(instrumentation => { | ||
this._unpatch(instrumentation) | ||
this._instrumented.delete(instrumentation) | ||
}) | ||
return Object.keys(integrations) | ||
.map(key => integrations[key]) | ||
.reduce((previous, current) => previous.concat(current), []) | ||
this._plugins.delete(plugin) | ||
} | ||
_unpatch (instrumentation) { | ||
try { | ||
instrumentation.unpatch.call(this, this._instrumented.get(instrumentation)) | ||
} catch (e) { | ||
log.error(e) | ||
} | ||
} | ||
} | ||
@@ -110,3 +169,3 @@ | ||
if (moduleBaseDir) { | ||
const packageJSON = path.join(moduleBaseDir, 'package.json') | ||
const packageJSON = `${moduleBaseDir}/package.json` | ||
return require(packageJSON).version | ||
@@ -116,2 +175,15 @@ } | ||
function filename (plugin) { | ||
return [plugin.name, plugin.file].filter(val => val).join('/') | ||
} | ||
function exists (basedir, file) { | ||
try { | ||
require.resolve(`${basedir}/${file}`) | ||
return true | ||
} catch (e) { | ||
return false | ||
} | ||
} | ||
module.exports = Instrumenter |
'use strict' | ||
const shimmer = require('shimmer') | ||
const kebabCase = require('lodash.kebabcase') | ||
@@ -113,7 +112,12 @@ | ||
'resource.name': getResourceName(method, fields), | ||
'span.type': 'worker', | ||
'out.host': channel.connection.stream._host, | ||
'out.port': channel.connection.stream.remotePort | ||
'span.type': 'worker' | ||
}) | ||
if (channel.connection && channel.connection.stream) { | ||
span.addTags({ | ||
'out.host': channel.connection.stream._host, | ||
'out.port': channel.connection.stream.remotePort | ||
}) | ||
} | ||
switch (method) { | ||
@@ -155,14 +159,14 @@ case 'basic.publish': | ||
patch (channel, tracer, config) { | ||
shimmer.wrap(channel.Channel.prototype, 'sendOrEnqueue', createWrapSendOrEnqueue(tracer, config)) | ||
shimmer.wrap(channel.Channel.prototype, 'sendImmediately', createWrapSendImmediately(tracer, config)) | ||
shimmer.wrap(channel.Channel.prototype, 'sendMessage', createWrapSendMessage(tracer, config)) | ||
shimmer.wrap(channel.BaseChannel.prototype, 'dispatchMessage', createWrapDispatchMessage(tracer, config)) | ||
this.wrap(channel.Channel.prototype, 'sendOrEnqueue', createWrapSendOrEnqueue(tracer, config)) | ||
this.wrap(channel.Channel.prototype, 'sendImmediately', createWrapSendImmediately(tracer, config)) | ||
this.wrap(channel.Channel.prototype, 'sendMessage', createWrapSendMessage(tracer, config)) | ||
this.wrap(channel.BaseChannel.prototype, 'dispatchMessage', createWrapDispatchMessage(tracer, config)) | ||
}, | ||
unpatch (channel) { | ||
shimmer.unwrap(channel.Channel.prototype, 'sendOrEnqueue') | ||
shimmer.unwrap(channel.Channel.prototype, 'sendImmediately') | ||
shimmer.unwrap(channel.Channel.prototype, 'sendMessage') | ||
shimmer.unwrap(channel.BaseChannel.prototype, 'dispatchMessage') | ||
this.unwrap(channel.Channel.prototype, 'sendOrEnqueue') | ||
this.unwrap(channel.Channel.prototype, 'sendImmediately') | ||
this.unwrap(channel.Channel.prototype, 'sendMessage') | ||
this.unwrap(channel.BaseChannel.prototype, 'dispatchMessage') | ||
} | ||
} | ||
] |
'use strict' | ||
const Tags = require('opentracing').Tags | ||
const shimmer = require('shimmer') | ||
@@ -113,6 +112,6 @@ function createWrapRequest (tracer, config) { | ||
patch (ConnectionPool, tracer, config) { | ||
shimmer.wrap(ConnectionPool.prototype, 'select', createWrapSelect(tracer, config)) | ||
this.wrap(ConnectionPool.prototype, 'select', createWrapSelect(tracer, config)) | ||
}, | ||
unpatch (ConnectionPool) { | ||
shimmer.unwrap(ConnectionPool.prototype, 'select') | ||
this.unwrap(ConnectionPool.prototype, 'select') | ||
} | ||
@@ -125,8 +124,8 @@ }, | ||
patch (Transport, tracer, config) { | ||
shimmer.wrap(Transport.prototype, 'request', createWrapRequest(tracer, config)) | ||
this.wrap(Transport.prototype, 'request', createWrapRequest(tracer, config)) | ||
}, | ||
unpatch (Transport) { | ||
shimmer.unwrap(Transport.prototype, 'request') | ||
this.unwrap(Transport.prototype, 'request') | ||
} | ||
} | ||
] |
@@ -6,3 +6,2 @@ 'use strict' | ||
const FORMAT_HTTP_HEADERS = opentracing.FORMAT_HTTP_HEADERS | ||
const shimmer = require('shimmer') | ||
const METHODS = require('methods').concat('use', 'route', 'param', 'all') | ||
@@ -153,14 +152,14 @@ const pathToRegExp = require('path-to-regexp') | ||
METHODS.forEach(method => { | ||
shimmer.wrap(express.application, method, createWrapMethod(tracer, config)) | ||
this.wrap(express.application, method, createWrapMethod(tracer, config)) | ||
}) | ||
shimmer.wrap(express.Router, 'process_params', createWrapProcessParams(tracer, config)) | ||
shimmer.wrap(express.Router, 'use', createWrapRouterMethod(tracer, config)) | ||
shimmer.wrap(express.Router, 'route', createWrapRouterMethod(tracer, config)) | ||
this.wrap(express.Router, 'process_params', createWrapProcessParams(tracer, config)) | ||
this.wrap(express.Router, 'use', createWrapRouterMethod(tracer, config)) | ||
this.wrap(express.Router, 'route', createWrapRouterMethod(tracer, config)) | ||
} | ||
function unpatch (express) { | ||
METHODS.forEach(method => shimmer.unwrap(express.application, method)) | ||
shimmer.unwrap(express.Router, 'process_params') | ||
shimmer.unwrap(express.Router, 'use') | ||
shimmer.unwrap(express.Router, 'route') | ||
METHODS.forEach(method => this.unwrap(express.application, method)) | ||
this.unwrap(express.Router, 'process_params') | ||
this.unwrap(express.Router, 'use') | ||
this.unwrap(express.Router, 'route') | ||
} | ||
@@ -167,0 +166,0 @@ |
'use strict' | ||
const shimmer = require('shimmer') | ||
const platform = require('../platform') | ||
function createWrapGraphql (tracer, config, defaultFieldResolver) { | ||
return function wrapGraphql (graphql) { | ||
return function graphqlWithTrace () { | ||
const source = arguments[1] || arguments[0].source | ||
const contextValue = arguments[3] || arguments[0].contextValue || {} | ||
function createWrapExecute (tracer, config, defaultFieldResolver) { | ||
return function wrapExecute (execute) { | ||
return function executeWithTrace () { | ||
const args = normalizeArgs(arguments) | ||
const schema = args.schema | ||
const document = args.document | ||
const contextValue = args.contextValue || {} | ||
const fieldResolver = args.fieldResolver || defaultFieldResolver | ||
if (arguments.length === 1) { | ||
arguments[0].contextValue = contextValue | ||
} else { | ||
arguments[3] = contextValue | ||
arguments.length = Math.max(arguments.length, 4) | ||
if (!schema || !document || typeof fieldResolver !== 'function') { | ||
return execute.apply(this, arguments) | ||
} | ||
args.fieldResolver = wrapResolve(fieldResolver, tracer, config) | ||
args.contextValue = contextValue | ||
Object.defineProperties(contextValue, { | ||
_datadog_operation: { value: {} }, | ||
_datadog_fields: { value: {} }, | ||
_datadog_source: { value: source } | ||
_datadog_source: { value: document._datadog_source } | ||
}) | ||
return graphql.apply(this, arguments) | ||
if (!schema._datadog_patched) { | ||
wrapFields(schema._queryType._fields, tracer, config, []) | ||
schema._datadog_patched = true | ||
} | ||
return call(execute, this, [args], defer(tracer), () => finishOperation(contextValue)) | ||
} | ||
@@ -30,18 +37,12 @@ } | ||
function createWrapExecute (tracer, config, defaultFieldResolver) { | ||
return function wrapExecute (execute) { | ||
return function executeWithTrace () { | ||
const schema = arguments[0] | ||
const contextValue = arguments[3] | ||
const fieldResolver = arguments[6] || defaultFieldResolver | ||
function createWrapParse () { | ||
return function wrapParse (parse) { | ||
return function parseWithTrace (source) { | ||
const document = parse.apply(this, arguments) | ||
arguments[6] = wrapResolve(fieldResolver, tracer, config) | ||
arguments[3] = contextValue | ||
Object.defineProperties(document, { | ||
_datadog_source: { value: source } | ||
}) | ||
if (!schema._datadog_patched) { | ||
wrapFields(schema._queryType._fields, tracer, config, []) | ||
schema._datadog_patched = true | ||
} | ||
return call(execute, this, arguments, defer(tracer), () => finishOperation(contextValue)) | ||
return document | ||
} | ||
@@ -139,2 +140,18 @@ } | ||
function normalizeArgs (args) { | ||
if (args.length === 1) { | ||
return args | ||
} | ||
return { | ||
schema: args[0], | ||
document: args[1], | ||
rootValue: args[2], | ||
contextValue: args[3], | ||
variableValues: args[4], | ||
operationName: args[5], | ||
fieldResolver: args[6] | ||
} | ||
} | ||
function createOperationSpan (tracer, config, contextValue, info) { | ||
@@ -223,9 +240,9 @@ const type = info.operation.operation | ||
name: 'graphql', | ||
file: 'graphql.js', | ||
file: 'execution/execute.js', | ||
versions: ['0.13.x'], | ||
patch (graphql, tracer, config) { | ||
shimmer.wrap(graphql, 'graphql', createWrapGraphql(tracer, config)) | ||
patch (execute, tracer, config) { | ||
this.wrap(execute, 'execute', createWrapExecute(tracer, config, execute.defaultFieldResolver)) | ||
}, | ||
unpatch (graphql) { | ||
shimmer.unwrap(graphql, 'graphql') | ||
unpatch (execute) { | ||
this.unwrap(execute, 'execute') | ||
} | ||
@@ -235,11 +252,11 @@ }, | ||
name: 'graphql', | ||
file: 'execution/execute.js', | ||
file: 'language/parser.js', | ||
versions: ['0.13.x'], | ||
patch (execute, tracer, config) { | ||
shimmer.wrap(execute, 'execute', createWrapExecute(tracer, config, execute.defaultFieldResolver)) | ||
patch (parser, tracer, config) { | ||
this.wrap(parser, 'parse', createWrapParse(tracer, config)) | ||
}, | ||
unpatch (execute) { | ||
shimmer.unwrap(execute, 'execute') | ||
unpatch (parser) { | ||
this.unwrap(parser, 'parse') | ||
} | ||
} | ||
] |
@@ -5,3 +5,2 @@ 'use strict' | ||
const opentracing = require('opentracing') | ||
const shimmer = require('shimmer') | ||
@@ -12,4 +11,4 @@ const Tags = opentracing.Tags | ||
function patch (http, tracer, config) { | ||
shimmer.wrap(http, 'request', request => makeRequestTrace(request)) | ||
shimmer.wrap(http, 'get', get => makeRequestTrace(get)) | ||
this.wrap(http, 'request', request => makeRequestTrace(request)) | ||
this.wrap(http, 'get', get => makeRequestTrace(get)) | ||
@@ -94,4 +93,4 @@ function makeRequestTrace (request) { | ||
function unpatch (http) { | ||
shimmer.unwrap(http, 'request') | ||
shimmer.unwrap(http, 'get') | ||
this.unwrap(http, 'request') | ||
this.unwrap(http, 'get') | ||
} | ||
@@ -98,0 +97,0 @@ |
@@ -5,4 +5,2 @@ 'use strict' | ||
const shimmer = require('shimmer') | ||
// Reference https://docs.mongodb.com/v3.6/reference/command/ | ||
@@ -198,10 +196,4 @@ const DATABASE_COMMANDS = [ | ||
span = child | ||
span.addTags({ | ||
'service.name': config.service || 'mongodb', | ||
'resource.name': resource, | ||
'span.type': 'db', | ||
'db.name': ns, | ||
'out.host': this.s.options.host, | ||
'out.port': this.s.options.port | ||
}) | ||
addTags(span, config, resource, ns, this) | ||
}) | ||
@@ -227,11 +219,10 @@ | ||
span = child | ||
span.addTags({ | ||
'service.name': config.service || 'mongodb', | ||
'resource.name': resource, | ||
'span.type': 'db', | ||
'db.name': this.ns, | ||
'out.host': this.topology.s.options.host, | ||
'out.port': this.topology.s.options.port, | ||
'mongodb.cursor.index': this.cursorState.cursorIndex | ||
}) | ||
addTags(span, config, resource, this.ns, this.topology) | ||
if (this.cursorState) { | ||
span.addTags({ | ||
'mongodb.cursor.index': this.cursorState.cursorIndex | ||
}) | ||
} | ||
}) | ||
@@ -244,2 +235,18 @@ | ||
function addTags (span, config, resource, ns, topology) { | ||
span.addTags({ | ||
'service.name': config.service || 'mongodb', | ||
'resource.name': resource, | ||
'span.type': 'db', | ||
'db.name': ns | ||
}) | ||
if (topology.s && topology.s.options) { | ||
span.addTags({ | ||
'out.host': topology.s.options.host, | ||
'out.port': topology.s.options.port | ||
}) | ||
} | ||
} | ||
function wrapCallback (tracer, span, done) { | ||
@@ -300,16 +307,16 @@ return tracer.bind((err, res) => { | ||
patch (mongo, tracer, config) { | ||
shimmer.wrap(mongo.Server.prototype, 'command', createWrapOperation(tracer, config)) | ||
shimmer.wrap(mongo.Server.prototype, 'insert', createWrapOperation(tracer, config, 'insert')) | ||
shimmer.wrap(mongo.Server.prototype, 'update', createWrapOperation(tracer, config, 'update')) | ||
shimmer.wrap(mongo.Server.prototype, 'remove', createWrapOperation(tracer, config, 'remove')) | ||
shimmer.wrap(mongo.Cursor.prototype, 'next', createWrapNext(tracer, config)) | ||
this.wrap(mongo.Server.prototype, 'command', createWrapOperation(tracer, config)) | ||
this.wrap(mongo.Server.prototype, 'insert', createWrapOperation(tracer, config, 'insert')) | ||
this.wrap(mongo.Server.prototype, 'update', createWrapOperation(tracer, config, 'update')) | ||
this.wrap(mongo.Server.prototype, 'remove', createWrapOperation(tracer, config, 'remove')) | ||
this.wrap(mongo.Cursor.prototype, 'next', createWrapNext(tracer, config)) | ||
}, | ||
unpatch (mongo) { | ||
shimmer.unwrap(mongo.Server.prototype, 'command') | ||
shimmer.unwrap(mongo.Server.prototype, 'insert') | ||
shimmer.unwrap(mongo.Server.prototype, 'update') | ||
shimmer.unwrap(mongo.Server.prototype, 'remove') | ||
shimmer.unwrap(mongo.Cursor.prototype, 'next') | ||
this.unwrap(mongo.Server.prototype, 'command') | ||
this.unwrap(mongo.Server.prototype, 'insert') | ||
this.unwrap(mongo.Server.prototype, 'update') | ||
this.unwrap(mongo.Server.prototype, 'remove') | ||
this.unwrap(mongo.Cursor.prototype, 'next') | ||
} | ||
} | ||
] |
'use strict' | ||
const Tags = require('opentracing').Tags | ||
const shimmer = require('shimmer') | ||
@@ -65,7 +64,7 @@ function createWrapQuery (tracer, config) { | ||
function patchConnection (Connection, tracer, config) { | ||
shimmer.wrap(Connection.prototype, 'query', createWrapQuery(tracer, config)) | ||
this.wrap(Connection.prototype, 'query', createWrapQuery(tracer, config)) | ||
} | ||
function unpatchConnection (Connection) { | ||
shimmer.unwrap(Connection.prototype, 'query') | ||
this.unwrap(Connection.prototype, 'query') | ||
} | ||
@@ -72,0 +71,0 @@ |
'use strict' | ||
const Tags = require('opentracing').Tags | ||
const shimmer = require('shimmer') | ||
@@ -65,7 +64,7 @@ function createWrapQuery (tracer, config) { | ||
function patchConnection (Connection, tracer, config) { | ||
shimmer.wrap(Connection.prototype, 'query', createWrapQuery(tracer, config)) | ||
this.wrap(Connection.prototype, 'query', createWrapQuery(tracer, config)) | ||
} | ||
function unpatchConnection (Connection) { | ||
shimmer.unwrap(Connection.prototype, 'query') | ||
this.unwrap(Connection.prototype, 'query') | ||
} | ||
@@ -72,0 +71,0 @@ |
'use strict' | ||
const Tags = require('opentracing').Tags | ||
const shimmer = require('shimmer') | ||
@@ -25,8 +24,11 @@ const OPERATION_NAME = 'pg.query' | ||
span.setTag('resource.name', statement) | ||
span.setTag('db.name', params.database) | ||
span.setTag('db.user', params.user) | ||
span.setTag('out.host', params.host) | ||
span.setTag('out.port', String(params.port)) | ||
span.setTag('span.type', 'db') | ||
if (params) { | ||
span.setTag('db.name', params.database) | ||
span.setTag('db.user', params.user) | ||
span.setTag('out.host', params.host) | ||
span.setTag('out.port', String(params.port)) | ||
} | ||
if (err) { | ||
@@ -52,7 +54,7 @@ span.addTags({ | ||
shimmer.wrap(pg.Client.prototype, 'query', queryWrap) | ||
this.wrap(pg.Client.prototype, 'query', queryWrap) | ||
} | ||
function unpatch (pg) { | ||
shimmer.unwrap(pg.Client.prototype, 'query') | ||
this.unwrap(pg.Client.prototype, 'query') | ||
} | ||
@@ -59,0 +61,0 @@ |
'use strict' | ||
const Tags = require('opentracing').Tags | ||
const shimmer = require('shimmer') | ||
@@ -19,4 +18,5 @@ function createWrapCreateClient (tracer) { | ||
return function createStreamWithTrace () { | ||
createStream.apply(this, arguments) | ||
const returnValue = createStream.apply(this, arguments) | ||
tracer.bindEmitter(this.stream) | ||
return returnValue | ||
} | ||
@@ -42,6 +42,11 @@ } | ||
'span.type': 'db', | ||
'db.name': this.selected_db || '0', | ||
'out.host': String(this.connection_options.host), | ||
'out.port': String(this.connection_options.port) | ||
'db.name': this.selected_db || '0' | ||
}) | ||
if (this.connection_options) { | ||
span.addTags({ | ||
'out.host': String(this.connection_options.host), | ||
'out.port': String(this.connection_options.port) | ||
}) | ||
} | ||
}) | ||
@@ -75,11 +80,11 @@ | ||
function patch (redis, tracer, config) { | ||
shimmer.wrap(redis.RedisClient.prototype, 'internal_send_command', createWrapInternalSendCommand(tracer, config)) | ||
shimmer.wrap(redis, 'createClient', createWrapCreateClient(tracer, config)) | ||
shimmer.wrap(redis.RedisClient.prototype, 'create_stream', createWrapCreateStream(tracer, config)) | ||
this.wrap(redis.RedisClient.prototype, 'internal_send_command', createWrapInternalSendCommand(tracer, config)) | ||
this.wrap(redis, 'createClient', createWrapCreateClient(tracer, config)) | ||
this.wrap(redis.RedisClient.prototype, 'create_stream', createWrapCreateStream(tracer, config)) | ||
} | ||
function unpatch (redis) { | ||
shimmer.unwrap(redis.RedisClient.prototype, 'internal_send_command') | ||
shimmer.unwrap(redis, 'createClient') | ||
shimmer.unwrap(redis.RedisClient.prototype, 'create_stream') | ||
this.unwrap(redis.RedisClient.prototype, 'internal_send_command') | ||
this.unwrap(redis, 'createClient') | ||
this.unwrap(redis.RedisClient.prototype, 'create_stream') | ||
} | ||
@@ -86,0 +91,0 @@ |
'use strict' | ||
const path = require('path') | ||
const proxyquire = require('proxyquire').noCallThru() | ||
@@ -11,6 +10,3 @@ | ||
let tracer | ||
let requireDir | ||
let Connection | ||
let Pool | ||
let plugins | ||
let shimmer | ||
@@ -38,3 +34,3 @@ beforeEach(() => { | ||
versions: ['2.x'], | ||
file: 'lib/Connection.js', | ||
file: 'lib/connection.js', | ||
patch: sinon.spy(), | ||
@@ -46,3 +42,3 @@ unpatch: sinon.spy() | ||
versions: ['2.x'], | ||
file: 'lib/Pool.js', | ||
file: 'lib/pool.js', | ||
patch: sinon.spy(), | ||
@@ -54,25 +50,16 @@ unpatch: sinon.spy() | ||
plugins = { | ||
other: { | ||
name: 'other', | ||
versions: ['1.x'], | ||
patch: sinon.spy(), | ||
unpatch: sinon.spy() | ||
} | ||
} | ||
shimmer = sinon.spy() | ||
shimmer.wrap = sinon.spy() | ||
shimmer.unwrap = sinon.spy() | ||
const mysqlDir = path.normalize(path.join(__dirname, 'node_modules', 'mysql-mock')) | ||
const connectionPath = path.join(mysqlDir, 'lib', 'Connection.js') | ||
const poolPath = path.join(mysqlDir, 'lib', 'Pool.js') | ||
Connection = 'Connection' | ||
Pool = 'Pool' | ||
requireDir = sinon.stub() | ||
requireDir.withArgs(path.join(__dirname, '../src/plugins')).returns(integrations) | ||
Instrumenter = proxyquire('../src/instrumenter', { | ||
'require-dir': requireDir, | ||
[connectionPath]: Connection, | ||
[poolPath]: Pool | ||
'shimmer': shimmer, | ||
'./plugins': { | ||
'http': integrations.http, | ||
'express-mock': integrations.express, | ||
'mysql-mock': integrations.mysql | ||
}, | ||
'./plugins/http': integrations.http, | ||
'./plugins/express-mock': integrations.express, | ||
'./plugins/mysql-mock': integrations.mysql | ||
}) | ||
@@ -83,2 +70,6 @@ | ||
afterEach(() => { | ||
delete require.cache[require.resolve('mysql-mock')] | ||
}) | ||
describe('with integrations enabled', () => { | ||
@@ -97,6 +88,4 @@ describe('use', () => { | ||
it('should allow configuring a plugin by instance', () => { | ||
const config = { foo: 'bar' } | ||
instrumenter.use(integrations.express, config) | ||
it('should default to an empty plugin configuration', () => { | ||
instrumenter.use('express-mock') | ||
instrumenter.patch() | ||
@@ -106,46 +95,50 @@ | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer', config) | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer', {}) | ||
}) | ||
it('should default to an empty plugin configuration', () => { | ||
it('should reapply the require hook when called multiple times', () => { | ||
instrumenter.use('mysql-mock') | ||
instrumenter.use('express-mock') | ||
instrumenter.patch() | ||
const express = require('express-mock') | ||
require('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer', {}) | ||
expect(integrations.express.patch).to.have.been.called | ||
}) | ||
it('should support a plugin instance', () => { | ||
const express = require('express-mock') | ||
it('should handle errors', () => { | ||
expect(() => instrumenter.use()).not.to.throw() | ||
}) | ||
instrumenter.use(express) | ||
instrumenter.patch() | ||
it('should not patch modules with the wrong API', () => { | ||
integrations.express.patch = sinon.stub().throws(new Error()) | ||
require('express-mock') | ||
instrumenter.use('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer', {}) | ||
const express = require('express-mock') | ||
expect(integrations.express.unpatch).to.have.been.calledWith(express) | ||
}) | ||
it('should reapply the require hook when called multiple times', () => { | ||
it('should not patch modules with invalid files', () => { | ||
integrations.mysql[0].file = 'invalid.js' | ||
instrumenter.use('mysql-mock') | ||
instrumenter.use('express-mock') | ||
instrumenter.patch() | ||
require('express-mock') | ||
require('mysql-mock') | ||
expect(integrations.express.patch).to.have.been.called | ||
expect(integrations.mysql[0].patch).to.not.have.been.called | ||
expect(integrations.mysql[1].patch).to.not.have.been.called | ||
}) | ||
it('should support third party plugins', () => { | ||
instrumenter.use(plugins.other) | ||
instrumenter.patch() | ||
it('should handle errors when unpatching', () => { | ||
integrations.mysql[1].unpatch = sinon.stub().throws(new Error()) | ||
integrations.mysql[1].file = 'invalid.js' | ||
const other = require('other') | ||
instrumenter.use('mysql-mock') | ||
expect(plugins.other.patch).to.have.been.calledWith(other, 'tracer', {}) | ||
}) | ||
require('mysql-mock') | ||
it('should handle errors', () => { | ||
expect(() => instrumenter.use()).not.to.throw() | ||
expect(integrations.mysql[0].patch).to.not.have.been.called | ||
expect(integrations.mysql[1].patch).to.not.have.been.called | ||
}) | ||
@@ -182,2 +175,5 @@ }) | ||
it('should support patching multiple files', () => { | ||
const Connection = require('mysql-mock/lib/connection') | ||
const Pool = require('mysql-mock/lib/pool') | ||
instrumenter.patch() | ||
@@ -187,3 +183,4 @@ | ||
expect(mysql).to.deep.equal({ foo: 'bar' }) | ||
expect(mysql).to.deep.equal({ name: 'mysql' }) | ||
expect(integrations.mysql[0].patch).to.have.been.calledWith(Connection, 'tracer', {}) | ||
@@ -204,3 +201,50 @@ expect(integrations.mysql[1].patch).to.have.been.calledWith(Pool, 'tracer', {}) | ||
}) | ||
it('should remove the require hooks', () => { | ||
instrumenter.patch() | ||
instrumenter.unpatch() | ||
require('express-mock') | ||
expect(integrations.express.patch).to.not.have.been.called | ||
}) | ||
it('should handle errors', () => { | ||
integrations.mysql[0].unpatch = sinon.stub().throws(new Error()) | ||
instrumenter.patch() | ||
require('mysql-mock') | ||
expect(() => instrumenter.unpatch()).to.not.throw() | ||
expect(integrations.mysql[1].unpatch).to.have.been.called | ||
}) | ||
}) | ||
describe('wrap', () => { | ||
it('should wrap the method on the object', () => { | ||
const obj = { method: () => {} } | ||
const wrapper = () => {} | ||
instrumenter.wrap(obj, 'method', wrapper) | ||
expect(shimmer.wrap).to.have.been.calledWith(obj, 'method', wrapper) | ||
}) | ||
it('should throw if the method does not exist', () => { | ||
const obj = {} | ||
const wrapper = () => {} | ||
expect(() => instrumenter.wrap(obj, 'method', wrapper)).to.throw() | ||
}) | ||
}) | ||
describe('unwrap', () => { | ||
it('should wrap the method on the object', () => { | ||
const obj = { method: () => {} } | ||
instrumenter.unwrap(obj, 'method') | ||
expect(shimmer.unwrap).to.have.been.calledWith(obj, 'method') | ||
}) | ||
}) | ||
}) | ||
@@ -220,12 +264,2 @@ | ||
it('should support an array of plugins', () => { | ||
instrumenter.use(integrations.mysql) | ||
instrumenter.patch({ plugins: false }) | ||
require('mysql-mock') | ||
expect(integrations.mysql[0].patch).to.have.been.calledWith(Connection, 'tracer', {}) | ||
expect(integrations.mysql[1].patch).to.have.been.calledWith(Pool, 'tracer', {}) | ||
}) | ||
describe('patch', () => { | ||
@@ -232,0 +266,0 @@ it('should not patch any module', () => { |
@@ -0,3 +1,6 @@ | ||
require('./lib/connection') | ||
require('./lib/pool') | ||
module.exports = { | ||
foo: 'bar' | ||
name: 'mysql' | ||
} |
@@ -9,2 +9,3 @@ 'use strict' | ||
const express = require('express') | ||
const path = require('path') | ||
@@ -19,3 +20,7 @@ let agent = null | ||
module.exports = { | ||
load (plugin, moduleToPatch, config) { | ||
load (plugin, pluginName, config) { | ||
[].concat(plugin).forEach(instrumentation => { | ||
this.wipe(instrumentation.name) | ||
}) | ||
tracer = require('../..') | ||
@@ -59,5 +64,3 @@ agent = express() | ||
tracer.use(plugin, config) | ||
require(moduleToPatch) | ||
tracer.use(pluginName, config) | ||
}) | ||
@@ -119,3 +122,13 @@ }) | ||
}) | ||
}, | ||
wipe (moduleName) { | ||
const basedir = path.join(__dirname, '..', '..', 'node_modules', moduleName) | ||
Object.keys(require.cache) | ||
.filter(name => name.indexOf(basedir) !== -1) | ||
.forEach(name => { | ||
delete require.cache[name] | ||
}) | ||
} | ||
} |
@@ -14,3 +14,2 @@ 'use strict' | ||
beforeEach(() => { | ||
elasticsearch = require('elasticsearch') | ||
plugin = require('../../src/plugins/elasticsearch') | ||
@@ -28,7 +27,9 @@ tracer = require('../..') | ||
beforeEach(() => { | ||
client = new elasticsearch.Client({ | ||
host: 'localhost:9200' | ||
}) | ||
return agent.load(plugin, 'elasticsearch') | ||
.then(() => { | ||
elasticsearch = require('elasticsearch') | ||
client = new elasticsearch.Client({ | ||
host: 'localhost:9200' | ||
}) | ||
}) | ||
}) | ||
@@ -235,7 +236,9 @@ | ||
beforeEach(() => { | ||
client = new elasticsearch.Client({ | ||
host: 'localhost:9200' | ||
}) | ||
return agent.load(plugin, 'elasticsearch', { service: 'test' }) | ||
.then(() => { | ||
elasticsearch = require('elasticsearch') | ||
client = new elasticsearch.Client({ | ||
host: 'localhost:9200' | ||
}) | ||
}) | ||
}) | ||
@@ -242,0 +245,0 @@ |
@@ -18,3 +18,2 @@ 'use strict' | ||
plugin = require('../../src/plugins/express') | ||
express = require('express') | ||
context = require('../../src/platform').context() | ||
@@ -31,2 +30,5 @@ }) | ||
return agent.load(plugin, 'express') | ||
.then(() => { | ||
express = require('express') | ||
}) | ||
}) | ||
@@ -436,2 +438,5 @@ | ||
return agent.load(plugin, 'express', config) | ||
.then(() => { | ||
express = require('express') | ||
}) | ||
}) | ||
@@ -438,0 +443,0 @@ |
@@ -14,43 +14,44 @@ 'use strict' | ||
describe('graphql', () => { | ||
beforeEach(() => { | ||
plugin = require('../../src/plugins/graphql') | ||
graphql = require('graphql') | ||
context = require('../../src/platform').context() | ||
schema = new graphql.GraphQLSchema({ | ||
query: new graphql.GraphQLObjectType({ | ||
name: 'RootQueryType', | ||
fields: { | ||
hello: { | ||
type: graphql.GraphQLString, | ||
args: { | ||
name: { | ||
type: graphql.GraphQLString | ||
} | ||
}, | ||
resolve (obj, args) { | ||
return args.name | ||
function buildSchema () { | ||
schema = new graphql.GraphQLSchema({ | ||
query: new graphql.GraphQLObjectType({ | ||
name: 'RootQueryType', | ||
fields: { | ||
hello: { | ||
type: graphql.GraphQLString, | ||
args: { | ||
name: { | ||
type: graphql.GraphQLString | ||
} | ||
}, | ||
human: { | ||
type: new graphql.GraphQLObjectType({ | ||
name: 'Human', | ||
fields: { | ||
name: { | ||
type: graphql.GraphQLString, | ||
resolve (obj, args) { | ||
return obj | ||
} | ||
resolve (obj, args) { | ||
return args.name | ||
} | ||
}, | ||
human: { | ||
type: new graphql.GraphQLObjectType({ | ||
name: 'Human', | ||
fields: { | ||
name: { | ||
type: graphql.GraphQLString, | ||
resolve (obj, args) { | ||
return obj | ||
} | ||
} | ||
}), | ||
resolve (obj, args) { | ||
return Promise.resolve('test') | ||
} | ||
}), | ||
resolve (obj, args) { | ||
return Promise.resolve('test') | ||
} | ||
} | ||
}) | ||
} | ||
}) | ||
}) | ||
} | ||
describe('graphql', () => { | ||
beforeEach(() => { | ||
plugin = require('../../src/plugins/graphql') | ||
context = require('../../src/platform').context() | ||
sort = spans => spans.sort((a, b) => a.start.toString() > b.start.toString() ? 1 : -1) | ||
@@ -66,2 +67,6 @@ }) | ||
return agent.load(plugin, 'graphql') | ||
.then(() => { | ||
graphql = require('graphql') | ||
buildSchema() | ||
}) | ||
}) | ||
@@ -294,2 +299,22 @@ | ||
it('should handle calling low level APIs directly', done => { | ||
const source = `query MyQuery { hello(name: "world") }` | ||
const document = graphql.parse(source) | ||
agent | ||
.use(traces => { | ||
const spans = sort(traces[0]) | ||
expect(spans).to.have.length(3) | ||
expect(spans[0]).to.have.property('service', 'test-graphql') | ||
expect(spans[0]).to.have.property('name', 'graphql.query') | ||
expect(spans[0]).to.have.property('resource', 'query MyQuery') | ||
expect(spans[0].meta).to.have.property('graphql.document', source) | ||
}) | ||
.then(done) | ||
.catch(done) | ||
graphql.execute(schema, document) | ||
}) | ||
it('should handle exceptions', done => { | ||
@@ -361,2 +386,6 @@ const error = new Error('test') | ||
return agent.load(plugin, 'graphql', { service: 'test' }) | ||
.then(() => { | ||
graphql = require('graphql') | ||
buildSchema() | ||
}) | ||
}) | ||
@@ -363,0 +392,0 @@ |
@@ -18,4 +18,2 @@ 'use strict' | ||
plugin = require('../../src/plugins/http') | ||
express = require('express') | ||
http = require('http') | ||
context = require('../../src/platform').context({ experimental: {} }) | ||
@@ -32,2 +30,6 @@ }) | ||
return agent.load(plugin, 'http') | ||
.then(() => { | ||
http = require('http') | ||
express = require('express') | ||
}) | ||
}) | ||
@@ -246,2 +248,6 @@ | ||
return agent.load(plugin, 'http', config) | ||
.then(() => { | ||
http = require('http') | ||
express = require('express') | ||
}) | ||
}) | ||
@@ -248,0 +254,0 @@ |
@@ -15,34 +15,4 @@ 'use strict' | ||
function setupMongo () { | ||
return new Promise((resolve, reject) => { | ||
server = new mongo.Server({ | ||
host: 'localhost', | ||
port: 27017, | ||
reconnect: false | ||
}) | ||
server.on('connect', server => { | ||
server.command('test', { | ||
create: collection | ||
}, {}, (err, result) => { | ||
server.destroy() | ||
server = null | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve() | ||
} | ||
}) | ||
}) | ||
server.on('error', reject) | ||
server.connect() | ||
}) | ||
} | ||
describe('mongodb-core', () => { | ||
beforeEach(() => { | ||
mongo = require('mongodb-core') | ||
plugin = require('../../src/plugins/mongodb-core') | ||
@@ -53,4 +23,2 @@ platform = require('../../src/platform') | ||
collection = platform.id().toString() | ||
return setupMongo() | ||
}) | ||
@@ -67,2 +35,4 @@ | ||
.then(() => { | ||
mongo = require('mongodb-core') | ||
server = new mongo.Server({ | ||
@@ -335,2 +305,4 @@ host: 'localhost', | ||
.then(() => { | ||
mongo = require('mongodb-core') | ||
server = new mongo.Server({ | ||
@@ -337,0 +309,0 @@ host: 'localhost', |
@@ -14,3 +14,2 @@ 'use strict' | ||
beforeEach(() => { | ||
mysql = require('mysql') | ||
plugin = require('../../src/plugins/mysql') | ||
@@ -17,0 +16,0 @@ context = require('../../src/platform').context() |
@@ -15,4 +15,3 @@ 'use strict' | ||
beforeEach(() => { | ||
pg = require('pg') | ||
plugin = proxyquire('../src/plugins/pg', { 'pg': pg }) | ||
plugin = require('../../src/plugins/pg') | ||
context = require('../../src/platform').context({ experimental: { asyncHooks: false } }) | ||
@@ -29,2 +28,4 @@ }) | ||
.then(() => { | ||
pg = require('pg') | ||
client = new pg.Client({ | ||
@@ -115,2 +116,4 @@ user: 'postgres', | ||
.then(() => { | ||
pg = require('pg') | ||
client = new pg.Client({ | ||
@@ -117,0 +120,0 @@ user: 'postgres', |
@@ -15,3 +15,2 @@ 'use strict' | ||
beforeEach(() => { | ||
redis = require('redis') | ||
plugin = require('../../src/plugins/redis') | ||
@@ -30,2 +29,3 @@ context = require('../../src/platform').context() | ||
.then(() => { | ||
redis = require('redis') | ||
client = redis.createClient() | ||
@@ -160,2 +160,3 @@ }) | ||
.then(() => { | ||
redis = require('redis') | ||
client = redis.createClient() | ||
@@ -162,0 +163,0 @@ }) |
@@ -192,2 +192,6 @@ 'use strict' | ||
global.it = function (title, fn) { | ||
if (!fn) { | ||
return it.apply(this, arguments) | ||
} | ||
if (fn.length > 0) { | ||
@@ -194,0 +198,0 @@ return it.call(this, title, function (done) { |
Sorry, the diff of this file is not supported yet
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
266037
118
7440
7
+ Addedlodash.uniq@^4.5.0
+ Addedlodash.uniq@4.5.0(transitive)
- Removedrequire-dir@^1.0.0
- Removedrequire-dir@1.2.0(transitive)