UNPKG

signalfx-tracing

Version:

Provides auto-instrumentation for JavaScript libraries and frameworks

157 lines (126 loc) 4.2 kB
'use strict' const Buffer = require('safe-buffer').Buffer const analyticsSampler = require('../analytics_sampler') const tx = require('./util/tx') function createWrapOperation (tracer, config, operationName) { return function wrapOperation (operation) { return function operationWithTrace (ns, ops, options, callback) { const scope = tracer.scope() const childOf = scope.active() const span = tracer.startSpan('mongodb.query', { childOf }) addTags(span, tracer, config, ns, ops, this, operationName) analyticsSampler.sample(span, config.analytics) if (typeof options === 'function') { return scope .bind(operation, span) .call(this, ns, ops, wrapCallback(tracer, span, options)) } else { return scope .bind(operation, span) .call(this, ns, ops, options, wrapCallback(tracer, span, callback)) } } } } function createWrapNext (tracer, config) { return function wrapNext (next) { return function nextWithTrace (cb) { const scope = tracer.scope() const childOf = scope.active() const span = tracer.startSpan('mongodb.query', { childOf }) addTags(span, tracer, config, this.ns, this.cmd, this.topology) if (this.cursorState) { span.addTags({ 'mongodb.cursor.index': this.cursorState.cursorIndex }) } scope.bind(next, span).call(this, wrapCallback(tracer, span, cb, this)) } } } function addTags (span, tracer, config, ns, cmd, topology, operationName) { if (!operationName) { operationName = Object.keys(cmd)[0] } span.setOperationName(`mongo.${operationName}`) const query = getQuery(cmd) const resource = getResource(ns, cmd, query, operationName) span.addTags({ 'service.name': config.service || `${tracer._service}-mongodb`, 'span.type': 'mongodb', 'db.name': ns, 'db.statement': resource, 'db.type': 'mongo', 'db.instance': ns.split('.')[0] }) addHost(span, topology) } function addHost (span, topology) { const options = topology && topology.s && topology.s.options if (options && options.host && options.port) { tx.setHost(span, topology.s.options.host, topology.s.options.port) } } function wrapCallback (tracer, span, done, cursor) { return tracer.scope().bind((err, res) => { if (err) { span.addTags({ 'sfx.error.kind': err.name, 'sfx.error.message': err.message, 'sfx.error.stack': err.stack }) } if (cursor) { addHost(span, cursor.server) } span.finish() if (done) { done(err, res) } }) } function getQuery (cmd) { return cmd.query && JSON.stringify(sanitize(cmd.query)) } function getResource (ns, cmd, query, operationName) { const parts = [operationName, ns] if (query) { parts.push(query) } return parts.join(' ') } function sanitize (input) { const output = {} if (!isObject(input) || Buffer.isBuffer(input) || isBSON(input)) return '?' for (const key in input) { if (typeof input[key] === 'function') continue output[key] = sanitize(input[key]) } return output } function isObject (val) { return typeof val === 'object' && val !== null && !(val instanceof Array) } function isBSON (val) { return val && val._bsontype } module.exports = [ { name: 'mongodb-core', versions: ['>=2'], patch (mongo, tracer, config) { this.wrap(mongo.Server.prototype, 'command', createWrapOperation(tracer, config)) this.wrap(mongo.Server.prototype, 'insert', createWrapOperation(tracer, config, 'insert')) this.wrap(mongo.Server.prototype, 'update', createWrapOperation(tracer, config, 'update')) this.wrap(mongo.Server.prototype, 'remove', createWrapOperation(tracer, config, 'remove')) this.wrap(mongo.Cursor.prototype, 'next', createWrapNext(tracer, config)) }, unpatch (mongo) { this.unwrap(mongo.Server.prototype, 'command') this.unwrap(mongo.Server.prototype, 'insert') this.unwrap(mongo.Server.prototype, 'update') this.unwrap(mongo.Server.prototype, 'remove') this.unwrap(mongo.Cursor.prototype, 'next') } } ]