Comparing version 0.1.1 to 0.1.2
{ | ||
"name": "dd-trace", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "Datadog APM tracing client for JavaScript (experimental)", | ||
@@ -8,5 +8,6 @@ "main": "index.js", | ||
"bench": "node benchmark", | ||
"lint": "eslint .", | ||
"lint": "eslint . && node scripts/check_licenses.js", | ||
"tdd": "mocha --watch", | ||
"test": "nyc --reporter text --reporter lcov mocha" | ||
"test": "nyc --reporter text --reporter lcov mocha", | ||
"version": "genversion lib/version.js && git add lib/version.js" | ||
}, | ||
@@ -24,3 +25,3 @@ "repository": { | ||
], | ||
"author": "Datadog, Inc.", | ||
"author": "Datadog Inc. <info@datadoghq.com>", | ||
"license": "BSD-3-Clause", | ||
@@ -40,10 +41,17 @@ "bugs": { | ||
"koalas": "^1.0.2", | ||
"methods": "^1.1.2", | ||
"msgpack-lite": "^0.1.26", | ||
"opentracing": "^0.14.1", | ||
"opentracing": "0.14.1", | ||
"parent-module": "^0.1.0", | ||
"performance-now": "^2.1.0", | ||
"read-pkg-up": "^3.0.0", | ||
"require-dir": "^1.0.0", | ||
"require-in-the-middle": "^2.2.1", | ||
"safe-buffer": "^5.1.1", | ||
"semver": "^5.5.0", | ||
"shimmer": "^1.2.0", | ||
"url-parse": "^1.2.0" | ||
}, | ||
"devDependencies": { | ||
"axios": "^0.18.0", | ||
"benchmark": "^2.1.4", | ||
@@ -60,2 +68,3 @@ "bluebird": "^3.5.1", | ||
"express": "^4.16.2", | ||
"genversion": "^2.0.1", | ||
"get-port": "^3.2.0", | ||
@@ -65,4 +74,5 @@ "mocha": "^5.0.0", | ||
"nyc": "^11.4.1", | ||
"pg": "^6.4.2", | ||
"proxyquire": "^1.8.0", | ||
"semver": "^5.5.0", | ||
"retry": "^0.10.1", | ||
"sinon": "^4.2.1", | ||
@@ -69,0 +79,0 @@ "sinon-chai": "^2.14.0" |
# dd-trace-js | ||
Experimental JavaScript tracer (APM) | ||
[![npm](https://img.shields.io/npm/v/dd-trace.svg)](https://www.npmjs.com/package/dd-trace) | ||
[![CircleCI](https://img.shields.io/circleci/project/github/DataDog/dd-trace-js.svg)](https://circleci.com/gh/DataDog/dd-trace-js/tree/master) | ||
**Experimental JavaScript Tracer!** | ||
This project is **experimental** and under active development. Use it at your own risk. | ||
## Installation | ||
@@ -16,31 +22,9 @@ | ||
### Example | ||
Simply require and initialize the tracer and all supported | ||
[libraries](#automatic-instrumentation) will automatically | ||
be instrumented. | ||
```js | ||
const tracer = require('dd-trace').init({ | ||
service: 'example' | ||
}) | ||
const express = require('express') | ||
const app = express() | ||
app.get('/hello/:name', (req, res) => { | ||
const options = { | ||
resource: '/hello/:name', | ||
type: 'web', | ||
tags: { | ||
'span.kind': 'server', | ||
'http.method': 'GET', | ||
'http.url': req.url, | ||
'http.status_code': '200' | ||
} | ||
} | ||
tracer.trace('say_hello', options, span => { | ||
res.send(`Hello, ${req.params.name}!`) | ||
span.finish() | ||
}) | ||
}) | ||
app.listen(3000) | ||
// The tracer must be initialized before other libraries | ||
const tracer = require('dd-trace').init() | ||
``` | ||
@@ -60,4 +44,13 @@ | ||
| flushInterval | | 2000 | Interval in milliseconds at which the tracer will submit traces to the agent. | | ||
| experimental | | {} | Experimental features can be enabled all at once using boolean `true` or individually using key/value pairs. Available experimental features: `asyncHooks`. | ||
| experimental | | {} | Experimental features can be enabled all at once using boolean `true` or individually using key/value pairs. Available experimental features: `asyncHooks`. | | ||
| plugins | | true | Whether or not to enable automatic instrumentation of external libraries using the built-in plugins. | | ||
### Automatic Instrumentation | ||
The following libraries are instrumented automatically by default: | ||
* [http](https://nodejs.org/api/http.html) | ||
* [express](https://expressjs.com/) (version 4) | ||
* [pg](https://node-postgres.com/) (version 6) | ||
### OpenTracing | ||
@@ -80,4 +73,43 @@ | ||
**NOTE: When using OpenTracing, context propagation is not handled | ||
automatically.** | ||
## Advanced Usage | ||
In some cases you may want to do manual instrumentation. For example | ||
if there is no built-in plugin covering a library you are using or if you want more control on how instrumentation is done. | ||
### Manual instrumentation | ||
```js | ||
const tracer = require('dd-trace').init() | ||
const http = require('http') | ||
const server = http.createServer((req, res) => { | ||
const options = { | ||
resource: '/hello/:name', | ||
type: 'web', | ||
tags: { | ||
'span.kind': 'server', | ||
'http.method': 'GET', | ||
'http.url': req.url, | ||
'http.status_code': '200' | ||
} | ||
} | ||
tracer.trace('say_hello', options, span => { | ||
res.write('Hello, World!') | ||
span.finish() | ||
}) | ||
res.end() | ||
}) | ||
server.listen(8000) | ||
``` | ||
## Development | ||
Before contributing to this open source project, read our [CONTRIBUTING.md](https://github.com/DataDog/dd-trace-js/blob/master/CONTRIBUTING.md). | ||
### Requirements | ||
@@ -96,2 +128,10 @@ | ||
Before running the tests, the data stores need to be running. | ||
The easiest way to start all of them is to use the provided | ||
docker-compose configuration: | ||
```sh | ||
$ docker-compose up -d | ||
``` | ||
To run the unit tests, use: | ||
@@ -98,0 +138,0 @@ |
@@ -19,3 +19,3 @@ 'use strict' | ||
this.debug = String(debug) === 'true' | ||
this.service = coalesce(options.service, platform.env('DD_SERVICE_NAME')) | ||
this.service = coalesce(options.service, platform.env('DD_SERVICE_NAME'), platform.service()) | ||
this.env = coalesce(options.env, platform.env('DD_ENV')) | ||
@@ -28,2 +28,3 @@ this.url = new URL(`${protocol}://${hostname}:${port}`) | ||
this.logger = options.logger | ||
this.plugins = coalesce(options.plugins, true) | ||
this.experimental = { | ||
@@ -30,0 +31,0 @@ asyncHooks: isFlagEnabled(options.experimental, 'asyncHooks') |
@@ -14,4 +14,4 @@ 'use strict' | ||
extractError(formatted, span._error) | ||
extractTags(formatted, span._tags) | ||
extractError(formatted, span._error) | ||
@@ -46,2 +46,8 @@ return formatted | ||
break | ||
case 'error.type': | ||
case 'error.msg': | ||
case 'error.stack': | ||
trace.error = 1 | ||
trace.meta[tag] = tags[tag] | ||
break | ||
default: | ||
@@ -55,3 +61,2 @@ trace.meta[tag] = tags[tag] | ||
if (error) { | ||
trace.error = 1 | ||
trace.meta['error.msg'] = error.message | ||
@@ -58,0 +63,0 @@ trace.meta['error.type'] = error.name |
'use strict' | ||
const requireDir = require('require-dir') | ||
const path = require('path') | ||
const semver = require('semver') | ||
const hook = require('require-in-the-middle') | ||
class Instrumenter { | ||
constructor (config) { | ||
constructor (tracer, config) { | ||
this._tracer = tracer | ||
this._plugins = loadPlugins(config) | ||
this._instrumented = new Map() | ||
} | ||
patch (tracer) { | ||
this._plugins.forEach(plugin => { | ||
plugin.patch(require(plugin.name), tracer) | ||
}) | ||
patch () { | ||
const instrumentedModules = this._plugins.map(plugin => plugin.name) | ||
hook(instrumentedModules, this.hookModule.bind(this)) | ||
} | ||
unpatch (tracer) { | ||
this._plugins.forEach(plugin => { | ||
plugin.unpatch(require(plugin.name), tracer) | ||
unpatch () { | ||
this._instrumented.forEach((instrumentation, moduleExports) => { | ||
instrumentation.unpatch(moduleExports) | ||
}) | ||
} | ||
hookModule (moduleExports, moduleName, moduleBaseDir) { | ||
const moduleVersion = getVersion(moduleBaseDir) | ||
this._plugins | ||
.filter(plugin => plugin.name === moduleName) | ||
.filter(plugin => matchVersion(moduleVersion, plugin.versions)) | ||
.forEach(plugin => { | ||
plugin.patch(moduleExports, this._tracer) | ||
this._instrumented.set(moduleExports, plugin) | ||
}) | ||
return moduleExports | ||
} | ||
} | ||
@@ -38,2 +56,13 @@ | ||
function matchVersion (version, ranges) { | ||
return !version || (ranges && ranges.some(range => semver.satisfies(version, range))) | ||
} | ||
function getVersion (moduleBaseDir) { | ||
if (moduleBaseDir) { | ||
const packageJSON = path.join(moduleBaseDir, 'package.json') | ||
return require(packageJSON).version | ||
} | ||
} | ||
module.exports = Instrumenter |
@@ -20,2 +20,3 @@ 'use strict' | ||
this._service = config.service | ||
this._url = config.url | ||
this._recorder = new Recorder(config.url, config.flushInterval, config.bufferSize) | ||
@@ -22,0 +23,0 @@ this._recorder.init() |
'use strict' | ||
const cls = require('cls-hooked') | ||
const clsBluebird = require('./cls_bluebird') | ||
const namespace = cls.createNamespace('dd-trace') | ||
clsBluebird(namespace) | ||
module.exports = namespace |
'use strict' | ||
const cls = require('continuation-local-storage') | ||
const clsBluebird = require('./cls_bluebird') | ||
const namespace = cls.createNamespace('dd-trace') | ||
clsBluebird(namespace) | ||
module.exports = namespace |
'use strict' | ||
const clsBluebird = require('cls-bluebird') | ||
module.exports = config => { | ||
@@ -14,5 +12,3 @@ let namespace | ||
clsBluebird(namespace) | ||
return namespace | ||
} |
@@ -6,2 +6,4 @@ 'use strict' | ||
const env = require('./env') | ||
const load = require('./load') | ||
const service = require('./service') | ||
const request = require('./request') | ||
@@ -12,5 +14,10 @@ const context = require('./context') | ||
module.exports = { | ||
name: () => 'nodejs', | ||
version: () => process.version, | ||
engine: () => process.jsEngine || 'v8', | ||
id, | ||
now, | ||
env, | ||
load, | ||
service, | ||
request, | ||
@@ -17,0 +24,0 @@ context, |
@@ -7,11 +7,18 @@ 'use strict' | ||
const Config = require('./config') | ||
const platform = require('./platform') | ||
const noop = new NoopTracer() | ||
let tracer = noop | ||
class TracerProxy extends Tracer { | ||
constructor () { | ||
super() | ||
this._tracer = noop | ||
} | ||
init (options) { | ||
if (tracer === noop) { | ||
if (this._tracer === noop) { | ||
platform.load() | ||
const config = new Config(options) | ||
tracer = new DatadogTracer(config) | ||
this._tracer = new DatadogTracer(config) | ||
} | ||
@@ -23,27 +30,27 @@ | ||
trace () { | ||
return tracer.trace.apply(tracer, arguments) | ||
return this._tracer.trace.apply(this._tracer, arguments) | ||
} | ||
startSpan () { | ||
return tracer.startSpan.apply(tracer, arguments) | ||
return this._tracer.startSpan.apply(this._tracer, arguments) | ||
} | ||
inject () { | ||
return tracer.inject.apply(tracer, arguments) | ||
return this._tracer.inject.apply(this._tracer, arguments) | ||
} | ||
extract () { | ||
return tracer.extract.apply(tracer, arguments) | ||
return this._tracer.extract.apply(this._tracer, arguments) | ||
} | ||
currentSpan () { | ||
return tracer.currentSpan.apply(tracer, arguments) | ||
return this._tracer.currentSpan.apply(this._tracer, arguments) | ||
} | ||
bind () { | ||
return tracer.bind.apply(tracer, arguments) | ||
return this._tracer.bind.apply(this._tracer, arguments) | ||
} | ||
bindEmitter () { | ||
return tracer.bindEmitter.apply(tracer, arguments) | ||
return this._tracer.bindEmitter.apply(this._tracer, arguments) | ||
} | ||
@@ -50,0 +57,0 @@ } |
@@ -5,2 +5,3 @@ 'use strict' | ||
const Tracer = require('./opentracing/tracer') | ||
const Instrumenter = require('./instrumenter') | ||
@@ -12,2 +13,4 @@ class DatadogTracer extends Tracer { | ||
this._context = platform.context(config) | ||
this._instrumenter = new Instrumenter(this, config) | ||
this._instrumenter.patch() | ||
} | ||
@@ -22,3 +25,3 @@ | ||
this._context.run(() => { | ||
const childOf = this._context.get('current') | ||
const childOf = options.childOf || this._context.get('current') | ||
const tags = Object.assign({ | ||
@@ -25,0 +28,0 @@ 'service.name': options.service || this._service, |
@@ -7,2 +7,3 @@ 'use strict' | ||
const encode = require('./encode') | ||
const tracerVersion = require('../lib/version') | ||
@@ -46,3 +47,8 @@ class Writer { | ||
headers: { | ||
'Content-Type': 'application/msgpack' | ||
'Content-Type': 'application/msgpack', | ||
'Datadog-Meta-Lang': platform.name(), | ||
'Datadog-Meta-Lang-Version': platform.version(), | ||
'Datadog-Meta-Lang-Interpreter': platform.engine(), | ||
'Datadog-Meta-Tracer-Version': tracerVersion, | ||
'X-Datadog-Trace-Count': String(this._queue.length) | ||
}, | ||
@@ -49,0 +55,0 @@ data |
@@ -5,16 +5,14 @@ 'use strict' | ||
let Config | ||
let platform | ||
beforeEach(() => { | ||
Config = require('../src/config') | ||
}) | ||
platform = { | ||
env: sinon.stub(), | ||
service: sinon.stub() | ||
} | ||
platform.service.returns('test') | ||
afterEach(() => { | ||
delete process.env.DD_TRACE_AGENT_HOSTNAME | ||
delete process.env.DD_TRACE_AGENT_PORT | ||
delete process.env.DD_TRACE_ENABLED | ||
delete process.env.DD_TRACE_DEBUG | ||
delete process.env.DD_SERVICE_NAME | ||
delete process.env.DD_ENV | ||
Config = require('../src/config') | ||
Config = proxyquire('../src/config', { | ||
'./platform': platform | ||
}) | ||
}) | ||
@@ -25,2 +23,3 @@ | ||
expect(config).to.have.property('service', 'test') | ||
expect(config).to.have.property('enabled', true) | ||
@@ -35,11 +34,12 @@ expect(config).to.have.property('debug', false) | ||
expect(config).to.have.deep.property('tags', {}) | ||
expect(config).to.have.property('plugins', true) | ||
}) | ||
it('should initialize from environment variables', () => { | ||
process.env.DD_TRACE_AGENT_HOSTNAME = 'agent' | ||
process.env.DD_TRACE_AGENT_PORT = '6218' | ||
process.env.DD_TRACE_ENABLED = 'false' | ||
process.env.DD_TRACE_DEBUG = 'true' | ||
process.env.DD_SERVICE_NAME = 'service' | ||
process.env.DD_ENV = 'test' | ||
platform.env.withArgs('DD_TRACE_AGENT_HOSTNAME').returns('agent') | ||
platform.env.withArgs('DD_TRACE_AGENT_PORT').returns('6218') | ||
platform.env.withArgs('DD_TRACE_ENABLED').returns('false') | ||
platform.env.withArgs('DD_TRACE_DEBUG').returns('true') | ||
platform.env.withArgs('DD_SERVICE_NAME').returns('service') | ||
platform.env.withArgs('DD_ENV').returns('test') | ||
@@ -68,3 +68,4 @@ const config = new Config() | ||
tags, | ||
flushInterval: 5000 | ||
flushInterval: 5000, | ||
plugins: false | ||
}) | ||
@@ -80,12 +81,13 @@ | ||
expect(config).to.have.deep.property('tags', tags) | ||
expect(config).to.have.deep.property('flushInterval', 5000) | ||
expect(config).to.have.property('flushInterval', 5000) | ||
expect(config).to.have.property('plugins', false) | ||
}) | ||
it('should give priority to the options', () => { | ||
process.env.DD_TRACE_AGENT_HOSTNAME = 'agent' | ||
process.env.DD_TRACE_AGENT_PORT = '6218' | ||
process.env.DD_TRACE_ENABLED = 'false' | ||
process.env.DD_TRACE_DEBUG = 'true' | ||
process.env.DD_SERVICE_NAME = 'service' | ||
process.env.DD_ENV = 'test' | ||
platform.env.withArgs('DD_TRACE_AGENT_HOSTNAME').returns('agent') | ||
platform.env.withArgs('DD_TRACE_AGENT_PORT').returns('6218') | ||
platform.env.withArgs('DD_TRACE_ENABLED').returns('false') | ||
platform.env.withArgs('DD_TRACE_DEBUG').returns('true') | ||
platform.env.withArgs('DD_SERVICE_NAME').returns('service') | ||
platform.env.withArgs('DD_ENV').returns('test') | ||
@@ -92,0 +94,0 @@ const config = new Config({ |
'use strict' | ||
const tracer = require('../') | ||
const express = require('express') | ||
@@ -12,2 +11,3 @@ const bodyParser = require('body-parser') | ||
describe('dd-trace', () => { | ||
let tracer | ||
let agent | ||
@@ -17,10 +17,13 @@ let listener | ||
beforeEach(() => { | ||
tracer = require('../') | ||
return getPort().then(port => { | ||
agent = express() | ||
listener = agent.listen(port, 'localhost') | ||
listener = agent.listen() | ||
tracer.init({ | ||
service: 'test', | ||
port, | ||
flushInterval: 10 | ||
port: listener.address().port, | ||
flushInterval: 10, | ||
plugins: false | ||
}) | ||
@@ -32,2 +35,3 @@ }) | ||
listener.close() | ||
delete require.cache[require.resolve('../')] | ||
}) | ||
@@ -34,0 +38,0 @@ |
@@ -87,3 +87,13 @@ 'use strict' | ||
}) | ||
it('should set the error flag when there is an error tag', () => { | ||
span._tags['error.type'] = 'Error' | ||
span._tags['error.msg'] = 'boom' | ||
span._tags['error.stack'] = '' | ||
trace = format(span) | ||
expect(trace.error).to.equal(1) | ||
}) | ||
}) | ||
}) |
@@ -9,19 +9,17 @@ 'use strict' | ||
let integrations | ||
let config | ||
let tracer | ||
let requireDir | ||
let foo | ||
let bar | ||
beforeEach(() => { | ||
foo = 'foo' | ||
bar = 'bar' | ||
tracer = 'tracer' | ||
integrations = { | ||
foo: { | ||
name: 'foo', | ||
http: { | ||
name: 'http', | ||
patch: sinon.spy(), | ||
unpatch: sinon.spy() | ||
}, | ||
bar: { | ||
name: 'bar', | ||
express: { | ||
name: 'express', | ||
versions: ['4.x'], | ||
patch: sinon.spy(), | ||
@@ -36,5 +34,3 @@ unpatch: sinon.spy() | ||
Instrumenter = proxyquire('../src/instrumenter', { | ||
'require-dir': requireDir, | ||
'foo': foo, | ||
'bar': bar | ||
'require-dir': requireDir | ||
}) | ||
@@ -45,25 +41,41 @@ }) | ||
beforeEach(() => { | ||
config = { plugins: true } | ||
instrumenter = new Instrumenter(config) | ||
instrumenter = new Instrumenter(tracer, { plugins: true }) | ||
}) | ||
describe('patch', () => { | ||
it('should patch all modules', () => { | ||
const tracer = 'tracer' | ||
it('should patch modules from node_modules when they are loaded', () => { | ||
instrumenter.patch() | ||
instrumenter.patch(tracer) | ||
const express = require('express') | ||
expect(integrations.foo.patch).to.have.been.calledWith(foo, tracer) | ||
expect(integrations.bar.patch).to.have.been.calledWith(bar, tracer) | ||
expect(integrations.express.patch).to.have.been.calledWith(express, tracer) | ||
}) | ||
it('should only patch a module if its version is supported by the plugin ', () => { | ||
integrations.express.versions = ['^3.0.0'] | ||
instrumenter.patch() | ||
const express = require('express') | ||
expect(integrations.express.patch).to.not.have.been.calledWith(express, tracer) | ||
}) | ||
it('should patch native modules when they are loaded', () => { | ||
instrumenter.patch() | ||
const http = require('http') | ||
expect(integrations.http.patch).to.have.been.calledWith(http, tracer) | ||
}) | ||
}) | ||
describe('unpatch', () => { | ||
it('should unpatch all modules', () => { | ||
const tracer = 'tracer' | ||
it('should unpatch patched modules', () => { | ||
instrumenter.patch() | ||
instrumenter.unpatch(tracer) | ||
const express = require('express') | ||
expect(integrations.foo.unpatch).to.have.been.calledWith(foo, tracer) | ||
expect(integrations.bar.unpatch).to.have.been.calledWith(bar, tracer) | ||
instrumenter.unpatch() | ||
expect(integrations.express.unpatch).to.have.been.calledWith(express) | ||
}) | ||
@@ -75,4 +87,3 @@ }) | ||
beforeEach(() => { | ||
config = { plugins: false } | ||
instrumenter = new Instrumenter(config) | ||
instrumenter = new Instrumenter(tracer, { plugins: false }) | ||
}) | ||
@@ -82,22 +93,10 @@ | ||
it('should not patch any module', () => { | ||
const tracer = 'tracer' | ||
instrumenter.patch() | ||
instrumenter.patch(tracer) | ||
const express = require('express') | ||
expect(integrations.foo.patch).to.not.have.been.called | ||
expect(integrations.bar.patch).to.not.have.been.called | ||
expect(integrations.express.patch).to.not.have.been.calledWith(express, tracer) | ||
}) | ||
}) | ||
describe('unpatch', () => { | ||
it('should not unpatch any module', () => { | ||
const tracer = 'tracer' | ||
instrumenter.unpatch(tracer) | ||
expect(integrations.foo.unpatch).to.not.have.been.called | ||
expect(integrations.bar.unpatch).to.not.have.been.called | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -9,2 +9,52 @@ 'use strict' | ||
describe('Node', () => { | ||
let platform | ||
describe('name', () => { | ||
beforeEach(() => { | ||
platform = require('../../../src/platform/node') | ||
}) | ||
it('should return nodejs', () => { | ||
expect(platform.name()).to.equal('nodejs') | ||
}) | ||
}) | ||
describe('version', () => { | ||
beforeEach(() => { | ||
platform = require('../../../src/platform/node') | ||
}) | ||
it('should return the process version', () => { | ||
const version = platform.version() | ||
expect(version).to.be.a('string') | ||
expect(semver.eq(version, semver.valid(version))).to.be.true | ||
}) | ||
}) | ||
describe('engine', () => { | ||
let realEngine | ||
beforeEach(() => { | ||
platform = require('../../../src/platform/node') | ||
realEngine = process.jsEngine | ||
}) | ||
afterEach(() => { | ||
process.jsEngine = realEngine | ||
}) | ||
it('should return the correct engine for Chakra', () => { | ||
process.jsEngine = 'chakracore' | ||
expect(platform.engine()).to.equal('chakracore') | ||
}) | ||
it('should return the correct engine for V8', () => { | ||
delete process.jsEngine | ||
expect(platform.engine()).to.equal('v8') | ||
}) | ||
}) | ||
describe('id', () => { | ||
@@ -72,2 +122,27 @@ let id | ||
describe('load', () => { | ||
let service | ||
beforeEach(() => { | ||
platform = require('../../../src/platform/node') | ||
service = platform._service | ||
}) | ||
afterEach(() => { | ||
platform._service = service | ||
}) | ||
it('should load the service name from the user module', () => { | ||
require('./load/direct') | ||
expect(platform._service).to.equal('foo') | ||
}) | ||
it('should work even in subfolders', () => { | ||
require('./load/indirect') | ||
expect(platform._service).to.have.equal('foo') | ||
}) | ||
}) | ||
describe('request', () => { | ||
@@ -232,4 +307,8 @@ let request | ||
let cls | ||
let clsBluebird | ||
let config | ||
beforeEach(() => { | ||
clsBluebird = sinon.spy(require('cls-bluebird')) | ||
require.cache[require.resolve('cls-bluebird')].exports = clsBluebird | ||
context = require('../../../src/platform/node/context') | ||
@@ -239,5 +318,3 @@ }) | ||
afterEach(() => { | ||
delete require.cache[require.resolve('../../../src/platform/node/context')] | ||
delete require.cache[require.resolve('bluebird')] | ||
delete require.cache[require.resolve('cls-bluebird')] | ||
cls.destroyNamespace('dd-trace') | ||
}) | ||
@@ -247,39 +324,31 @@ | ||
beforeEach(() => { | ||
cls = require('../../../src/platform/node/context/cls') | ||
cls = require('continuation-local-storage') | ||
config = { experimental: { asyncHooks: false } } | ||
namespace = context(config) | ||
}) | ||
afterEach(() => { | ||
delete require.cache[require.resolve('../../../src/platform/node/context/cls')] | ||
}) | ||
it('should use the correct implementation from the experimental flag', () => { | ||
expect(context({ experimental: { asyncHooks: false } })).to.equal(cls) | ||
}) | ||
testContext({ experimental: { asyncHooks: false } }) | ||
testContext('../../../src/platform/node/context/cls') | ||
}) | ||
if (semver.gte(semver.valid(process.version), '8.2.0')) { | ||
beforeEach(() => { | ||
cls = require('../../../src/platform/node/context/cls_hooked') | ||
}) | ||
afterEach(() => { | ||
delete require.cache[require.resolve('../../../src/platform/node/context/cls_hooked')] | ||
}) | ||
describe('cls-hooked', () => { | ||
it('should use the correct implementation from the experimental flag', () => { | ||
expect(context({ experimental: { asyncHooks: true } })).to.equal(cls) | ||
beforeEach(() => { | ||
cls = require('cls-hooked') | ||
config = { experimental: { asyncHooks: true } } | ||
namespace = context(config) | ||
}) | ||
testContext({ experimental: { asyncHooks: true } }) | ||
testContext('../../../src/platform/node/context/cls_hooked') | ||
}) | ||
} | ||
function testContext (config) { | ||
beforeEach(() => { | ||
namespace = context(config) | ||
function testContext (modulePath) { | ||
afterEach(() => { | ||
delete require.cache[require.resolve(modulePath)] | ||
}) | ||
it('should use the correct implementation from the experimental flag', () => { | ||
expect(namespace).to.equal(require(modulePath)) | ||
}) | ||
describe('get/set', () => { | ||
@@ -378,2 +447,15 @@ it('should store a value', done => { | ||
}) | ||
it('should only patch bluebird once', () => { | ||
context(config) | ||
expect(clsBluebird).to.not.have.been.called | ||
}) | ||
it('should skip patching bluebird on error', () => { | ||
clsBluebird = proxyquire('../src/platform/node/context/cls_bluebird', { | ||
'cls-bluebird': () => { throw new Error() } | ||
}) | ||
expect(() => clsBluebird(namespace)).to.not.throw() | ||
}) | ||
} | ||
@@ -380,0 +462,0 @@ }) |
@@ -12,2 +12,3 @@ 'use strict' | ||
let config | ||
let platform | ||
@@ -41,6 +42,11 @@ beforeEach(() => { | ||
platform = { | ||
load: sinon.spy() | ||
} | ||
Proxy = proxyquire('../src/proxy', { | ||
'./tracer': DatadogTracer, | ||
'./noop': NoopTracer, | ||
'./config': Config | ||
'./config': Config, | ||
'./platform': platform | ||
}) | ||
@@ -66,2 +72,10 @@ | ||
it('should load the platform', () => { | ||
const options = {} | ||
proxy.init(options) | ||
expect(platform.load).to.have.been.called | ||
}) | ||
it('should not initialize twice', () => { | ||
@@ -68,0 +82,0 @@ proxy.init() |
@@ -8,5 +8,15 @@ 'use strict' | ||
const nock = require('nock') | ||
const retry = require('retry') | ||
const pg = require('pg') | ||
const platform = require('../src/platform') | ||
const node = require('../src/platform/node') | ||
const retryOptions = { | ||
retries: 10, | ||
factor: 1, | ||
minTimeout: 1000, | ||
maxTimeout: 1000, | ||
randomize: false | ||
} | ||
chai.use(sinonChai) | ||
@@ -20,1 +30,45 @@ | ||
platform.use(node) | ||
waitForServices() | ||
.then(run) | ||
.catch(err => { | ||
setImmediate(() => { throw err }) | ||
}) | ||
function waitForServices () { | ||
return Promise.all([ | ||
waitForPostgres() | ||
]) | ||
} | ||
function waitForPostgres () { | ||
return new Promise((resolve, reject) => { | ||
const operation = retry.operation(retryOptions) | ||
operation.attempt(currentAttempt => { | ||
const client = new pg.Client({ | ||
user: 'postgres', | ||
password: 'postgres', | ||
database: 'postgres', | ||
application_name: 'test' | ||
}) | ||
client.connect((err) => { | ||
if (operation.retry(err)) return | ||
if (err) return reject(err) | ||
client.query('SELECT version()', (err, result) => { | ||
if (operation.retry(err)) return | ||
if (err) return reject(err) | ||
client.end((err) => { | ||
if (operation.retry(err)) return | ||
if (err) return reject(err) | ||
resolve() | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
} |
'use strict' | ||
const Span = require('../src/opentracing/span') | ||
const SpanContext = require('../src/opentracing/span_context') | ||
const Config = require('../src/config') | ||
@@ -12,2 +13,4 @@ const platform = require('../src/platform') | ||
let config | ||
let instrumenter | ||
let Instrumenter | ||
@@ -20,3 +23,10 @@ beforeEach(() => { | ||
Tracer = require('../src/tracer') | ||
instrumenter = { | ||
patch: sinon.spy() | ||
} | ||
Instrumenter = sinon.stub().returns(instrumenter) | ||
Tracer = proxyquire('../src/tracer', { | ||
'./instrumenter': Instrumenter | ||
}) | ||
}) | ||
@@ -29,2 +39,9 @@ | ||
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', () => { | ||
@@ -100,2 +117,17 @@ it('should run the callback with the new span', done => { | ||
}) | ||
it('should support a custom parent span', done => { | ||
const childOf = new SpanContext({ | ||
traceId: 1234, | ||
spanId: 5678 | ||
}) | ||
tracer = new Tracer(config) | ||
tracer.trace('name', { childOf }, current => { | ||
expect(current.context().traceId).to.equal(childOf.traceId) | ||
expect(current.context().parentId).to.equal(childOf.spanId) | ||
done() | ||
}) | ||
}) | ||
}) | ||
@@ -102,0 +134,0 @@ |
@@ -25,2 +25,5 @@ 'use strict' | ||
platform = { | ||
name: sinon.stub(), | ||
version: sinon.stub(), | ||
engine: sinon.stub(), | ||
request: sinon.stub().returns(Promise.resolve()), | ||
@@ -49,3 +52,4 @@ msgpack: { | ||
'./format': format, | ||
'./encode': encode | ||
'./encode': encode, | ||
'../lib/version': 'tracerVersion' | ||
}) | ||
@@ -103,2 +107,5 @@ writer = new Writer(url, 3) | ||
platform.msgpack.prefix.withArgs(['encoded', 'encoded']).returns('prefixed') | ||
platform.name.returns('lang') | ||
platform.version.returns('version') | ||
platform.engine.returns('interpreter') | ||
@@ -116,3 +123,8 @@ writer.append(span) | ||
headers: { | ||
'Content-Type': 'application/msgpack' | ||
'Content-Type': 'application/msgpack', | ||
'Datadog-Meta-Lang': 'lang', | ||
'Datadog-Meta-Lang-Version': 'version', | ||
'Datadog-Meta-Lang-Interpreter': 'interpreter', | ||
'Datadog-Meta-Tracer-Version': 'tracerVersion', | ||
'X-Datadog-Trace-Count': '2' | ||
}, | ||
@@ -119,0 +131,0 @@ data: 'prefixed' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 6 instances in 1 package
117577
84
3439
181
7
17
22
1
4
+ Addedmethods@^1.1.2
+ Addedparent-module@^0.1.0
+ Addedread-pkg-up@^3.0.0
+ Addedrequire-in-the-middle@^2.2.1
+ Addedsemver@^5.5.0
+ Addedshimmer@^1.2.0
+ Addedcallsites@1.0.1(transitive)
+ Addederror-ex@1.3.2(transitive)
+ Addedfind-up@2.1.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhosted-git-info@2.8.9(transitive)
+ Addedis-arrayish@0.2.1(transitive)
+ Addedis-core-module@2.15.1(transitive)
+ Addedjson-parse-better-errors@1.0.2(transitive)
+ Addedload-json-file@4.0.0(transitive)
+ Addedlocate-path@2.0.0(transitive)
+ Addedmethods@1.1.2(transitive)
+ Addedmodule-details-from-path@1.0.3(transitive)
+ Addednormalize-package-data@2.5.0(transitive)
+ Addedopentracing@0.14.1(transitive)
+ Addedp-limit@1.3.0(transitive)
+ Addedp-locate@2.0.0(transitive)
+ Addedp-try@1.0.0(transitive)
+ Addedparent-module@0.1.0(transitive)
+ Addedparse-json@4.0.0(transitive)
+ Addedpath-exists@3.0.0(transitive)
+ Addedpath-parse@1.0.7(transitive)
+ Addedpath-type@3.0.0(transitive)
+ Addedpify@3.0.0(transitive)
+ Addedread-pkg@3.0.0(transitive)
+ Addedread-pkg-up@3.0.0(transitive)
+ Addedrequire-in-the-middle@2.2.2(transitive)
+ Addedresolve@1.22.8(transitive)
+ Addedspdx-correct@3.2.0(transitive)
+ Addedspdx-exceptions@2.5.0(transitive)
+ Addedspdx-expression-parse@3.0.1(transitive)
+ Addedspdx-license-ids@3.0.20(transitive)
+ Addedstrip-bom@3.0.0(transitive)
+ Addedsupports-preserve-symlinks-flag@1.0.0(transitive)
+ Addedvalidate-npm-package-license@3.0.4(transitive)
- Removedopentracing@0.14.7(transitive)
Updatedopentracing@0.14.1