Skip to content

Commit

Permalink
Add tedious integration (#599)
Browse files Browse the repository at this point in the history
* Add tedious integration
  • Loading branch information
rishabh authored Jul 3, 2019
1 parent 865ef1d commit e08bb89
Show file tree
Hide file tree
Showing 9 changed files with 560 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,19 @@ jobs:
- PLUGINS=memcached
- image: memcached:1.5-alpine

node-tedious:
<<: *node-plugin-base
docker:
- image: node:8
environment:
- SERVICES=mssql
- PLUGINS=tedious
- image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu
environment:
- "ACCEPT_EULA=Y"
- "SA_PASSWORD=DD_HUNTER2"
- "MSSQL_PID=Express"

node-mysql:
<<: *node-plugin-base
docker:
Expand Down Expand Up @@ -557,6 +570,7 @@ workflows:
- node-koa
- node-memcached
- node-mongodb-core
- node-tedious
- node-mysql
- node-net
- node-paperplane
Expand Down Expand Up @@ -654,5 +668,6 @@ workflows:
- node-redis
- node-restify
- node-router
- node-tedious
- node-when
- node-winston
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ services:
image: postgres:9.5-alpine
ports:
- '127.0.0.1:5432:5432'
mssql:
image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu
environment:
- "ACCEPT_EULA=Y"
- "SA_PASSWORD=DD_HUNTER2"
- "MSSQL_PID=Express"
ports:
- "127.0.0.1:1433:1433"
mysql:
image: mysql:5.7
environment:
Expand Down
2 changes: 2 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ tracer.use('pg', {
<h5 id="restify"></h5>
<h5 id="restify-tags"></h5>
<h5 id="restify-config"></h5>
<h5 id="tedious"></h5>
<h5 id="when"></h5>
<h5 id="winston"></h5>
<h3 id="integrations-list">Available Plugins</h3>
Expand Down Expand Up @@ -291,6 +292,7 @@ tracer.use('pg', {
* [redis](./interfaces/plugins.redis.html)
* [restify](./interfaces/plugins.restify.html)
* [router](./interfaces/plugins.router.html)
* [tedious](./interfaces/plugins.tedious.html)
* [when](./interfaces/plugins.when.html)
* [winston](./interfaces/plugins.winston.html)

Expand Down
1 change: 1 addition & 0 deletions docs/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ tracer.use('redis');
tracer.use('restify');
tracer.use('restify', httpServerOptions);
tracer.use('router');
tracer.use('tedious');
tracer.use('when');
tracer.use('winston');

Expand Down
7 changes: 7 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ interface Plugins {
"redis": plugins.redis;
"restify": plugins.restify;
"router": plugins.router;
"tedious": plugins.tedious;
"when": plugins.when;
"winston": plugins.winston;
}
Expand Down Expand Up @@ -792,6 +793,12 @@ declare namespace plugins {
*/
interface router extends Integration {}

/**
* This plugin automatically instruments the
* [tedious](https://github.com/tediousjs/tedious/) module.
*/
interface tedious extends Integration {}

/**
* This plugin patches the [when](https://github.com/cujojs/when)
* module to bind the promise callback the the caller context.
Expand Down
133 changes: 133 additions & 0 deletions packages/datadog-plugin-tedious/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
'use strict'

const Tags = require('../../../ext/tags')
const Kinds = require('../../../ext/kinds')
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
const tx = require('../../dd-trace/src/plugins/util/tx')

const procnameRegex = /^sp_[a-z]+$/

function createWrapRequestClass (tracer) {
return function wrapRequestClass (Request) {
class RequestWithTrace extends Request {
constructor (sqlTextOrProcedure, callback) {
super(sqlTextOrProcedure, callback)
tracer.scope().bind(this)
}
}

return RequestWithTrace
}
}

function createWrapConnectionClass (tracer) {
return function wrapConnectionClass (Connection) {
class ConnectionWithTrace extends Connection {
constructor (config) {
super(config)
tracer.scope().bind(this)
}
}

return ConnectionWithTrace
}
}

function createWrapMakeRequest (tracer, config) {
return function wrapMakeRequest (makeRequest) {
return function makeRequestWithTrace (request) {
const connectionConfig = this.config
const scope = tracer.scope()
const childOf = scope.active()
const query = getQuery(request)

if (!query) {
return makeRequest.apply(this, arguments)
}

const span = tracer.startSpan(`tedious.request`, {
childOf,
tags: {
[Tags.SPAN_KIND]: Kinds.CLIENT,
'db.type': 'mssql',
'span.type': 'sql',
'component': 'tedious',
'service.name': config.service || `${tracer._service}-mssql`,
'resource.name': query
}
})

addConnectionTags(span, connectionConfig)
addDatabaseTags(span, connectionConfig)
addProcIdTags(span, request)

analyticsSampler.sample(span, config.analytics)
request.callback = tx.wrap(span, request.callback)

return scope.bind(makeRequest, span).apply(this, arguments)
}
}
}

function createWrapGetRowStream (tracer) {
return function wrapGetRowStream (getRowStream) {
return function getRowStreamWithTrace () {
const scope = tracer.scope()

const rowToPacketTransform = getRowStream.apply(this, arguments)
return scope.bind(rowToPacketTransform)
}
}
}

function getQuery (request) {
if (request.parameters) {
if (request.parameters.length === 0) {
return request.sqlTextOrProcedure
} else {
const statement = request.parametersByName.statement || request.parametersByName.stmt
return statement.value
}
}
}

function addConnectionTags (span, connectionConfig) {
span.setTag('out.host', connectionConfig.server)
span.setTag('out.port', connectionConfig.options.port)
}

function addDatabaseTags (span, connectionConfig) {
span.setTag('db.user', connectionConfig.userName || connectionConfig.authentication.options.userName)
span.setTag('db.name', connectionConfig.options.database)
span.setTag('db.instance', connectionConfig.options.instanceName)
}

function addProcIdTags (span, request) {
if (!procnameRegex.test(request.sqlTextOrProcedure)) return
span.setTag('tds.proc.name', request.sqlTextOrProcedure)
}

module.exports = [
{
name: 'tedious',
versions: [ '>=1.0.0' ],
patch (tedious, tracer, config) {
this.wrap(tedious, 'Request', createWrapRequestClass(tracer))
this.wrap(tedious, 'Connection', createWrapConnectionClass(tracer))
this.wrap(tedious.Connection.prototype, 'makeRequest', createWrapMakeRequest(tracer, config))

if (tedious.BulkLoad && tedious.BulkLoad.prototype.getRowStream) {
this.wrap(tedious.BulkLoad.prototype, 'getRowStream', createWrapGetRowStream(tracer))
}
},
unpatch (tedious) {
this.unwrap(tedious, 'Request')
this.unwrap(tedious, 'Connection')
this.unwrap(tedious.Connection.prototype, 'makeRequest')

if (tedious.BulkLoad && tedious.BulkLoad.prototype.getRowStream) {
this.unwrap(tedious.BulkLoad.prototype, 'getRowStream')
}
}
}
]
Loading

0 comments on commit e08bb89

Please sign in to comment.