From f4d85ba4d67ed9f84f161a3910150f244b2ea4b1 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Tue, 1 Oct 2019 15:28:58 -0700 Subject: [PATCH 1/4] chore: gRPC example using proto-loader --- examples/grpc_dynamic_codegen/README.md | 60 +++++++++++++++++++ .../grpc_dynamic_codegen/capitalize_client.js | 48 +++++++++++++++ .../grpc_dynamic_codegen/capitalize_server.js | 52 ++++++++++++++++ examples/grpc_dynamic_codegen/package.json | 44 ++++++++++++++ .../grpc_dynamic_codegen/protos/defs.proto | 19 ++++++ examples/grpc_dynamic_codegen/setup.js | 40 +++++++++++++ 6 files changed, 263 insertions(+) create mode 100644 examples/grpc_dynamic_codegen/README.md create mode 100644 examples/grpc_dynamic_codegen/capitalize_client.js create mode 100644 examples/grpc_dynamic_codegen/capitalize_server.js create mode 100644 examples/grpc_dynamic_codegen/package.json create mode 100644 examples/grpc_dynamic_codegen/protos/defs.proto create mode 100644 examples/grpc_dynamic_codegen/setup.js diff --git a/examples/grpc_dynamic_codegen/README.md b/examples/grpc_dynamic_codegen/README.md new file mode 100644 index 0000000000..a28d1c4238 --- /dev/null +++ b/examples/grpc_dynamic_codegen/README.md @@ -0,0 +1,60 @@ +# Overview + +Our service takes in a payload containing bytes and capitalizes them. + +Using OpenTelemetry gRPC Instrumentation, we can collect traces of our system and export them to the backend of our choice (we can use Zipkin or Jaeger for this example), to give observability to our distributed systems. + +> This is the dynamic code generation variant of the gRPC examples. Code in these examples is generated at runtime using Protobuf.js. + +## Installation + +```sh +$ # from this directory +$ npm install +``` + +Setup [Zipkin Tracing](https://zipkin.io/pages/quickstart.html) +or +Setup [Jaeger Tracing](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one) + +## Run the Application + +### Zipkin + + - Run the server + + ```sh + $ # from this directory + $ npm run zipkin:server + ``` + + - Run the client + + ```sh + $ # from this directory + $ npm run zipkin:client + ``` + +### Jaeger + + - Run the server + + ```sh + $ # from this directory + $ npm run jaeger:server + ``` + + - Run the client + + ```sh + $ # from this directory + $ npm run jaeger:client + ``` + +## Useful links +- For more information on OpenTelemetry, visit: +- For more information on OpenTelemetry for Node.js, visit: + +## LICENSE + +Apache License 2.0 diff --git a/examples/grpc_dynamic_codegen/capitalize_client.js b/examples/grpc_dynamic_codegen/capitalize_client.js new file mode 100644 index 0000000000..23614f879d --- /dev/null +++ b/examples/grpc_dynamic_codegen/capitalize_client.js @@ -0,0 +1,48 @@ +'use strict'; + +const opentelemetry = require('@opentelemetry/core'); +const config = require('./setup'); + +/** + * The trace instance needs to be initialized first, if you want to enable + * automatic tracing for built-in plugins (gRPC in this case). + */ +config.setupTracerAndExporters('grpc-client-service'); + +const path = require('path'); +const grpc = require('grpc'); +const protoLoader = require('@grpc/proto-loader'); + +const tracer = opentelemetry.getTracer(); + +const PROTO_PATH = path.join(__dirname, 'protos/defs.proto'); +const PROTO_OPTIONS = { keepCase: true, enums: String, defaults: true, oneofs: true }; +const definition = protoLoader.loadSync(PROTO_PATH, PROTO_OPTIONS); +const rpcProto = grpc.loadPackageDefinition(definition).rpc; + +function main () { + const client = new rpcProto.Fetch('localhost:50051', + grpc.credentials.createInsecure()); + const data = process.argv[2] || 'opentelemetry'; + console.log('> ', data); + + const span = tracer.startSpan('tutorialsClient.capitalize'); + tracer.withSpan(span, () => { + client.capitalize({ data: Buffer.from(data) }, function (err, response) { + if (err) { + console.log('could not get grpc response'); + return; + } + console.log('< ', response.data.toString('utf8')); + span.end(); + }); + }); + + // The process must live for at least the interval past any traces that + // must be exported, or some risk being lost if they are recorded after the + // last export. + console.log('Sleeping 5 seconds before shutdown to ensure all records are flushed.') + setTimeout(() => { console.log('Completed.'); }, 5000); +} + +main(); diff --git a/examples/grpc_dynamic_codegen/capitalize_server.js b/examples/grpc_dynamic_codegen/capitalize_server.js new file mode 100644 index 0000000000..5896f7e537 --- /dev/null +++ b/examples/grpc_dynamic_codegen/capitalize_server.js @@ -0,0 +1,52 @@ +'use strict'; + +const opentelemetry = require('@opentelemetry/core'); + +/** + * The trace instance needs to be initialized first, if you want to enable + * automatic tracing for built-in plugins (gRPC in this case). + */ +const config = require('./setup'); +config.setupTracerAndExporters('grpc-server-service'); + +const path = require('path'); +const grpc = require('grpc'); +const protoLoader = require('@grpc/proto-loader'); + +const PROTO_PATH = path.join(__dirname, 'protos/defs.proto'); +const PROTO_OPTIONS = { keepCase: true, enums: String, defaults: true, oneofs: true }; +const definition = protoLoader.loadSync(PROTO_PATH, PROTO_OPTIONS); +const rpcProto = grpc.loadPackageDefinition(definition).rpc; + +const tracer = opentelemetry.getTracer(); + +/** Implements the Capitalize RPC method. */ +function capitalize (call, callback) { + const currentSpan = tracer.getCurrentSpan(); + // display traceid in the terminal + console.log(`traceid: ${currentSpan.context().traceId}`); + + const span = tracer.startSpan('tutorials.FetchImpl.capitalize', { + parent: currentSpan, + kind: 1, // server + }); + + const data = call.request.data.toString('utf8'); + const capitalized = data.toUpperCase(); + for (let i = 0; i < 100000000; i++) {} + span.end(); + callback(null, { data: Buffer.from(capitalized) }); +} + +/** + * Starts an RPC server that receives requests for the Fetch service at the + * sample server port. + */ +function main () { + const server = new grpc.Server(); + server.addService(rpcProto.Fetch.service, { capitalize: capitalize }); + server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); + server.start(); +} + +main(); diff --git a/examples/grpc_dynamic_codegen/package.json b/examples/grpc_dynamic_codegen/package.json new file mode 100644 index 0000000000..8a414635be --- /dev/null +++ b/examples/grpc_dynamic_codegen/package.json @@ -0,0 +1,44 @@ +{ + "name": "grpc-dynamic-codegen-example", + "version": "0.0.1", + "description": "Example of gRPC integration with OpenTelemetry", + "main": "index.js", + "scripts": { + "zipkin:server": "cross-env EXPORTER=zipkin node ./capitalize_server.js", + "zipkin:client": "cross-env EXPORTER=zipkin node ./capitalize_client.js", + "jaeger:server": "cross-env EXPORTER=jaeger node ./capitalize_server.js", + "jaeger:client": "cross-env EXPORTER=jaeger node ./capitalize_client.js" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git" + }, + "keywords": [ + "opentelemetry", + "grpc", + "tracing" + ], + "engines": { + "node": ">=8" + }, + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/open-telemetry/opentelemetry-js/issues" + }, + "dependencies": { + "@grpc/proto-loader": "^0.4.0", + "@opentelemetry/tracer-basic": "^0.0.1", + "@opentelemetry/core": "^0.0.1", + "@opentelemetry/exporter-jaeger": "^0.0.1", + "@opentelemetry/exporter-zipkin": "^0.0.1", + "@opentelemetry/node-sdk": "^0.0.1", + "@opentelemetry/plugin-grpc": "^0.0.1", + "grpc": "^1.23.3", + "node-pre-gyp": "0.12.0" + }, + "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", + "devDependencies": { + "cross-env": "^6.0.0" + } +} diff --git a/examples/grpc_dynamic_codegen/protos/defs.proto b/examples/grpc_dynamic_codegen/protos/defs.proto new file mode 100644 index 0000000000..cc4a40334c --- /dev/null +++ b/examples/grpc_dynamic_codegen/protos/defs.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package rpc; + +service Fetch { + // Sends a capitalizes payload + rpc Capitalize(Payload) returns (Payload) {} +} + +// The request and response payload containing the id and data. +message Payload { + int32 id = 1; + bytes data = 2; +} diff --git a/examples/grpc_dynamic_codegen/setup.js b/examples/grpc_dynamic_codegen/setup.js new file mode 100644 index 0000000000..7b3f5ec15a --- /dev/null +++ b/examples/grpc_dynamic_codegen/setup.js @@ -0,0 +1,40 @@ +'use strict'; + +const opentelemetry = require('@opentelemetry/core'); +const { NodeTracer } = require('@opentelemetry/node-sdk'); +const { SimpleSpanProcessor } = require('@opentelemetry/tracer-basic'); +const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); +const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); +const EXPORTER = process.env.EXPORTER || ''; + +function setupTracerAndExporters(service) { + const tracer = new NodeTracer({ + plugins: { + grpc: { + enabled: true, + // if it can't find the module, put the absolute path since the packages are not published yet + path: '/Users/mayurkale/gcode/opentelemetry-node/packages/opentelemetry-plugin-grpc' + } + } + }); + + let exporter; + if (EXPORTER.toLowerCase().startsWith('z')) { + exporter = new ZipkinExporter({ + serviceName: service, + }); + } else { + exporter = new JaegerExporter({ + serviceName: service, + // The default flush interval is 5 seconds. + flushInterval: 2000 + }); + } + + tracer.addSpanProcessor(new SimpleSpanProcessor(exporter)); + + // Initialize the OpenTelemetry APIs to use the BasicTracer bindings + opentelemetry.initGlobalTracer(tracer); +} + +exports.setupTracerAndExporters = setupTracerAndExporters; From 44e528c770bb988266f4edce30dfbaf5f309955c Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Tue, 1 Oct 2019 15:31:13 -0700 Subject: [PATCH 2/4] chore: gRPC example using proto-loader --- examples/grpc_dynamic_codegen/setup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/grpc_dynamic_codegen/setup.js b/examples/grpc_dynamic_codegen/setup.js index 7b3f5ec15a..ac789f4d7c 100644 --- a/examples/grpc_dynamic_codegen/setup.js +++ b/examples/grpc_dynamic_codegen/setup.js @@ -12,8 +12,8 @@ function setupTracerAndExporters(service) { plugins: { grpc: { enabled: true, - // if it can't find the module, put the absolute path since the packages are not published yet - path: '/Users/mayurkale/gcode/opentelemetry-node/packages/opentelemetry-plugin-grpc' + // You may use a package name or absolute path to the file. + path: '@opentelemetry/plugin-grpc' } } }); From d88714cf584ac0062da69637472b06f4c9d218ac Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Fri, 18 Oct 2019 11:08:01 -0700 Subject: [PATCH 3/4] fix: review comments --- examples/grpc_dynamic_codegen/capitalize_client.js | 4 +++- examples/grpc_dynamic_codegen/capitalize_server.js | 7 ++++--- examples/grpc_dynamic_codegen/package.json | 1 + examples/grpc_dynamic_codegen/setup.js | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/grpc_dynamic_codegen/capitalize_client.js b/examples/grpc_dynamic_codegen/capitalize_client.js index 23614f879d..0b7b360f0e 100644 --- a/examples/grpc_dynamic_codegen/capitalize_client.js +++ b/examples/grpc_dynamic_codegen/capitalize_client.js @@ -20,7 +20,7 @@ const PROTO_OPTIONS = { keepCase: true, enums: String, defaults: true, oneofs: t const definition = protoLoader.loadSync(PROTO_PATH, PROTO_OPTIONS); const rpcProto = grpc.loadPackageDefinition(definition).rpc; -function main () { +function main() { const client = new rpcProto.Fetch('localhost:50051', grpc.credentials.createInsecure()); const data = process.argv[2] || 'opentelemetry'; @@ -34,6 +34,8 @@ function main () { return; } console.log('< ', response.data.toString('utf8')); + // display traceid in the terminal + console.log(`traceid: ${span.context().traceId}`); span.end(); }); }); diff --git a/examples/grpc_dynamic_codegen/capitalize_server.js b/examples/grpc_dynamic_codegen/capitalize_server.js index 5896f7e537..c18731d4c7 100644 --- a/examples/grpc_dynamic_codegen/capitalize_server.js +++ b/examples/grpc_dynamic_codegen/capitalize_server.js @@ -1,5 +1,6 @@ 'use strict'; +const { SpanKind } = require('@opentelemetry/types'); const opentelemetry = require('@opentelemetry/core'); /** @@ -21,14 +22,14 @@ const rpcProto = grpc.loadPackageDefinition(definition).rpc; const tracer = opentelemetry.getTracer(); /** Implements the Capitalize RPC method. */ -function capitalize (call, callback) { +function capitalize(call, callback) { const currentSpan = tracer.getCurrentSpan(); // display traceid in the terminal console.log(`traceid: ${currentSpan.context().traceId}`); const span = tracer.startSpan('tutorials.FetchImpl.capitalize', { parent: currentSpan, - kind: 1, // server + kind: SpanKind.SERVER, }); const data = call.request.data.toString('utf8'); @@ -42,7 +43,7 @@ function capitalize (call, callback) { * Starts an RPC server that receives requests for the Fetch service at the * sample server port. */ -function main () { +function main() { const server = new grpc.Server(); server.addService(rpcProto.Fetch.service, { capitalize: capitalize }); server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); diff --git a/examples/grpc_dynamic_codegen/package.json b/examples/grpc_dynamic_codegen/package.json index 8a414635be..7c81fa6415 100644 --- a/examples/grpc_dynamic_codegen/package.json +++ b/examples/grpc_dynamic_codegen/package.json @@ -29,6 +29,7 @@ "dependencies": { "@grpc/proto-loader": "^0.4.0", "@opentelemetry/tracer-basic": "^0.0.1", + "@opentelemetry/types": "^0.0.1", "@opentelemetry/core": "^0.0.1", "@opentelemetry/exporter-jaeger": "^0.0.1", "@opentelemetry/exporter-zipkin": "^0.0.1", diff --git a/examples/grpc_dynamic_codegen/setup.js b/examples/grpc_dynamic_codegen/setup.js index ac789f4d7c..f83789bebf 100644 --- a/examples/grpc_dynamic_codegen/setup.js +++ b/examples/grpc_dynamic_codegen/setup.js @@ -31,6 +31,8 @@ function setupTracerAndExporters(service) { }); } + // It is recommended to use this `BatchSpanProcessor` for better performance + // and optimization, especially in production. tracer.addSpanProcessor(new SimpleSpanProcessor(exporter)); // Initialize the OpenTelemetry APIs to use the BasicTracer bindings From 29bd172eb0269e499db478bc676dfba4a04eb4c8 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Fri, 18 Oct 2019 13:34:02 -0700 Subject: [PATCH 4/4] fix: we use latest OT package versions --- examples/grpc_dynamic_codegen/package.json | 16 ++++++++-------- examples/grpc_dynamic_codegen/setup.js | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/grpc_dynamic_codegen/package.json b/examples/grpc_dynamic_codegen/package.json index 7c81fa6415..60cdfaba0b 100644 --- a/examples/grpc_dynamic_codegen/package.json +++ b/examples/grpc_dynamic_codegen/package.json @@ -1,6 +1,6 @@ { "name": "grpc-dynamic-codegen-example", - "version": "0.0.1", + "version": "0.1.1", "description": "Example of gRPC integration with OpenTelemetry", "main": "index.js", "scripts": { @@ -28,13 +28,13 @@ }, "dependencies": { "@grpc/proto-loader": "^0.4.0", - "@opentelemetry/tracer-basic": "^0.0.1", - "@opentelemetry/types": "^0.0.1", - "@opentelemetry/core": "^0.0.1", - "@opentelemetry/exporter-jaeger": "^0.0.1", - "@opentelemetry/exporter-zipkin": "^0.0.1", - "@opentelemetry/node-sdk": "^0.0.1", - "@opentelemetry/plugin-grpc": "^0.0.1", + "@opentelemetry/tracing": "^0.1.1", + "@opentelemetry/types": "^0.1.1", + "@opentelemetry/core": "^0.1.1", + "@opentelemetry/exporter-jaeger": "^0.1.1", + "@opentelemetry/exporter-zipkin": "^0.1.1", + "@opentelemetry/node": "^0.1.1", + "@opentelemetry/plugin-grpc": "^0.1.1", "grpc": "^1.23.3", "node-pre-gyp": "0.12.0" }, diff --git a/examples/grpc_dynamic_codegen/setup.js b/examples/grpc_dynamic_codegen/setup.js index f83789bebf..5ac2a903e9 100644 --- a/examples/grpc_dynamic_codegen/setup.js +++ b/examples/grpc_dynamic_codegen/setup.js @@ -1,8 +1,8 @@ 'use strict'; const opentelemetry = require('@opentelemetry/core'); -const { NodeTracer } = require('@opentelemetry/node-sdk'); -const { SimpleSpanProcessor } = require('@opentelemetry/tracer-basic'); +const { NodeTracer } = require('@opentelemetry/node'); +const { SimpleSpanProcessor } = require('@opentelemetry/tracing'); const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); const EXPORTER = process.env.EXPORTER || '';