Comparing version 0.1.3 to 0.1.4
@@ -1,2 +0,1 @@ | ||
// generated by genversion | ||
module.exports = '0.1.3' | ||
module.exports = '0.1.4' |
{ | ||
"name": "dd-trace", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "Datadog APM tracing client for JavaScript (experimental)", | ||
@@ -10,4 +10,3 @@ "main": "index.js", | ||
"tdd": "mocha --watch", | ||
"test": "nyc --reporter text --reporter lcov mocha", | ||
"version": "genversion lib/version.js && git add lib/version.js" | ||
"test": "nyc --reporter text --reporter lcov mocha" | ||
}, | ||
@@ -66,3 +65,2 @@ "repository": { | ||
"express": "^4.16.2", | ||
"genversion": "^2.0.1", | ||
"get-port": "^3.2.0", | ||
@@ -69,0 +67,0 @@ "mocha": "^5.0.0", |
@@ -8,14 +8,39 @@ 'use strict' | ||
// TODO: lazy load built-in plugins | ||
class Instrumenter { | ||
constructor (tracer, config) { | ||
constructor (tracer) { | ||
this._tracer = tracer | ||
this._plugins = loadPlugins(config) | ||
this._integrations = loadIntegrations() | ||
this._plugins = new Map() | ||
this._instrumented = new Map() | ||
} | ||
patch () { | ||
const instrumentedModules = this._plugins.map(plugin => plugin.name) | ||
hook(instrumentedModules, this.hookModule.bind(this)) | ||
use (name, config) { | ||
config = config || {} | ||
if (typeof name === 'string') { | ||
this._integrations | ||
.filter(plugin => plugin.name === name) | ||
.forEach(plugin => this._plugins.set(plugin, config)) | ||
} else { | ||
[].concat(name) | ||
.forEach(plugin => this._plugins.set(plugin, config)) | ||
} | ||
this.reload() | ||
} | ||
patch (config) { | ||
config = config || {} | ||
if (config.plugins !== false) { | ||
loadIntegrations().forEach(integration => { | ||
this._plugins.has(integration) || this._plugins.set(integration) | ||
}) | ||
} | ||
this.reload() | ||
} | ||
unpatch () { | ||
@@ -27,11 +52,20 @@ this._instrumented.forEach((instrumentation, moduleExports) => { | ||
reload () { | ||
const instrumentedModules = Array.from(this._plugins.keys()).map(plugin => plugin.name) | ||
hook(instrumentedModules, this.hookModule.bind(this)) | ||
} | ||
hookModule (moduleExports, moduleName, moduleBaseDir) { | ||
const moduleVersion = getVersion(moduleBaseDir) | ||
this._plugins | ||
Array.from(this._plugins.keys()) | ||
.filter(plugin => plugin.name === moduleName) | ||
.filter(plugin => matchVersion(moduleVersion, plugin.versions)) | ||
.forEach(plugin => { | ||
plugin.patch(moduleExports, this._tracer) | ||
this._instrumented.set(moduleExports, plugin) | ||
let moduleToPatch = moduleExports | ||
if (plugin.file) { | ||
moduleToPatch = require(path.join(moduleBaseDir, plugin.file)) | ||
} | ||
plugin.patch(moduleToPatch, this._tracer._tracer, this._plugins.get(plugin)) | ||
this._instrumented.set(moduleToPatch, plugin) | ||
}) | ||
@@ -43,15 +77,8 @@ | ||
function loadPlugins (config) { | ||
if (config.plugins === false) { | ||
return [] | ||
} | ||
const plugins = [] | ||
function loadIntegrations () { | ||
const integrations = requireDir('./plugins') | ||
Object.keys(integrations).forEach(key => { | ||
plugins.push(integrations[key]) | ||
}) | ||
return plugins | ||
return Object.keys(integrations) | ||
.map(key => integrations[key]) | ||
.reduce((previous, current) => previous.concat(current), []) | ||
} | ||
@@ -58,0 +85,0 @@ |
@@ -10,3 +10,3 @@ 'use strict' | ||
const parentPath = parentModule(callerPath) | ||
const pkg = readPkgUp.sync({ cwd: path.dirname(parentPath) }).pkg | ||
const pkg = readPkgUp.sync({ cwd: path.dirname(parentPath) }).pkg || {} | ||
@@ -13,0 +13,0 @@ this._service = pkg.name |
@@ -11,3 +11,3 @@ 'use strict' | ||
function patch (express, tracer) { | ||
function patch (express, tracer, config) { | ||
METHODS.forEach((method) => { | ||
@@ -39,3 +39,3 @@ shimmer.wrap(express.application, method, wrapper) | ||
span.setTag('service.name', tracer._service) | ||
span.setTag('service.name', config.service || tracer._service) | ||
span.setTag('span.type', 'web') | ||
@@ -42,0 +42,0 @@ span.setTag(Tags.HTTP_STATUS_CODE, res.statusCode) |
@@ -10,3 +10,3 @@ 'use strict' | ||
function patch (http, tracer) { | ||
function patch (http, tracer, config) { | ||
shimmer.wrap(http, 'request', request => makeRequestTrace(request)) | ||
@@ -39,3 +39,3 @@ shimmer.wrap(http, 'get', get => makeRequestTrace(get)) | ||
span.addTags({ | ||
'service.name': 'http-client', | ||
'service.name': config.service || 'http-client', | ||
'span.type': 'web', | ||
@@ -42,0 +42,0 @@ 'resource.name': options.pathname |
@@ -8,3 +8,3 @@ 'use strict' | ||
function patch (pg, tracer) { | ||
function patch (pg, tracer, config) { | ||
function queryWrap (query) { | ||
@@ -24,3 +24,3 @@ return function queryTrace () { | ||
}, span => { | ||
span.setTag('service.name', 'postgres') | ||
span.setTag('service.name', config.service || 'postgres') | ||
span.setTag('resource.name', statement) | ||
@@ -27,0 +27,0 @@ span.setTag('db.name', params.database) |
@@ -7,2 +7,3 @@ 'use strict' | ||
const Config = require('./config') | ||
const Instrumenter = require('./instrumenter') | ||
const platform = require('./platform') | ||
@@ -16,2 +17,3 @@ | ||
this._tracer = noop | ||
this._instrumenter = new Instrumenter(this) | ||
} | ||
@@ -24,2 +26,4 @@ | ||
const config = new Config(options) | ||
this._instrumenter.patch(config) | ||
this._tracer = new DatadogTracer(config) | ||
@@ -31,6 +35,19 @@ } | ||
trace () { | ||
return this._tracer.trace.apply(this._tracer, arguments) | ||
use () { | ||
this._instrumenter.use.apply(this._instrumenter, arguments) | ||
return this | ||
} | ||
trace (operationName, options, callback) { | ||
if (callback) { | ||
return this._tracer.trace.apply(this._tracer, arguments) | ||
} else { | ||
return new Promise((resolve, reject) => { | ||
this._tracer.trace(operationName, options, span => { | ||
resolve(span) | ||
}) | ||
}) | ||
} | ||
} | ||
startSpan () { | ||
@@ -37,0 +54,0 @@ return this._tracer.startSpan.apply(this._tracer, arguments) |
@@ -11,7 +11,10 @@ 'use strict' | ||
this._writer = new Writer(url, size) | ||
this._scheduler = new Scheduler(() => this._writer.flush(), interval) | ||
if (interval > 0) { | ||
this._scheduler = new Scheduler(() => this._writer.flush(), interval) | ||
} | ||
} | ||
init () { | ||
this._scheduler.start() | ||
this._scheduler && this._scheduler.start() | ||
} | ||
@@ -21,2 +24,6 @@ | ||
this._writer.append(span) | ||
if (!this._scheduler) { | ||
this._writer.flush() | ||
} | ||
} | ||
@@ -23,0 +30,0 @@ } |
@@ -5,3 +5,2 @@ 'use strict' | ||
const Tracer = require('./opentracing/tracer') | ||
const Instrumenter = require('./instrumenter') | ||
@@ -13,4 +12,2 @@ class DatadogTracer extends Tracer { | ||
this._context = platform.context(config) | ||
this._instrumenter = new Instrumenter(this, config) | ||
this._instrumenter.patch() | ||
} | ||
@@ -17,0 +14,0 @@ |
@@ -25,3 +25,3 @@ 'use strict' | ||
port: listener.address().port, | ||
flushInterval: 10, | ||
flushInterval: 0, | ||
plugins: false | ||
@@ -28,0 +28,0 @@ }) |
'use strict' | ||
const path = require('path') | ||
const proxyquire = require('proxyquire').noCallThru() | ||
@@ -11,5 +12,10 @@ | ||
let requireDir | ||
let Connection | ||
let Pool | ||
let plugins | ||
beforeEach(() => { | ||
tracer = 'tracer' | ||
tracer = { | ||
_tracer: 'tracer' | ||
} | ||
@@ -23,9 +29,41 @@ integrations = { | ||
express: { | ||
name: 'express', | ||
name: 'express-mock', | ||
versions: ['4.x'], | ||
patch: sinon.spy(), | ||
unpatch: sinon.spy() | ||
}, | ||
mysql: [ | ||
{ | ||
name: 'mysql-mock', | ||
versions: ['2.x'], | ||
file: 'lib/Connection.js', | ||
patch: sinon.spy(), | ||
unpatch: sinon.spy() | ||
}, | ||
{ | ||
name: 'mysql-mock', | ||
versions: ['2.x'], | ||
file: 'lib/Pool.js', | ||
patch: sinon.spy(), | ||
unpatch: sinon.spy() | ||
} | ||
] | ||
} | ||
plugins = { | ||
other: { | ||
name: 'other', | ||
versions: ['1.x'], | ||
patch: sinon.spy(), | ||
unpatch: 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() | ||
@@ -35,9 +73,72 @@ requireDir.withArgs('./plugins').returns(integrations) | ||
Instrumenter = proxyquire('../src/instrumenter', { | ||
'require-dir': requireDir | ||
'require-dir': requireDir, | ||
[connectionPath]: Connection, | ||
[poolPath]: Pool | ||
}) | ||
instrumenter = new Instrumenter(tracer) | ||
}) | ||
describe('when enabled', () => { | ||
beforeEach(() => { | ||
instrumenter = new Instrumenter(tracer, { plugins: true }) | ||
describe('with integrations enabled', () => { | ||
describe('use', () => { | ||
it('should allow configuring a plugin by name', () => { | ||
const config = { foo: 'bar' } | ||
instrumenter.use('express-mock', config) | ||
instrumenter.patch() | ||
const express = require('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer', config) | ||
}) | ||
it('should allow configuring a plugin by instance', () => { | ||
const config = { foo: 'bar' } | ||
instrumenter.use(integrations.express, config) | ||
instrumenter.patch() | ||
const express = require('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer', config) | ||
}) | ||
it('should default to an empty plugin configuration', () => { | ||
instrumenter.use('express-mock') | ||
instrumenter.patch() | ||
const express = require('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer', {}) | ||
}) | ||
it('should support a plugin instance', () => { | ||
const express = require('express-mock') | ||
instrumenter.use(express) | ||
instrumenter.patch() | ||
require('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer') | ||
}) | ||
it('should reapply the require hook when called multiple times', () => { | ||
instrumenter.use('mysql-mock') | ||
instrumenter.use('express-mock') | ||
instrumenter.patch() | ||
require('express-mock') | ||
expect(integrations.express.patch).to.have.been.called | ||
}) | ||
it('should support third party plugins', () => { | ||
instrumenter.use(plugins.other) | ||
instrumenter.patch() | ||
const other = require('other') | ||
expect(plugins.other.patch).to.have.been.calledWith(other, 'tracer') | ||
}) | ||
}) | ||
@@ -49,5 +150,5 @@ | ||
const express = require('express') | ||
const express = require('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, tracer) | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer') | ||
}) | ||
@@ -59,5 +160,5 @@ | ||
const express = require('express') | ||
const express = require('express-mock') | ||
expect(integrations.express.patch).to.not.have.been.calledWith(express, tracer) | ||
expect(integrations.express.patch).to.not.have.been.calledWith(express, 'tracer') | ||
}) | ||
@@ -70,4 +171,15 @@ | ||
expect(integrations.http.patch).to.have.been.calledWith(http, tracer) | ||
expect(integrations.http.patch).to.have.been.called | ||
expect(integrations.http.patch).to.have.been.calledWith(http, 'tracer') | ||
}) | ||
it('should support patching multiple files', () => { | ||
instrumenter.patch() | ||
const mysql = require('mysql-mock') | ||
expect(mysql).to.deep.equal({ foo: 'bar' }) | ||
expect(integrations.mysql[0].patch).to.have.been.calledWith(Connection, 'tracer') | ||
expect(integrations.mysql[1].patch).to.have.been.calledWith(Pool, 'tracer') | ||
}) | ||
}) | ||
@@ -79,3 +191,3 @@ | ||
const express = require('express') | ||
const express = require('express-mock') | ||
@@ -89,14 +201,31 @@ instrumenter.unpatch() | ||
describe('when disabled', () => { | ||
beforeEach(() => { | ||
instrumenter = new Instrumenter(tracer, { plugins: false }) | ||
describe('with integrations disabled', () => { | ||
describe('use', () => { | ||
it('should still allow adding plugins manually by name', () => { | ||
instrumenter.use('express-mock') | ||
instrumenter.patch({ plugins: false }) | ||
const express = require('express-mock') | ||
expect(integrations.express.patch).to.have.been.calledWith(express, 'tracer') | ||
}) | ||
}) | ||
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', () => { | ||
it('should not patch any module', () => { | ||
instrumenter.patch() | ||
instrumenter.patch({ plugins: false }) | ||
const express = require('express') | ||
const express = require('express-mock') | ||
expect(integrations.express.patch).to.not.have.been.calledWith(express, tracer) | ||
expect(integrations.express.patch).to.not.have.been.calledWith(express, 'tracer') | ||
}) | ||
@@ -103,0 +232,0 @@ }) |
'use strict' | ||
const apiCompatibilityChecks = require('opentracing/lib/test/api_compatibility').default | ||
const DatadogTracer = require('../../src/opentracing/tracer') | ||
const tracer = require('../..') | ||
apiCompatibilityChecks(() => { | ||
const clock = sinon.useFakeTimers() | ||
const tracer = new DatadogTracer({ service: 'test' }) | ||
clock.restore() | ||
return tracer | ||
return tracer.init({ | ||
service: 'test', | ||
flushInterval: 0, | ||
plugins: false | ||
}) | ||
}) |
@@ -123,5 +123,15 @@ 'use strict' | ||
let service | ||
let load | ||
let readPkgUp | ||
beforeEach(() => { | ||
readPkgUp = { | ||
sync: sinon.stub() | ||
} | ||
platform = require('../../../src/platform/node') | ||
load = proxyquire('../src/platform/node/load', { | ||
'read-pkg-up': readPkgUp | ||
}) | ||
service = platform._service | ||
@@ -140,2 +150,10 @@ }) | ||
it('should not load the service name if the module information is unavailable', () => { | ||
readPkgUp.sync.returns({ pkg: undefined }) | ||
load.call(platform) | ||
expect(platform._service).to.be.undefined | ||
}) | ||
it('should work even in subfolders', () => { | ||
@@ -306,3 +324,2 @@ require('./load/indirect') | ||
let namespace | ||
let cls | ||
let clsBluebird | ||
@@ -312,4 +329,4 @@ let config | ||
beforeEach(() => { | ||
clsBluebird = sinon.spy(require('cls-bluebird')) | ||
require.cache[require.resolve('cls-bluebird')].exports = clsBluebird | ||
require('cls-bluebird') | ||
clsBluebird = sinon.spy(require.cache[require.resolve('cls-bluebird')], 'exports') | ||
context = require('../../../src/platform/node/context') | ||
@@ -319,3 +336,3 @@ }) | ||
afterEach(() => { | ||
cls.destroyNamespace('dd-trace') | ||
require.cache[require.resolve('cls-bluebird')].exports.restore() | ||
}) | ||
@@ -325,3 +342,2 @@ | ||
beforeEach(() => { | ||
cls = require('continuation-local-storage') | ||
config = { experimental: { asyncHooks: false } } | ||
@@ -337,3 +353,2 @@ namespace = context(config) | ||
beforeEach(() => { | ||
cls = require('cls-hooked') | ||
config = { experimental: { asyncHooks: true } } | ||
@@ -348,6 +363,2 @@ namespace = context(config) | ||
function testContext (modulePath) { | ||
afterEach(() => { | ||
delete require.cache[require.resolve(modulePath)] | ||
}) | ||
it('should use the correct implementation from the experimental flag', () => { | ||
@@ -381,7 +392,7 @@ expect(namespace).to.equal(require(modulePath)) | ||
setImmediate(() => test()) | ||
namespace.run(() => { | ||
namespace.set('foo', childValue) | ||
}) | ||
test() | ||
}) | ||
@@ -388,0 +399,0 @@ |
@@ -13,5 +13,6 @@ 'use strict' | ||
let tracer = null | ||
let handlers = [] | ||
module.exports = { | ||
load (plugin, moduleToPatch) { | ||
load (plugin, moduleToPatch, config) { | ||
tracer = require('../..') | ||
@@ -25,2 +26,11 @@ agent = express() | ||
agent.put('/v0.3/traces', (req, res) => { | ||
res.status(200).send('OK') | ||
if (handlers[0]) { | ||
handlers[0](req.body) | ||
handlers.shift() | ||
} | ||
}) | ||
return getPort().then(port => { | ||
@@ -32,3 +42,6 @@ return new Promise((resolve, reject) => { | ||
server.on('close', () => plugin.unpatch(moduleToPatch)) | ||
server.on('close', () => { | ||
tracer._instrumenter.unpatch() | ||
tracer = null | ||
}) | ||
@@ -38,7 +51,9 @@ tracer.init({ | ||
port, | ||
flushInterval: 10, | ||
flushInterval: 0, | ||
plugins: false | ||
}) | ||
plugin.patch(moduleToPatch, tracer._tracer) | ||
tracer.use(plugin, config) | ||
require(moduleToPatch) | ||
}) | ||
@@ -49,5 +64,11 @@ }) | ||
use (callback) { | ||
agent.put('/v0.3/traces', (req, res) => { | ||
res.status(200).send('OK') | ||
callback(req.body) | ||
return new Promise((resolve, reject) => { | ||
handlers.push(function () { | ||
try { | ||
callback.apply(null, arguments) | ||
resolve() | ||
} catch (e) { | ||
reject(e) | ||
} | ||
}) | ||
}) | ||
@@ -64,5 +85,5 @@ }, | ||
agent = null | ||
tracer = null | ||
handlers = [] | ||
delete require.cache[require.resolve('../..')] | ||
} | ||
} |
@@ -18,4 +18,2 @@ 'use strict' | ||
context = require('../../src/platform').context({ experimental: { asyncHooks: false } }) | ||
return agent.load(plugin, express) | ||
}) | ||
@@ -28,74 +26,110 @@ | ||
it('should do automatic instrumentation', done => { | ||
const app = express() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
describe('without configuration', () => { | ||
beforeEach(() => { | ||
return agent.load(plugin, 'express') | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'test') | ||
expect(traces[0][0]).to.have.property('type', 'web') | ||
expect(traces[0][0]).to.have.property('resource', '/user') | ||
expect(traces[0][0].meta).to.have.property('span.kind', 'server') | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/user`) | ||
expect(traces[0][0].meta).to.have.property('http.method', 'GET') | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
it('should do automatic instrumentation', done => { | ||
const app = express() | ||
done() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios | ||
.get(`http://localhost:${port}/user`) | ||
.catch(done) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'test') | ||
expect(traces[0][0]).to.have.property('type', 'web') | ||
expect(traces[0][0]).to.have.property('resource', '/user') | ||
expect(traces[0][0].meta).to.have.property('span.kind', 'server') | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/user`) | ||
expect(traces[0][0].meta).to.have.property('http.method', 'GET') | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
done() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios | ||
.get(`http://localhost:${port}/user`) | ||
.catch(done) | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('should support custom routers', done => { | ||
const app = express() | ||
it('should support custom routers', done => { | ||
const app = express() | ||
app.use((req, res) => { | ||
res.status(200).send() | ||
app.use((req, res) => { | ||
res.status(200).send() | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('resource', 'express.request') | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
done() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios | ||
.get(`http://localhost:${port}`) | ||
.catch(done) | ||
}) | ||
}) | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('resource', 'express.request') | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
it('should support context propagation', done => { | ||
const app = express() | ||
done() | ||
app.use((req, res, next) => { | ||
context.set('foo', 'bar') | ||
next() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios | ||
.get(`http://localhost:${port}`) | ||
.catch(done) | ||
app.get('/user', (req, res) => { | ||
res.status(200).send(context.get('foo')) | ||
}) | ||
getPort().then(port => { | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios.get(`http://localhost:${port}/user`) | ||
.then(res => { | ||
expect(res.status).to.equal(200) | ||
expect(res.data).to.equal('bar') | ||
done() | ||
}) | ||
.catch(done) | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('should support context propagation', done => { | ||
const app = express() | ||
it('should extract its parent span from the headers', done => { | ||
const app = express() | ||
app.use((req, res, next) => { | ||
context.set('foo', 'bar') | ||
next() | ||
}) | ||
app.get('/user', (req, res) => { | ||
expect(agent.currentSpan().context().baggageItems).to.have.property('foo', 'bar') | ||
res.status(200).send() | ||
}) | ||
app.get('/user', (req, res) => { | ||
res.status(200).send(context.get('foo')) | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].trace_id.toString()).to.equal('1234') | ||
expect(traces[0][0].parent_id.toString()).to.equal('5678') | ||
getPort().then(port => { | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios.get(`http://localhost:${port}/user`) | ||
.then(res => { | ||
expect(res.status).to.equal(200) | ||
expect(res.data).to.equal('bar') | ||
done() | ||
}) | ||
.catch(done) | ||
done() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios | ||
.get(`http://localhost:${port}/user`, { | ||
headers: { | ||
'x-datadog-trace-id': '1234', | ||
'x-datadog-parent-id': '5678', | ||
'ot-baggage-foo': 'bar' | ||
} | ||
}) | ||
.catch(done) | ||
}) | ||
}) | ||
@@ -105,28 +139,32 @@ }) | ||
it('should extract its parent span from the headers', done => { | ||
const app = express() | ||
describe('with configuration', () => { | ||
let config | ||
app.get('/user', (req, res) => { | ||
expect(agent.currentSpan().context().baggageItems).to.have.property('foo', 'bar') | ||
res.status(200).send() | ||
beforeEach(() => { | ||
config = { | ||
service: 'custom' | ||
} | ||
return agent.load(plugin, 'express', config) | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].trace_id.toString()).to.equal('1234') | ||
expect(traces[0][0].parent_id.toString()).to.equal('5678') | ||
it('should be configured with the correct values', done => { | ||
const app = express() | ||
done() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios | ||
.get(`http://localhost:${port}/user`, { | ||
headers: { | ||
'x-datadog-trace-id': '1234', | ||
'x-datadog-parent-id': '5678', | ||
'ot-baggage-foo': 'bar' | ||
} | ||
}) | ||
.catch(done) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'custom') | ||
done() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
axios | ||
.get(`http://localhost:${port}/user`) | ||
.catch(done) | ||
}) | ||
}) | ||
@@ -133,0 +171,0 @@ }) |
@@ -17,4 +17,2 @@ 'use strict' | ||
http = require('http') | ||
return agent.load(plugin, http) | ||
}) | ||
@@ -27,107 +25,154 @@ | ||
it('should do automatic instrumentation', done => { | ||
const app = express() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
describe('without configuration', () => { | ||
beforeEach(() => { | ||
return agent.load(plugin, 'http') | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'http-client') | ||
expect(traces[0][0]).to.have.property('type', 'web') | ||
expect(traces[0][0]).to.have.property('resource', '/user') | ||
expect(traces[0][0].meta).to.have.property('span.kind', 'client') | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/user`) | ||
expect(traces[0][0].meta).to.have.property('http.method', 'GET') | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
it('should do automatic instrumentation', done => { | ||
const app = express() | ||
done() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.request(`http://localhost:${port}/user`, res => { | ||
res.on('data', () => {}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'http-client') | ||
expect(traces[0][0]).to.have.property('type', 'web') | ||
expect(traces[0][0]).to.have.property('resource', '/user') | ||
expect(traces[0][0].meta).to.have.property('span.kind', 'client') | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/user`) | ||
expect(traces[0][0].meta).to.have.property('http.method', 'GET') | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
done() | ||
}) | ||
req.end() | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.request(`http://localhost:${port}/user`, res => { | ||
res.on('data', () => {}) | ||
}) | ||
req.end() | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('should also support http.get', done => { | ||
const app = express() | ||
it('should also support http.get', done => { | ||
const app = express() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.not.be.undefined | ||
done() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.get(`http://localhost:${port}/user`, res => { | ||
res.on('data', () => {}) | ||
}) | ||
req.end() | ||
}) | ||
}) | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.not.be.undefined | ||
it('should support configuration as an URL object', done => { | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/user`) | ||
done() | ||
done() | ||
}) | ||
const uri = { | ||
hostname: 'localhost', | ||
port, | ||
pathname: '/user' | ||
} | ||
const req = http.request(uri) | ||
req.end() | ||
}) | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.get(`http://localhost:${port}/user`, res => { | ||
res.on('data', () => {}) | ||
it('should use the correct defaults when not specified', done => { | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/`) | ||
done() | ||
}) | ||
const req = http.request({ | ||
port | ||
}) | ||
req.end() | ||
}) | ||
}) | ||
}) | ||
it('should support configuration as an URL object', done => { | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/user`) | ||
it('should not require consuming the data', done => { | ||
const app = express() | ||
done() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
}) | ||
const uri = { | ||
hostname: 'localhost', | ||
port, | ||
pathname: '/user' | ||
} | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.not.be.undefined | ||
const req = http.request(uri) | ||
done() | ||
}) | ||
req.end() | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.request(`http://localhost:${port}/user`) | ||
req.end() | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('should use the correct defaults when not specified', done => { | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/`) | ||
it('should inject its parent span in the headers', done => { | ||
const app = express() | ||
done() | ||
}) | ||
app.get('/user', (req, res) => { | ||
expect(req.get('x-datadog-trace-id')).to.be.a('string') | ||
expect(req.get('x-datadog-parent-id')).to.be.a('string') | ||
const req = http.request({ | ||
port | ||
res.status(200).send() | ||
}) | ||
req.end() | ||
}) | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
it('should not require consuming the data', done => { | ||
const app = express() | ||
done() | ||
}) | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.request(`http://localhost:${port}/user`) | ||
req.end() | ||
}) | ||
}) | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.not.be.undefined | ||
it('should handle errors', done => { | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('error.type', 'Error') | ||
expect(traces[0][0].meta).to.have.property('error.msg', `connect ECONNREFUSED 127.0.0.1:${port}`) | ||
expect(traces[0][0].meta).to.have.property('error.stack') | ||
done() | ||
}) | ||
done() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.request(`http://localhost:${port}/user`) | ||
@@ -140,40 +185,35 @@ | ||
it('should inject its parent span in the headers', done => { | ||
const app = express() | ||
describe('with configuration', () => { | ||
let config | ||
app.get('/user', (req, res) => { | ||
expect(req.get('x-datadog-trace-id')).to.be.a('string') | ||
expect(req.get('x-datadog-parent-id')).to.be.a('string') | ||
beforeEach(() => { | ||
config = { | ||
service: 'custom' | ||
} | ||
res.status(200).send() | ||
return agent.load(plugin, 'http', config) | ||
}) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('http.status_code', '200') | ||
it('should be configured with the correct values', done => { | ||
const app = express() | ||
done() | ||
app.get('/user', (req, res) => { | ||
res.status(200).send() | ||
}) | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.request(`http://localhost:${port}/user`) | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'custom') | ||
req.end() | ||
}) | ||
}) | ||
}) | ||
done() | ||
}) | ||
it('should handle errors', done => { | ||
getPort().then(port => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('error.type', 'Error') | ||
expect(traces[0][0].meta).to.have.property('error.msg', `connect ECONNREFUSED 127.0.0.1:${port}`) | ||
expect(traces[0][0].meta).to.have.property('error.stack') | ||
appListener = app.listen(port, 'localhost', () => { | ||
const req = http.request(`http://localhost:${port}/user`, res => { | ||
res.on('data', () => {}) | ||
}) | ||
done() | ||
req.end() | ||
}) | ||
}) | ||
const req = http.request(`http://localhost:${port}/user`) | ||
req.end() | ||
}) | ||
@@ -180,0 +220,0 @@ }) |
@@ -13,4 +13,2 @@ 'use strict' | ||
plugin = proxyquire('../src/plugins/pg', { 'pg': pg }) | ||
return agent.load(plugin, pg) | ||
}) | ||
@@ -22,56 +20,83 @@ | ||
it('should do automatic instrumentation when using callbacks', done => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'postgres') | ||
expect(traces[0][0]).to.have.property('resource', 'SELECT $1::text as message') | ||
expect(traces[0][0]).to.have.property('type', 'db') | ||
expect(traces[0][0].meta).to.have.property('db.name', 'postgres') | ||
expect(traces[0][0].meta).to.have.property('db.user', 'postgres') | ||
expect(traces[0][0].meta).to.have.property('db.type', 'postgres') | ||
expect(traces[0][0].meta).to.have.property('span.kind', 'client') | ||
done() | ||
describe('without configuration', () => { | ||
beforeEach(() => { | ||
return agent.load(plugin, 'pg') | ||
}) | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres', | ||
application_name: 'test' | ||
}) | ||
it('should do automatic instrumentation when using callbacks', done => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'postgres') | ||
expect(traces[0][0]).to.have.property('resource', 'SELECT $1::text as message') | ||
expect(traces[0][0]).to.have.property('type', 'db') | ||
expect(traces[0][0].meta).to.have.property('db.name', 'postgres') | ||
expect(traces[0][0].meta).to.have.property('db.user', 'postgres') | ||
expect(traces[0][0].meta).to.have.property('db.type', 'postgres') | ||
expect(traces[0][0].meta).to.have.property('span.kind', 'client') | ||
client.connect((err) => { | ||
if (err) throw err | ||
done() | ||
}) | ||
client.query('SELECT $1::text as message', ['Hello world!'], (err, result) => { | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres', | ||
application_name: 'test' | ||
}) | ||
client.connect((err) => { | ||
if (err) throw err | ||
client.end((err) => { | ||
client.query('SELECT $1::text as message', ['Hello world!'], (err, result) => { | ||
if (err) throw err | ||
client.end((err) => { | ||
if (err) throw err | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('should handle errors', done => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('error.type', 'error') | ||
expect(traces[0][0].meta).to.have.property('error.msg', 'syntax error at or near "INVALID"') | ||
expect(traces[0][0].meta).to.have.property('error.stack') | ||
it('should handle errors', done => { | ||
agent.use(traces => { | ||
expect(traces[0][0].meta).to.have.property('error.type', 'error') | ||
expect(traces[0][0].meta).to.have.property('error.msg', 'syntax error at or near "INVALID"') | ||
expect(traces[0][0].meta).to.have.property('error.stack') | ||
done() | ||
}) | ||
done() | ||
}) | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres' | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres' | ||
}) | ||
client.connect((err) => { | ||
if (err) throw err | ||
client.query('INVALID', (err, result) => { | ||
expect(err).to.be.an('error') | ||
client.end((err) => { | ||
if (err) throw err | ||
}) | ||
}) | ||
}) | ||
}) | ||
client.connect((err) => { | ||
if (err) throw err | ||
it('should work without a callback', done => { | ||
agent.use(traces => { | ||
done() | ||
}) | ||
client.query('INVALID', (err, result) => { | ||
expect(err).to.be.an('error') | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres' | ||
}) | ||
client.connect((err) => { | ||
if (err) throw err | ||
client.query('SELECT $1::text as message', ['Hello World!']) | ||
client.end((err) => { | ||
@@ -84,19 +109,36 @@ if (err) throw err | ||
it('should work without a callback', done => { | ||
agent.use(traces => { | ||
done() | ||
}) | ||
describe('with configuration', () => { | ||
let config | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres' | ||
beforeEach(() => { | ||
config = { | ||
service: 'custom' | ||
} | ||
return agent.load(plugin, 'pg', config) | ||
}) | ||
client.connect((err) => { | ||
if (err) throw err | ||
it('should be configured with the correct values', done => { | ||
agent.use(traces => { | ||
expect(traces[0][0]).to.have.property('service', 'custom') | ||
client.query('SELECT $1::text as message', ['Hello World!']) | ||
client.end((err) => { | ||
done() | ||
}) | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres' | ||
}) | ||
client.connect((err) => { | ||
if (err) throw err | ||
client.query('SELECT $1::text as message', ['Hello world!'], (err, result) => { | ||
if (err) throw err | ||
client.end((err) => { | ||
if (err) throw err | ||
}) | ||
}) | ||
}) | ||
@@ -103,0 +145,0 @@ }) |
@@ -10,2 +10,4 @@ 'use strict' | ||
let noop | ||
let Instrumenter | ||
let instrumenter | ||
let Config | ||
@@ -17,2 +19,3 @@ let config | ||
tracer = { | ||
use: sinon.stub().returns('tracer'), | ||
trace: sinon.stub().returns('span'), | ||
@@ -28,2 +31,3 @@ startSpan: sinon.stub().returns('span'), | ||
noop = { | ||
use: sinon.stub().returns('tracer'), | ||
trace: sinon.stub().returns('span'), | ||
@@ -38,4 +42,10 @@ startSpan: sinon.stub().returns('span'), | ||
instrumenter = { | ||
patch: sinon.spy(), | ||
use: sinon.spy() | ||
} | ||
DatadogTracer = sinon.stub().returns(tracer) | ||
NoopTracer = sinon.stub().returns(noop) | ||
Instrumenter = sinon.stub().returns(instrumenter) | ||
@@ -52,2 +62,3 @@ config = {} | ||
'./noop': NoopTracer, | ||
'./instrumenter': Instrumenter, | ||
'./config': Config, | ||
@@ -60,2 +71,11 @@ './platform': platform | ||
describe('use', () => { | ||
it('should call the underlying instrumenter', () => { | ||
const returnValue = proxy.use('a', 'b', 'c') | ||
expect(instrumenter.use).to.have.been.calledWith('a', 'b', 'c') | ||
expect(returnValue).to.equal(proxy) | ||
}) | ||
}) | ||
describe('uninitialized', () => { | ||
@@ -99,2 +119,14 @@ describe('init', () => { | ||
}) | ||
it('should return a promise if a callback is not provided', () => { | ||
const promise = proxy.trace('a', 'b') | ||
expect(noop.trace).to.have.been.calledWith('a', 'b') | ||
noop.trace.firstCall.args[2]('span') | ||
return promise.then(span => { | ||
expect(span).to.equal('span') | ||
}) | ||
}) | ||
}) | ||
@@ -161,2 +193,16 @@ | ||
// it('should setup automatic instrumentation', () => { | ||
// expect(Instrumenter).to.have.been.calledWith(tracer) | ||
// expect(instrumenter.patch).to.have.been.called | ||
// }) | ||
describe('use', () => { | ||
it('should call the underlying Instrumenter', () => { | ||
const returnValue = proxy.use('a', 'b', 'c') | ||
expect(instrumenter.use).to.have.been.calledWith('a', 'b', 'c') | ||
expect(returnValue).to.equal(proxy) | ||
}) | ||
}) | ||
describe('trace', () => { | ||
@@ -169,2 +215,14 @@ it('should call the underlying DatadogTracer', () => { | ||
}) | ||
it('should return a promise if a callback is not provided', () => { | ||
const promise = proxy.trace('a', 'b') | ||
expect(tracer.trace).to.have.been.calledWith('a', 'b') | ||
tracer.trace.firstCall.args[2]('span') | ||
return promise.then(span => { | ||
expect(span).to.equal('span') | ||
}) | ||
}) | ||
}) | ||
@@ -171,0 +229,0 @@ |
@@ -10,4 +10,6 @@ 'use strict' | ||
let recorder | ||
let span | ||
beforeEach(() => { | ||
span = {} | ||
scheduler = { | ||
@@ -27,31 +29,56 @@ start: sinon.spy(), | ||
}) | ||
recorder = new Recorder('http://test', 1000, 2) | ||
}) | ||
describe('init', () => { | ||
it('should schedule flushing after the configured interval', () => { | ||
writer.length = 0 | ||
describe('when interval is set to a positive number', () => { | ||
beforeEach(() => { | ||
recorder = new Recorder('http://test', 1000, 2) | ||
}) | ||
recorder.init() | ||
Scheduler.firstCall.args[0]() | ||
describe('init', () => { | ||
it('should schedule flushing after the configured interval', () => { | ||
writer.length = 0 | ||
expect(scheduler.start).to.have.been.called | ||
expect(writer.flush).to.have.been.called | ||
recorder.init() | ||
Scheduler.firstCall.args[0]() | ||
expect(scheduler.start).to.have.been.called | ||
expect(writer.flush).to.have.been.called | ||
}) | ||
}) | ||
describe('record', () => { | ||
beforeEach(() => { | ||
span = {} | ||
}) | ||
it('should record a span', () => { | ||
writer.length = 0 | ||
recorder.record(span) | ||
expect(writer.append).to.have.been.calledWith(span) | ||
}) | ||
}) | ||
}) | ||
describe('record', () => { | ||
let span | ||
describe('when interval is set to 0', () => { | ||
beforeEach(() => { | ||
span = {} | ||
recorder = new Recorder('http://test', 0) | ||
}) | ||
it('should record a span', () => { | ||
writer.length = 0 | ||
describe('init', () => { | ||
it('should not schedule flushing', () => { | ||
writer.length = 0 | ||
recorder.init() | ||
expect(scheduler.start).to.not.have.been.called | ||
}) | ||
}) | ||
it('should flush right away when interval is set to 0', () => { | ||
recorder.record(span) | ||
expect(writer.append).to.have.been.calledWith(span) | ||
expect(writer.flush).to.have.been.called | ||
}) | ||
}) | ||
}) |
@@ -23,2 +23,3 @@ 'use strict' | ||
instrumenter = { | ||
use: sinon.spy(), | ||
patch: sinon.spy() | ||
@@ -38,9 +39,2 @@ } | ||
it('should setup automatic instrumentation', () => { | ||
tracer = new Tracer(config) | ||
expect(Instrumenter).to.have.been.calledWith(tracer) | ||
expect(instrumenter.patch).to.have.been.called | ||
}) | ||
describe('trace', () => { | ||
@@ -47,0 +41,0 @@ it('should run the callback with the new span', done => { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
131004
21
92
3816
9
6