diff --git a/config.js b/config.js index 18a5d29db..c56fcce96 100644 --- a/config.js +++ b/config.js @@ -45,6 +45,7 @@ module.exports = { plugins: { 'connect': path.join(__dirname, 'src/plugins/plugin-connect.js'), 'express': path.join(__dirname, 'src/plugins/plugin-express.js'), + 'google-gax': path.join(__dirname, 'src/plugins/plugin-google-gax.js'), 'grpc': path.join(__dirname, 'src/plugins/plugin-grpc.js'), 'hapi': path.join(__dirname, 'src/plugins/plugin-hapi.js'), 'http': path.join(__dirname, 'src/plugins/plugin-http.js'), diff --git a/src/plugins/plugin-google-gax.js b/src/plugins/plugin-google-gax.js new file mode 100644 index 000000000..4d1701e09 --- /dev/null +++ b/src/plugins/plugin-google-gax.js @@ -0,0 +1,50 @@ +/** + * Copyright 2017 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +var shimmer = require('shimmer'); + +module.exports = [ + { + file: '', + versions: '^0.10.x', + patch: function(gax, api) { + shimmer.wrap(gax, 'createApiCall', function(createApiCall) { + return function createApiCall_trace(funcWithAuth, settings, optDescriptor) { + var funcWithAuthThen = funcWithAuth.then; + funcWithAuth.then = function(cb) { + var result = funcWithAuthThen.call(this, cb); + var resultThen = result.then; + result.then = function(cb) { + return resultThen.call(this, api.wrap(cb)); + }; + return result; + }; + var apiCallInner = createApiCall.call(this, funcWithAuth, settings, optDescriptor); + return function apiCallInner_trace(request, callOptions, callback) { + // This api.wrap is only applied to ensure context is restored when the user callback + // is invoked. It is not required to trace google api libraries. + return apiCallInner.call(this, request, callOptions, api.wrap(callback)); + }; + }; + }); + }, + unpatch: function(gax) { + shimmer.unwrap(gax, 'createApiCall'); + } + } +]; diff --git a/test/plugins/fixtures/google-cloud-speech0.6/index.js b/test/plugins/fixtures/google-cloud-speech0.6/index.js new file mode 100644 index 000000000..2d9492ef4 --- /dev/null +++ b/test/plugins/fixtures/google-cloud-speech0.6/index.js @@ -0,0 +1 @@ +module.exports = require('@google-cloud/speech'); diff --git a/test/plugins/fixtures/google-cloud-speech0.6/package.json b/test/plugins/fixtures/google-cloud-speech0.6/package.json new file mode 100644 index 000000000..1252a60c2 --- /dev/null +++ b/test/plugins/fixtures/google-cloud-speech0.6/package.json @@ -0,0 +1,8 @@ +{ + "name": "google-cloud-speech0.6", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "@google-cloud/speech": "^0.6.0" + } +} diff --git a/test/plugins/test-trace-google-gax.js b/test/plugins/test-trace-google-gax.js new file mode 100644 index 000000000..98c996060 --- /dev/null +++ b/test/plugins/test-trace-google-gax.js @@ -0,0 +1,61 @@ +/** + * Copyright 2017 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +var common = require('./common.js'); +var assert = require('assert'); +var path = require('path'); + +describe('google-gax', function() { + var agent; + var speech; + + before(function() { + agent = require('../..').start({ + enhancedDatabaseReporting: true, + samplingRate: 0 + }); + process.env.GOOGLE_APPLICATION_CREDENTIALS = + path.join(__dirname, '..', 'fixtures', 'gcloud-credentials.json'); + speech = require('./fixtures/google-cloud-speech0.6')(); + }); + + after(function() { + delete process.env.GOOGLE_APPLICATION_CREDENTIALS; + }); + + it('should not interfere with google-cloud api tracing', function(done) { + common.runInTransaction(agent, function(endRootSpan) { + speech.recognize('./index.js', { + encoding: 'LINEAR16', + sampleRate: 16000 + }, function(err, res) { + endRootSpan(); + // Authentication will fail due to invalid credentials but a span will still be + // generated. + assert.equal(err.message, 'invalid_client'); + assert.equal(err.code, 16); + var span = common.getMatchingSpan(agent, function(span) { + return span.kind === 'RPC_CLIENT' && span.name.indexOf('grpc:') === 0; + }); + assert.ok(span); + assert.equal(span.name, 'grpc:/google.cloud.speech.v1beta1.Speech/SyncRecognize'); + done(); + }); + }); + }); + +}); diff --git a/test/test-config-plugins.js b/test/test-config-plugins.js index 348cc74a0..342a999be 100644 --- a/test/test-config-plugins.js +++ b/test/test-config-plugins.js @@ -21,7 +21,7 @@ var trace = require('..'); var common = require('./plugins/common.js'); -var instrumentedModules = ['connect', 'express', 'grpc', 'hapi', 'http', 'koa', +var instrumentedModules = ['connect', 'express', 'google-gax', 'grpc', 'hapi', 'http', 'koa', 'mongodb-core', 'mysql', 'redis', 'restify']; describe('plugin configuration', function() {