Comparing version 0.1.7 to 0.2.0
@@ -6,4 +6,4 @@ # Contributing to dd-trace-js | ||
This will ensure we avoid duplicating work, or that your code can't be merged due to a rapidly changing | ||
base. If you have any questions, create a [GitHub issue][1] and reach us! | ||
base. If you would like support for a module that is not listed, [contact support][1] to share a request. | ||
[1]: https://github.com/DataDog/dd-trace-js/issues | ||
[1]: https://docs.datadoghq.com/help |
@@ -1,1 +0,1 @@ | ||
module.exports = '0.1.7' | ||
module.exports = '0.2.0' |
{ | ||
"name": "dd-trace", | ||
"version": "0.1.7", | ||
"version": "0.2.0", | ||
"description": "Datadog APM tracing client for JavaScript (experimental)", | ||
@@ -8,5 +8,7 @@ "main": "index.js", | ||
"bench": "node benchmark", | ||
"jsdoc": "gulp jsdoc", | ||
"jsdoc:watch": "gulp jsdoc:watch", | ||
"lint": "eslint . && node scripts/check_licenses.js", | ||
"tdd": "mocha --watch", | ||
"test": "nyc --reporter text --reporter lcov mocha" | ||
"test": "nyc --reporter text --reporter lcov mocha 'test/**/*.spec.js'" | ||
}, | ||
@@ -67,4 +69,9 @@ "repository": { | ||
"get-port": "^3.2.0", | ||
"gulp": "^3.9.1", | ||
"gulp-jsdoc3": "^2.0.0", | ||
"jsdoc": "^3.5.5", | ||
"mocha": "^5.0.0", | ||
"mongodb-core": "^3.0.7", | ||
"mysql": "^2.15.0", | ||
"mysql2": "^1.5.3", | ||
"nock": "^9.1.6", | ||
@@ -71,0 +78,0 @@ "nyc": "^11.4.1", |
102
README.md
@@ -6,104 +6,14 @@ # dd-trace-js | ||
**Experimental JavaScript Tracer!** | ||
**JavaScript APM Tracer (beta)** | ||
This project is **experimental** and under active development. Use it at your own risk. | ||
This project is in open beta and under active development. Please contact [Datadog support](https://docs.datadoghq.com/help) with any questions. | ||
## Installation | ||
## Getting Started | ||
### NodeJS | ||
For a basic product overview, check out our [setup documentation](https://docs.datadoghq.com/tracing/setup/javascript/). | ||
```sh | ||
npm install --save dd-trace | ||
``` | ||
For installation, configuration, and details about using the API, check out our [API documentation](https://datadog.github.io/dd-trace-js). | ||
*Node >= 4 is required.* | ||
For descriptions of terminology used in APM, take a look at the [official documentation](https://docs.datadoghq.com/tracing/visualization/). | ||
## Usage | ||
Simply require and initialize the tracer and all supported | ||
[libraries](#automatic-instrumentation) will automatically | ||
be instrumented. | ||
```js | ||
// The tracer must be initialized before other libraries | ||
const tracer = require('dd-trace').init() | ||
``` | ||
### Available Options | ||
Options can be configured as a parameter to the `init()` method | ||
or as environment variables. | ||
| Config | Environment Variable | Default | Description | | ||
| ------------- | ---------------------------- | --------- | ----------- | | ||
| debug | DD_TRACE_DEBUG | false | Enable debug logging in the tracer. | | ||
| service | DD_SERVICE_NAME | | The service name to be used for this program. | | ||
| hostname | DD_TRACE_AGENT_HOSTNAME | localhost | The address of the trace agent that the tracer will submit to. | | ||
| port | DD_TRACE_AGENT_PORT | 8126 | The port of the trace agent that the tracer will submit to. | | ||
| 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`. | | ||
| 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 | ||
This library is OpenTracing compliant, so once the tracer is initialized | ||
it can be used as a global tracer. | ||
```js | ||
const tracer = require('dd-trace').init() | ||
const opentracing = require('opentracing') | ||
opentracing.initGlobalTracer(tracer) | ||
``` | ||
Then the tracer will be available with `opentracing.globalTracer()`. | ||
See the OpenTracing JavaScript [documentation](https://github.com/opentracing/opentracing-javascript) | ||
and [API](https://doc.esdoc.org/github.com/opentracing/opentracing-javascript/) for more details. | ||
**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 | ||
@@ -110,0 +20,0 @@ |
@@ -8,7 +8,2 @@ 'use strict' | ||
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') |
@@ -7,4 +7,7 @@ 'use strict' | ||
const exec = require('child_process').execSync | ||
exec(`git checkout master`) | ||
exec(`git pull`) | ||
const pkg = require('../package.json') | ||
const increment = getIncrement() | ||
@@ -15,3 +18,2 @@ const version = semver.inc(pkg.version, increment) | ||
exec(`git checkout master`) | ||
exec(`git checkout -b v${version}`) | ||
@@ -18,0 +20,0 @@ write('package.json', JSON.stringify(pkg, null, 2) + '\n') |
@@ -13,17 +13,22 @@ 'use strict' | ||
const debug = coalesce(options.debug, platform.env('DD_TRACE_DEBUG'), false) | ||
const service = coalesce(options.service, platform.env('DD_SERVICE_NAME'), platform.service()) | ||
const env = coalesce(options.env, platform.env('DD_ENV')) | ||
const protocol = 'http' | ||
const hostname = coalesce(options.hostname, platform.env('DD_TRACE_AGENT_HOSTNAME'), 'localhost') | ||
const port = coalesce(options.port, platform.env('DD_TRACE_AGENT_PORT'), 8126) | ||
const sampleRate = coalesce(Math.min(Math.max(options.sampleRate, 0), 1), 1) | ||
const flushInterval = coalesce(parseInt(options.flushInterval, 10), 2000) | ||
const plugins = coalesce(options.plugins, true) | ||
this.enabled = String(enabled) === 'true' | ||
this.debug = String(debug) === 'true' | ||
this.service = coalesce(options.service, platform.env('DD_SERVICE_NAME'), platform.service()) | ||
this.env = coalesce(options.env, platform.env('DD_ENV')) | ||
this.service = service | ||
this.env = env | ||
this.url = new URL(`${protocol}://${hostname}:${port}`) | ||
this.tags = coalesce(options.tags, {}) | ||
this.flushInterval = coalesce(options.flushInterval, 2000) | ||
this.tags = Object.assign({}, options.tags) | ||
this.flushInterval = flushInterval | ||
this.bufferSize = 100000 | ||
this.sampleRate = 1 | ||
this.sampleRate = sampleRate | ||
this.logger = options.logger | ||
this.plugins = coalesce(options.plugins, true) | ||
this.plugins = !!plugins | ||
this.experimental = { | ||
@@ -36,5 +41,9 @@ asyncHooks: isFlagEnabled(options.experimental, 'asyncHooks') | ||
function isFlagEnabled (obj, prop) { | ||
return obj === true || (typeof obj === 'object' && obj !== null && obj[prop]) | ||
return obj === true || (isObject(obj) && !!obj[prop]) | ||
} | ||
function isObject (value) { | ||
return typeof value === 'object' && value !== null | ||
} | ||
module.exports = Config |
@@ -28,4 +28,4 @@ 'use strict' | ||
parent_id: spanContext.parentId, | ||
name: span._operationName, | ||
service: tracer._service, | ||
name: String(span._operationName), | ||
service: String(tracer._service), | ||
error: 0, | ||
@@ -44,3 +44,3 @@ meta: {}, | ||
case 'resource.name': | ||
trace[map[tag]] = tags[tag] | ||
trace[map[tag]] = String(tags[tag]) | ||
break | ||
@@ -51,6 +51,6 @@ case 'error.type': | ||
trace.error = 1 | ||
trace.meta[tag] = tags[tag] | ||
trace.meta[tag] = String(tags[tag]) | ||
break | ||
default: | ||
trace.meta[tag] = tags[tag] | ||
trace.meta[tag] = String(tags[tag]) | ||
} | ||
@@ -57,0 +57,0 @@ }) |
@@ -7,2 +7,3 @@ 'use strict' | ||
const hook = require('require-in-the-middle') | ||
const log = require('./log') | ||
@@ -53,4 +54,8 @@ // TODO: lazy load built-in plugins | ||
reload () { | ||
const instrumentedModules = Array.from(this._plugins.keys()).map(plugin => plugin.name) | ||
hook(instrumentedModules, this.hookModule.bind(this)) | ||
try { | ||
const instrumentedModules = Array.from(this._plugins.keys()).map(plugin => plugin.name) | ||
hook(instrumentedModules, this.hookModule.bind(this)) | ||
} catch (e) { | ||
log.error(e) | ||
} | ||
} | ||
@@ -57,0 +62,0 @@ |
'use strict' | ||
let _logger = { | ||
debug: () => {}, | ||
error: () => {} | ||
const _default = { | ||
debug: message => console.log(message), /* eslint-disable-line no-console */ | ||
error: err => console.error(err) /* eslint-disable-line no-console */ | ||
} | ||
let _logger = _default | ||
let _enabled = false | ||
module.exports = { | ||
use (logger) { | ||
const isObject = logger && typeof logger === 'object' | ||
if (isObject && logger.debug instanceof Function && logger.error instanceof Function) { | ||
if (logger && logger.debug instanceof Function && logger.error instanceof Function) { | ||
_logger = logger | ||
} | ||
return this | ||
}, | ||
toggle (enabled) { | ||
_enabled = enabled | ||
return this | ||
}, | ||
reset () { | ||
_logger = _default | ||
_enabled = false | ||
return this | ||
}, | ||
debug (message) { | ||
_logger.debug(message) | ||
if (_enabled) { | ||
_logger.debug(message instanceof Function ? message() : message) | ||
} | ||
return this | ||
}, | ||
error (message) { | ||
_logger.error(message) | ||
error (err) { | ||
if (_enabled) { | ||
if (err instanceof Function) { | ||
err = err() | ||
} | ||
_logger.error(typeof err === 'string' ? new Error(err) : err) | ||
} | ||
return this | ||
} | ||
} |
@@ -7,2 +7,3 @@ 'use strict' | ||
const platform = require('../platform') | ||
const log = require('../log') | ||
@@ -73,9 +74,13 @@ class DatadogSpan extends Span { | ||
_addTags (keyValuePairs) { | ||
Object.keys(keyValuePairs).forEach(key => { | ||
this._tags[key] = String(keyValuePairs[key]) | ||
}) | ||
try { | ||
Object.keys(keyValuePairs).forEach(key => { | ||
this._tags[key] = String(keyValuePairs[key]) | ||
}) | ||
} catch (e) { | ||
log.error(e) | ||
} | ||
} | ||
_finish (finishTime) { | ||
finishTime = finishTime || platform.now() | ||
finishTime = parseInt(finishTime, 10) || platform.now() | ||
@@ -82,0 +87,0 @@ this._duration = finishTime - this._startTime |
@@ -5,3 +5,5 @@ 'use strict' | ||
const Tracer = opentracing.Tracer | ||
const Reference = opentracing.Reference | ||
const Span = require('./span') | ||
const SpanContext = require('./span_context') | ||
const Recorder = require('../recorder') | ||
@@ -19,2 +21,3 @@ const Sampler = require('../sampler') | ||
log.use(config.logger) | ||
log.toggle(config.debug) | ||
@@ -55,3 +58,8 @@ this._service = config.service | ||
_inject (spanContext, format, carrier) { | ||
this._propagators[format].inject(spanContext, carrier) | ||
try { | ||
this._propagators[format].inject(spanContext, carrier) | ||
} catch (e) { | ||
log.error(e) | ||
} | ||
return this | ||
@@ -61,3 +69,8 @@ } | ||
_extract (format, carrier) { | ||
return this._propagators[format].extract(carrier) | ||
try { | ||
return this._propagators[format].extract(carrier) | ||
} catch (e) { | ||
log.error(e) | ||
return null | ||
} | ||
} | ||
@@ -76,2 +89,15 @@ | ||
const ref = references[i] | ||
if (!(ref instanceof Reference)) { | ||
log.error(() => `Expected ${ref} to be an instance of opentracing.Reference`) | ||
break | ||
} | ||
const spanContext = ref.referencedContext() | ||
if (!(spanContext instanceof SpanContext)) { | ||
log.error(() => `Expected ${spanContext} to be an instance of SpanContext`) | ||
break | ||
} | ||
if (ref.type() === opentracing.REFERENCE_CHILD_OF) { | ||
@@ -78,0 +104,0 @@ parent = ref.referencedContext() |
@@ -18,6 +18,8 @@ 'use strict' | ||
const req = http.request(options, res => { | ||
res.on('data', chunk => {}) | ||
let data = '' | ||
res.on('data', chunk => { data += chunk }) | ||
res.on('end', () => { | ||
if (res.statusCode >= 200 && res.statusCode <= 299) { | ||
resolve() | ||
resolve(data) | ||
} else { | ||
@@ -24,0 +26,0 @@ const error = new Error(http.STATUS_CODES[res.statusCode]) |
@@ -86,21 +86,3 @@ 'use strict' | ||
function createWrapRouterHandle (tracer, config) { | ||
const context = tracer._context | ||
return function wrapRouterHandle (handle) { | ||
return function handleWithTrace (req, res, out) { | ||
let returnValue | ||
context.run(() => { | ||
returnValue = handle.call(this, req, res, context.bind(out)) | ||
}) | ||
return returnValue | ||
} | ||
} | ||
} | ||
function createWrapRouterMethod (tracer) { | ||
const context = tracer._context | ||
return function wrapRouterMethod (original) { | ||
@@ -116,3 +98,3 @@ return function methodWithTrace (fn) { | ||
layer.handle_request = (req, res, next) => { | ||
return handle.call(layer, req, res, context.bind(next)) | ||
return handle.call(layer, req, res, next) | ||
} | ||
@@ -150,3 +132,2 @@ | ||
shimmer.wrap(express.Router, 'process_params', createWrapProcessParams(tracer, config)) | ||
shimmer.wrap(express.Router, 'handle', createWrapRouterHandle(tracer, config)) | ||
shimmer.wrap(express.Router, 'use', createWrapRouterMethod(tracer, config)) | ||
@@ -159,3 +140,2 @@ shimmer.wrap(express.Router, 'route', createWrapRouterMethod(tracer, config)) | ||
shimmer.unwrap(express.Router, 'process_params') | ||
shimmer.unwrap(express.Router, 'handle') | ||
shimmer.unwrap(express.Router, 'use') | ||
@@ -162,0 +142,0 @@ shimmer.unwrap(express.Router, 'route') |
@@ -17,3 +17,3 @@ 'use strict' | ||
const uri = extractUrl(options) | ||
const method = options.method || 'GET' | ||
const method = (options.method || 'GET').toUpperCase() | ||
@@ -41,3 +41,3 @@ if (uri === `${tracer._url.href}/v0.3/traces`) { | ||
'span.type': 'web', | ||
'resource.name': options.pathname | ||
'resource.name': method | ||
}) | ||
@@ -44,0 +44,0 @@ |
'use strict' | ||
const Tracer = require('opentracing').Tracer | ||
const BaseTracer = require('opentracing').Tracer | ||
const NoopTracer = require('./noop') | ||
@@ -12,3 +12,9 @@ const DatadogTracer = require('./tracer') | ||
class TracerProxy extends Tracer { | ||
/** | ||
* The Datadog Tracer. An instance of this class is what is returned by the module. | ||
* | ||
* @extends external:"opentracing.Tracer" | ||
* @hideconstructor | ||
*/ | ||
class Tracer extends BaseTracer { | ||
constructor () { | ||
@@ -20,2 +26,19 @@ super() | ||
/** | ||
* Initializes the tracer. This should be called before importing other libraries. | ||
* | ||
* @param {Object} [options] Configuration options. | ||
* @param {boolean} [options.debug=false] Enable debug logging in the tracer. | ||
* @param {string} [options.service] The service name to be used for this program. | ||
* @param {string} [options.hostname=localhost] The address of the trace agent that the tracer will submit to. | ||
* @param {number|string} [options.port=8126] The port of the trace agent that the tracer will submit to. | ||
* @param {number} [options.sampleRate=1] Percentage of spans to sample as a float between 0 and 1. | ||
* @param {number} [options.flushInterval=2000] Interval in milliseconds at which the tracer | ||
* will submit traces to the agent. | ||
* @param {Object|boolean} [options.experimental={}] Experimental features can be enabled all at once | ||
* using boolean `true` or individually using key/value pairs. | ||
* @param {boolean} [options.experimental.asyncHooks=false] Whether to use Node's experimental async hooks. | ||
* @param {boolean} [options.plugins=true] Whether to load all built-in plugins. | ||
* @returns {Tracer} Self | ||
*/ | ||
init (options) { | ||
@@ -34,2 +57,10 @@ if (this._tracer === noop) { | ||
/** | ||
* Enable and optionally configure a plugin. | ||
* | ||
* @param {string} plugin The name of a built-in plugin. | ||
* @param {Object} [config] Configuration options. | ||
* @param {string} [config.service] The service name to be used for this plugin. | ||
* @returns {Tracer} Self | ||
*/ | ||
use () { | ||
@@ -40,2 +71,19 @@ this._instrumenter.use.apply(this._instrumenter, arguments) | ||
/** | ||
* Initiate a trace and creates a new span. | ||
* | ||
* @param {string} name The operation name to be used for this span. | ||
* @param {Object} [options] Configuration options. These will take precedence over environment variables. | ||
* @param {string} [options.service] The service name to be used for this span. | ||
* The service name from the tracer will be used if this is not provided. | ||
* @param {string} [options.resource] The resource name to be used for this span. | ||
* The operation name will be used if this is not provided. | ||
* @param {string} [options.type] The span type to be used for this span. | ||
* @param {?external:"opentracing.Span"|external:"opentracing.SpanContext"} [options.childOf] | ||
* The parent span or span context for the new span. Generally this is not needed as it will be | ||
* fetched from the current context. | ||
* @param {string} [options.tags={}] Global tags that should be assigned to every span. | ||
* @param {traceCallback} [callback] Optional callback. A promise will be returned instead if not set. | ||
* @returns {Promise<external:"opentracing.Span">|undefined} | ||
*/ | ||
trace (operationName, options, callback) { | ||
@@ -69,2 +117,7 @@ if (callback) { | ||
/** | ||
* Get the span from the current context. | ||
* | ||
* @returns {?external:"opentracing.Span"} The current span or null if outside a trace context. | ||
*/ | ||
currentSpan () { | ||
@@ -74,2 +127,8 @@ return this._tracer.currentSpan.apply(this._tracer, arguments) | ||
/** | ||
* Bind a function to the current trace context. | ||
* | ||
* @param {Function} callback The function to bind. | ||
* @returns {Function} The callback wrapped up in a context closure. | ||
*/ | ||
bind () { | ||
@@ -79,7 +138,12 @@ return this._tracer.bind.apply(this._tracer, arguments) | ||
/** | ||
* Bind an EventEmitter to the current trace context. | ||
* | ||
* @param {Function} callback The function to bind. | ||
*/ | ||
bindEmitter () { | ||
return this._tracer.bindEmitter.apply(this._tracer, arguments) | ||
this._tracer.bindEmitter.apply(this._tracer, arguments) | ||
} | ||
} | ||
module.exports = TracerProxy | ||
module.exports = Tracer |
@@ -24,4 +24,10 @@ 'use strict' | ||
if (trace.started.length === trace.finished.length) { | ||
const buffer = encode(trace.finished.map(format)) | ||
const formattedTrace = trace.finished.map(format) | ||
log.debug(() => `Encoding trace: ${JSON.stringify(formattedTrace)}`) | ||
const buffer = encode(formattedTrace) | ||
log.debug(() => `Adding encoded trace to buffer: ${buffer}`) | ||
if (this.length < this._size) { | ||
@@ -38,20 +44,23 @@ this._queue.push(buffer) | ||
const data = platform.msgpack.prefix(this._queue) | ||
const options = { | ||
protocol: this._url.protocol, | ||
hostname: this._url.hostname, | ||
port: this._url.port, | ||
path: '/v0.3/traces', | ||
method: 'PUT', | ||
headers: { | ||
'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) | ||
} | ||
} | ||
log.debug(() => `Request to the agent: ${JSON.stringify(options)}`) | ||
platform | ||
.request({ | ||
protocol: this._url.protocol, | ||
hostname: this._url.hostname, | ||
port: this._url.port, | ||
path: '/v0.3/traces', | ||
method: 'PUT', | ||
headers: { | ||
'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) | ||
}, | ||
data | ||
}) | ||
.request(Object.assign({ data }, options)) | ||
.then(res => log.debug(`Response from the agent: ${res}`)) | ||
.catch(e => log.error(e)) | ||
@@ -58,0 +67,0 @@ |
@@ -33,2 +33,3 @@ 'use strict' | ||
expect(config).to.have.property('plugins', true) | ||
expect(config).to.have.property('env', undefined) | ||
}) | ||
@@ -64,2 +65,3 @@ | ||
env: 'test', | ||
sampleRate: 0.5, | ||
logger, | ||
@@ -77,2 +79,3 @@ tags, | ||
expect(config).to.have.property('env', 'test') | ||
expect(config).to.have.property('sampleRate', 0.5) | ||
expect(config).to.have.property('logger', logger) | ||
@@ -130,2 +133,8 @@ expect(config).to.have.deep.property('tags', tags) | ||
}) | ||
it('should sanitize the sample rate to be between 0 and 1', () => { | ||
expect(new Config({ sampleRate: -1 })).to.have.property('sampleRate', 0) | ||
expect(new Config({ sampleRate: 2 })).to.have.property('sampleRate', 1) | ||
expect(new Config({ sampleRate: NaN })).to.have.property('sampleRate', 1) | ||
}) | ||
}) |
@@ -97,3 +97,21 @@ 'use strict' | ||
}) | ||
it('should sanitize the input', () => { | ||
tracer._service = null | ||
span._operationName = null | ||
span._tags = { | ||
'foo.bar': null | ||
} | ||
span._startTime = NaN | ||
span._duration = NaN | ||
trace = format(span) | ||
expect(trace.name).to.equal('null') | ||
expect(trace.service).to.equal('null') | ||
expect(trace.meta['foo.bar']).to.equal('null') | ||
expect(trace.start).to.be.instanceof(Uint64BE) | ||
expect(trace.duration).to.be.instanceof(Uint64BE) | ||
}) | ||
}) | ||
}) |
@@ -141,2 +141,6 @@ 'use strict' | ||
}) | ||
it('should handle errors', () => { | ||
expect(() => instrumenter.use()).not.to.throw() | ||
}) | ||
}) | ||
@@ -143,0 +147,0 @@ |
'use strict' | ||
/* eslint-disable no-console */ | ||
describe('log', () => { | ||
let log | ||
let logger | ||
let error | ||
beforeEach(() => { | ||
sinon.stub(console, 'log') | ||
sinon.stub(console, 'error') | ||
error = new Error() | ||
logger = { | ||
debug: sinon.spy(), | ||
error: sinon.spy() | ||
} | ||
log = require('../src/log') | ||
log.toggle(true) | ||
}) | ||
describe('without a logger', () => { | ||
it('should be a no op', () => { | ||
expect(log.debug).to.not.throw() | ||
expect(log.error).to.not.throw() | ||
afterEach(() => { | ||
log.reset() | ||
console.log.restore() | ||
console.error.restore() | ||
}) | ||
it('should support chaining', () => { | ||
expect(() => { | ||
log | ||
.use(logger) | ||
.toggle(true) | ||
.error('error') | ||
.debug('debug') | ||
.reset() | ||
}).to.not.throw() | ||
}) | ||
describe('debug', () => { | ||
it('should log to console by default', () => { | ||
log.debug('debug') | ||
expect(console.log).to.have.been.calledWith('debug') | ||
}) | ||
it('should support callbacks that return a message', () => { | ||
log.debug(() => 'debug') | ||
expect(console.log).to.have.been.calledWith('debug') | ||
}) | ||
}) | ||
describe('with an empty logger', () => { | ||
beforeEach(() => { | ||
log.use(null) | ||
describe('error', () => { | ||
it('should log to console by default', () => { | ||
log.error(error) | ||
expect(console.error).to.have.been.calledWith(error) | ||
}) | ||
it('should be a no op', () => { | ||
expect(log.debug).to.not.throw() | ||
expect(log.error).to.not.throw() | ||
it('should support callbacks that return a error', () => { | ||
log.error(() => error) | ||
expect(console.error).to.have.been.calledWith(error) | ||
}) | ||
it('should convert strings to errors', () => { | ||
log.error('error') | ||
expect(console.error).to.have.been.called | ||
expect(console.error.firstCall.args[0]).to.be.instanceof(Error) | ||
expect(console.error.firstCall.args[0]).to.have.property('message', 'error') | ||
}) | ||
it('should convert messages from callbacks to errors', () => { | ||
log.error(() => 'error') | ||
expect(console.error).to.have.been.called | ||
expect(console.error.firstCall.args[0]).to.be.instanceof(Error) | ||
expect(console.error.firstCall.args[0]).to.have.property('message', 'error') | ||
}) | ||
}) | ||
describe('with an invalid logger', () => { | ||
beforeEach(() => { | ||
log.use('invalid') | ||
describe('toggle', () => { | ||
it('should disable the logger', () => { | ||
log.toggle(false) | ||
log.debug('debug') | ||
log.error(error) | ||
expect(console.log).to.not.have.been.called | ||
expect(console.error).to.not.have.been.called | ||
}) | ||
it('should be a no op', () => { | ||
expect(log.debug).to.not.throw() | ||
expect(log.error).to.not.throw() | ||
it('should enable the logger', () => { | ||
log.toggle(false) | ||
log.toggle(true) | ||
log.debug('debug') | ||
log.error(error) | ||
expect(console.log).to.have.been.calledWith('debug') | ||
expect(console.error).to.have.been.calledWith(error) | ||
}) | ||
}) | ||
describe('with a valid logger', () => { | ||
let logger | ||
describe('use', () => { | ||
it('should set the underlying logger when valid', () => { | ||
log.use(logger) | ||
log.debug('debug') | ||
log.error(error) | ||
beforeEach(() => { | ||
logger = { | ||
debug: sinon.spy(), | ||
error: sinon.spy() | ||
} | ||
expect(logger.debug).to.have.been.calledWith('debug') | ||
expect(logger.error).to.have.been.calledWith(error) | ||
}) | ||
it('be a no op with an empty logger', () => { | ||
log.use(null) | ||
log.debug('debug') | ||
log.error(error) | ||
expect(console.log).to.have.been.calledWith('debug') | ||
expect(console.error).to.have.been.calledWith(error) | ||
}) | ||
it('be a no op with an invalid logger', () => { | ||
log.use('invalid') | ||
log.debug('debug') | ||
log.error(error) | ||
expect(console.log).to.have.been.calledWith('debug') | ||
expect(console.error).to.have.been.calledWith(error) | ||
}) | ||
}) | ||
describe('reset', () => { | ||
it('should reset the logger', () => { | ||
log.use(logger) | ||
log.reset() | ||
log.toggle(true) | ||
log.debug('debug') | ||
log.error(error) | ||
expect(console.log).to.have.been.calledWith('debug') | ||
expect(console.error).to.have.been.calledWith(error) | ||
}) | ||
it('should call the underlying logger', () => { | ||
it('should reset the toggle', () => { | ||
log.use(logger) | ||
log.reset() | ||
log.debug('debug') | ||
log.error('error') | ||
log.error(error) | ||
expect(logger.debug).to.have.been.calledWith('debug') | ||
expect(logger.error).to.have.been.calledWith('error') | ||
expect(console.log).to.not.have.been.called | ||
expect(console.error).to.not.have.been.called | ||
}) | ||
}) | ||
}) |
@@ -117,2 +117,8 @@ 'use strict' | ||
}) | ||
it('should handle errors', () => { | ||
span = new Span(tracer, { operationName: 'operation' }) | ||
expect(() => span.addTags()).not.to.throw() | ||
}) | ||
}) | ||
@@ -119,0 +125,0 @@ |
@@ -13,2 +13,3 @@ 'use strict' | ||
let recorder | ||
let SpanContext | ||
let spanContext | ||
@@ -27,2 +28,4 @@ let fields | ||
SpanContext = sinon.spy() | ||
span = {} | ||
@@ -54,7 +57,9 @@ Span = sinon.stub().returns(span) | ||
logger: 'logger', | ||
tags: {} | ||
tags: {}, | ||
debug: false | ||
} | ||
log = { | ||
use: sinon.spy() | ||
use: sinon.spy(), | ||
toggle: sinon.spy() | ||
} | ||
@@ -64,2 +69,3 @@ | ||
'./span': Span, | ||
'./span_context': SpanContext, | ||
'../recorder': Recorder, | ||
@@ -82,6 +88,7 @@ './propagation/text_map': TextMapPropagator, | ||
it('should be support logging', () => { | ||
it('should support logging', () => { | ||
tracer = new Tracer(config) | ||
expect(log.use).to.have.been.calledWith(config.logger) | ||
expect(log.toggle).to.have.been.calledWith(config.debug) | ||
}) | ||
@@ -108,3 +115,3 @@ | ||
it('should start a span that is the child of a span', () => { | ||
const parent = {} | ||
const parent = new SpanContext() | ||
@@ -125,3 +132,3 @@ fields.references = [ | ||
it('should start a span that follows from a span', () => { | ||
const parent = {} | ||
const parent = new SpanContext() | ||
@@ -142,7 +149,7 @@ fields.references = [ | ||
it('should ignore additional follow references', () => { | ||
const parent = {} | ||
const parent = new SpanContext() | ||
fields.references = [ | ||
new Reference(opentracing.REFERENCE_FOLLOWS_FROM, parent), | ||
new Reference(opentracing.REFERENCE_FOLLOWS_FROM, {}) | ||
new Reference(opentracing.REFERENCE_FOLLOWS_FROM, new SpanContext()) | ||
] | ||
@@ -160,4 +167,6 @@ | ||
it('should ignore unknown references', () => { | ||
const parent = new SpanContext() | ||
fields.references = [ | ||
new Reference('test', {}) | ||
new Reference('test', parent) | ||
] | ||
@@ -174,2 +183,28 @@ | ||
it('should ignore references that are not references', () => { | ||
fields.references = [{}] | ||
tracer = new Tracer(config) | ||
tracer.startSpan('name', fields) | ||
expect(Span).to.have.been.calledWithMatch(tracer, { | ||
operationName: 'name', | ||
parent: null | ||
}) | ||
}) | ||
it('should ignore references to objects other than span contexts', () => { | ||
fields.references = [ | ||
new Reference(opentracing.REFERENCE_CHILD_OF, {}) | ||
] | ||
tracer = new Tracer(config) | ||
tracer.startSpan('name', fields) | ||
expect(Span).to.have.been.calledWithMatch(tracer, { | ||
operationName: 'name', | ||
parent: null | ||
}) | ||
}) | ||
it('should merge default tracer tags with span tags', () => { | ||
@@ -239,2 +274,8 @@ config.tags = { | ||
}) | ||
it('should handle errors', () => { | ||
tracer = new Tracer(config) | ||
expect(() => tracer.inject()).not.to.throw() | ||
}) | ||
}) | ||
@@ -272,3 +313,9 @@ | ||
}) | ||
it('should handle errors', () => { | ||
tracer = new Tracer(config) | ||
expect(() => tracer.extract()).not.to.throw() | ||
}) | ||
}) | ||
}) |
@@ -192,3 +192,3 @@ 'use strict' | ||
.put('/path', { foo: 'bar' }) | ||
.reply(200) | ||
.reply(200, 'OK') | ||
@@ -205,2 +205,4 @@ return request({ | ||
data: Buffer.from(JSON.stringify({ foo: 'bar' })) | ||
}).then(res => { | ||
expect(res).to.equal('OK') | ||
}) | ||
@@ -217,3 +219,3 @@ }) | ||
.put('/path', 'fizzbuzz') | ||
.reply(200) | ||
.reply(200, 'OK') | ||
@@ -230,2 +232,4 @@ return request({ | ||
data: [Buffer.from('fizz', 'utf-8'), Buffer.from('buzz', 'utf-8')] | ||
}).then(res => { | ||
expect(res).to.equal('OK') | ||
}) | ||
@@ -232,0 +236,0 @@ }) |
@@ -226,2 +226,3 @@ 'use strict' | ||
app.get('/user', (req, res) => { | ||
expect(context.get('current')).to.not.be.undefined | ||
res.status(200).send(context.get('foo')) | ||
@@ -228,0 +229,0 @@ }) |
@@ -40,3 +40,3 @@ 'use strict' | ||
expect(traces[0][0]).to.have.property('type', 'web') | ||
expect(traces[0][0]).to.have.property('resource', '/user') | ||
expect(traces[0][0]).to.have.property('resource', 'GET') | ||
expect(traces[0][0].meta).to.have.property('span.kind', 'client') | ||
@@ -43,0 +43,0 @@ expect(traces[0][0].meta).to.have.property('http.url', `http://localhost:${port}/user`) |
@@ -12,2 +12,3 @@ 'use strict' | ||
const redis = require('redis') | ||
const mongo = require('mongodb-core') | ||
const platform = require('../src/platform') | ||
@@ -17,6 +18,6 @@ const node = require('../src/platform/node') | ||
const retryOptions = { | ||
retries: 10, | ||
retries: 60, | ||
factor: 1, | ||
minTimeout: 3000, | ||
maxTimeout: 3000, | ||
minTimeout: 5000, | ||
maxTimeout: 5000, | ||
randomize: false | ||
@@ -44,3 +45,4 @@ } | ||
waitForMysql(), | ||
waitForRedis() | ||
waitForRedis(), | ||
waitForMongo() | ||
]) | ||
@@ -83,16 +85,19 @@ } | ||
return new Promise((resolve, reject) => { | ||
const connection = mysql.createConnection({ | ||
host: 'localhost', | ||
user: 'user', | ||
password: 'userpass', | ||
database: 'db' | ||
}) | ||
const operation = retry.operation(retryOptions) | ||
connection.connect() | ||
operation.attempt(currentAttempt => { | ||
const connection = mysql.createConnection({ | ||
host: 'localhost', | ||
user: 'user', | ||
password: 'userpass', | ||
database: 'db' | ||
}) | ||
connection.query('SELECT 1 + 1 AS solution', (error, results, fields) => { | ||
if (error) throw error | ||
connection.connect(err => { | ||
if (operation.retry(err)) return | ||
if (err) reject(err) | ||
connection.end(() => resolve()) | ||
}) | ||
}) | ||
connection.end(() => resolve()) | ||
}) | ||
@@ -119,1 +124,28 @@ } | ||
} | ||
function waitForMongo () { | ||
return new Promise((resolve, reject) => { | ||
const operation = retry.operation(retryOptions) | ||
operation.attempt(currentAttempt => { | ||
const server = new mongo.Server({ | ||
host: 'localhost', | ||
port: 27017, | ||
reconnect: false | ||
}) | ||
server.on('connect', server => { | ||
server.destroy() | ||
resolve() | ||
}) | ||
server.on('error', err => { | ||
if (!operation.retry(err)) { | ||
reject(err) | ||
} | ||
}) | ||
server.connect() | ||
}) | ||
}) | ||
} |
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
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
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
200474
105
5666
28
91