diff --git a/packages/datadog-plugin-grpc/src/util.js b/packages/datadog-plugin-grpc/src/util.js index 5f5f4fc6683..0512c0eb19e 100644 --- a/packages/datadog-plugin-grpc/src/util.js +++ b/packages/datadog-plugin-grpc/src/util.js @@ -25,6 +25,8 @@ module.exports = { }, addMetadataTags (span, metadata, filter, type) { + if (!metadata) return + const values = filter(metadata.getMap()) for (const key in values) { diff --git a/packages/datadog-plugin-grpc/test/client.spec.js b/packages/datadog-plugin-grpc/test/client.spec.js index 73e1a164b53..ede72caf617 100644 --- a/packages/datadog-plugin-grpc/test/client.spec.js +++ b/packages/datadog-plugin-grpc/test/client.spec.js @@ -13,8 +13,9 @@ describe('Plugin', () => { let port let server let tracer + let loader - function buildClient (service) { + function buildClient (service, ClientService) { service = Object.assign({ getBidi: () => {}, getServerStream: () => {}, @@ -22,7 +23,8 @@ describe('Plugin', () => { getUnary: () => {} }, service) - const loader = require('../../../versions/@grpc/proto-loader').get() + loader = require('../../../versions/@grpc/proto-loader').get() + const definition = loader.loadSync(`${__dirname}/test.proto`) const TestService = grpc.loadPackageDefinition(definition).test.TestService @@ -32,7 +34,9 @@ describe('Plugin', () => { server.addService(TestService.service, service) server.start() - return new TestService(`localhost:${port}`, grpc.credentials.createInsecure()) + ClientService = ClientService || TestService + + return new ClientService(`localhost:${port}`, grpc.credentials.createInsecure()) } describe('grpc/client', () => { @@ -260,6 +264,36 @@ describe('Plugin', () => { client.getUnary({ first: 'foobar' }, () => {}) }) + it('should handle protocol errors', done => { + const definition = loader.loadSync(`${__dirname}/invalid.proto`) + const test = grpc.loadPackageDefinition(definition).test + const client = buildClient({ + getUnary: (_, callback) => callback(null) + }, test.TestService) + + agent + .use(traces => { + expect(traces[0][0]).to.have.property('error', 1) + expect(traces[0][0].meta).to.include({ + 'error.msg': '13 INTERNAL: Failed to parse server response', + 'error.type': 'Error', + 'grpc.method.name': 'getUnary', + 'grpc.method.service': 'TestService', + 'grpc.method.package': 'test', + 'grpc.method.path': '/test.TestService/getUnary', + 'grpc.method.kind': kinds.unary, + 'grpc.status.code': '13', + 'span.kind': 'client', + 'component': 'grpc' + }) + expect(traces[0][0].meta).to.have.property('error.stack') + }) + .then(done) + .catch(done) + + client.getUnary({ first: 'foobar' }, () => {}) + }) + it('should handle a missing callback', done => { const client = buildClient({ getUnary: (_, callback) => callback() diff --git a/packages/datadog-plugin-grpc/test/invalid.proto b/packages/datadog-plugin-grpc/test/invalid.proto new file mode 100644 index 00000000000..5ea8339aab0 --- /dev/null +++ b/packages/datadog-plugin-grpc/test/invalid.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package test; + +service TestService { + rpc get_Bidi (stream Request) returns (stream Response) {} // test rename support + rpc getServerStream (Request) returns (stream Response) {} + rpc getClientStream (stream Request) returns (Response) {} + rpc getUnary (Request) returns (Response) {} +} + +message Request { + string first = 1; + int32 second = 2; +} + +message Response { + required string first = 1; +} diff --git a/packages/datadog-plugin-grpc/test/test.proto b/packages/datadog-plugin-grpc/test/test.proto index f58442eef9b..5ab59fa077c 100644 --- a/packages/datadog-plugin-grpc/test/test.proto +++ b/packages/datadog-plugin-grpc/test/test.proto @@ -3,10 +3,10 @@ syntax = "proto3"; package test; service TestService { - rpc get_Bidi (stream Request) returns (stream Empty) {} // test rename support - rpc getServerStream (Request) returns (stream Empty) {} - rpc getClientStream (stream Request) returns (Empty) {} - rpc getUnary (Request) returns (Empty) {} + rpc get_Bidi (stream Request) returns (stream Response) {} // test rename support + rpc getServerStream (Request) returns (stream Response) {} + rpc getClientStream (stream Request) returns (Response) {} + rpc getUnary (Request) returns (Response) {} } message Request { @@ -14,5 +14,4 @@ message Request { int32 second = 2; } -message Empty { -} +message Response {}