Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gRPC example using proto-loader #385

Merged
merged 4 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions examples/grpc_dynamic_codegen/README.md
Original file line number Diff line number Diff line change
@@ -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: <https://opentelemetry.io/>
- For more information on OpenTelemetry for Node.js, visit: <https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node-sdk>

## LICENSE

Apache License 2.0
50 changes: 50 additions & 0 deletions examples/grpc_dynamic_codegen/capitalize_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'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'));
mayurkale22 marked this conversation as resolved.
Show resolved Hide resolved
// display traceid in the terminal
console.log(`traceid: ${span.context().traceId}`);
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();
53 changes: 53 additions & 0 deletions examples/grpc_dynamic_codegen/capitalize_server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict';

const { SpanKind } = require('@opentelemetry/types');
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: SpanKind.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();
45 changes: 45 additions & 0 deletions examples/grpc_dynamic_codegen/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "grpc-dynamic-codegen-example",
"version": "0.1.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/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"
},
"homepage": "https://github.com/open-telemetry/opentelemetry-js#readme",
"devDependencies": {
"cross-env": "^6.0.0"
}
}
19 changes: 19 additions & 0 deletions examples/grpc_dynamic_codegen/protos/defs.proto
Original file line number Diff line number Diff line change
@@ -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;
mayurkale22 marked this conversation as resolved.
Show resolved Hide resolved
}
42 changes: 42 additions & 0 deletions examples/grpc_dynamic_codegen/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

const opentelemetry = require('@opentelemetry/core');
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 || '';

function setupTracerAndExporters(service) {
const tracer = new NodeTracer({
plugins: {
grpc: {
enabled: true,
// You may use a package name or absolute path to the file.
path: '@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
});
}

// It is recommended to use this `BatchSpanProcessor` for better performance
// and optimization, especially in production.
tracer.addSpanProcessor(new SimpleSpanProcessor(exporter));
mayurkale22 marked this conversation as resolved.
Show resolved Hide resolved

// Initialize the OpenTelemetry APIs to use the BasicTracer bindings
opentelemetry.initGlobalTracer(tracer);
}

exports.setupTracerAndExporters = setupTracerAndExporters;