Skip to content

Commit

Permalink
chore: initial commit of grpc-census-example
Browse files Browse the repository at this point in the history
Signed-off-by: Kelly, Niall <[email protected]>
  • Loading branch information
Kelly, Niall authored and Kelly, Niall committed Jun 4, 2020
1 parent d03e05a commit 3b482f4
Show file tree
Hide file tree
Showing 9 changed files with 617 additions and 0 deletions.
130 changes: 130 additions & 0 deletions examples/grpc-census-prop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Introduction

This example uses the same gRPC [defs.proto](./protos/defs.proto) as the
[grpc_dynamic_codegen](../grpc_dynamic_codegen)
example in which a server takes a payload containing bytes and capitalizes them.
In this case we are demonstrating the use of the
[propagator-grpc-census-binary](../../propagators/opentelemetry-propagator-grpc-census-binary)
propagator. The propagator can be useful when communicating with another service
that is already instrumented using OpenCensus.

If both sides of gRPC communication are using OpenTelemetry instrumentation then
the `propagator-grpc-census-binary` propagator isn't required. Context will be
propagated using the `traceparent` header (thanks to the
[HttpTraceContext](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts)
propagator from opentelemetry-core). If there is a mix of OpenCensus and OpenTelemetry
instrumentation then the `propagator-grpc-census-binary` propagator allows OpenTelemetry
to propagate context through the `grpc-trace-bin` binary header.

The same source code is used to run various versions of the client and server. Environment
variables (set up through `scripts` in [package.json](./package.json) determine the various
combinations. This table shows what to expect:

| Combination | Client Instrumentation | Server Instrumentation | Propagation Header |
| :---------- | :--------------------- | :--------------------- | :----------------- |
| 1 | OpenTelemetry (default propagator) | OpenTelemetry (default propagator) | `traceparent` |
| 2 | OpenCensus | OpenTelemetry (**binary propagator**) | `grpc-trace-bin` |
| 3 | OpenCensus | OpenCensus | `grpc-trace-bin` |
| 4 | OpenTelemetry (**binary propagator**) | OpenCensus | `grpc-trace-bin` |

If context propagation is working correctly we should see consistent values
for `traceId` in the output of both the client and server. (Note: the example
uses simple Console Exporters rather than Jaeger or Zipkin). The servers also
output the contents of `grpc.Metadata` which allows us to see the values of
`traceparent` or `grpc-trace-bin` where applicable.

## Installation

```sh
$ # from this directory
$ npm install
```

## Running the Client and Server combinations

### Combination 1

OpenTelemetry (with default propagator) used on both client and server.
Propagation through `traceparent` header.

- Run the server

```sh
$ # from this directory
$ npm run server:otel:defprop
```

- Run the client

```sh
$ # from this directory
$ npm run client:otel:defprop
```

### Combination 2

OpenTelemetry (with **binary propagator**) used on server, OpenCensus used
on client. Propagation through `grpc-trace-bin` header.

- Run the server

```sh
$ # from this directory
$ npm run server:otel:binprop
```

- Run the client

```sh
$ # from this directory
$ npm run client:census
```

See [combination2](./combination2.md) for example output

### Combination 3

OpenCensus used on both client and server. Propagation through `grpc-trace-bin` header.

- Run the server

```sh
$ # from this directory
$ npm run server:census
```

- Run the client

```sh
$ # from this directory
$ npm run client:census
```

### Combination 4

OpenCensus used on server, OpenTelemetry (with **binary propagator**) used on
client. Propagation through `grpc-trace-bin` header.

- Run the server

```sh
$ # from this directory
$ npm run server:census
```

- Run the client

```sh
$ # from this directory
$ npm run client:otel:binprop
```

See [combination4](./combination4.md) for example output

## 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
73 changes: 73 additions & 0 deletions examples/grpc-census-prop/capitalize_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use strict';

const binaryPropagator = process.env.BINARY_PROPAGATOR === 'true' ? true : false;
const censusTracer = process.env.CENSUS_TRACER === 'true' ? true : false;
let tracer;
if (censusTracer) {
tracer = require('./tracer_census')();
} else {
tracer = require('./tracer')('example-grpc-capitalize-client', binaryPropagator);
}

const path = require('path');
const grpc = require('grpc');

const PROTO_PATH = path.join(__dirname, 'protos/defs.proto');

// Even though grpc.load is deprecated in favor of @grpc/proto-loader, it
// appears @opencensus/instrumentation-grpc only gets to set the
// grpc-trace-bin header if we use grpc.load
const Fetch = grpc.load(PROTO_PATH).rpc.Fetch;

function main() {
const client = new Fetch('localhost:50051',
grpc.credentials.createInsecure());
const data = process.argv[2] || 'opentelemetry';
console.log('> ', data);

if (censusTracer) {
capitalizeWithCensusTracing(tracer, client, data);
}
else {
capitalizeWithOTelTracing(tracer, client, data);
}

// 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);
}

function capitalizeWithCensusTracing(tracer, client, data) {
tracer.startRootSpan({name: 'tutorialsClient.capitalize'}, rootSpan => {
client.capitalize({ data: Buffer.from(data) }, (err, response) => {
if (err) {
console.log('could not get grpc response');
rootSpan.end();
return;
}
console.log('< ', response.data.toString('utf8'));

rootSpan.end();
});
});
}

function capitalizeWithOTelTracing(tracer, client, data) {
const span = tracer.startSpan('tutorialsClient.capitalize');
tracer.withSpan(span, () => {
client.capitalize({ data: Buffer.from(data) }, (err, response) => {
if (err) {
console.log('could not get grpc response');
return;
}
console.log('< ', response.data.toString('utf8'));
// display traceid in the terminal
console.log(`traceid: ${span.context().traceId}`);
span.end();
});
});
}

main();
93 changes: 93 additions & 0 deletions examples/grpc-census-prop/capitalize_server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'use strict';

const binaryPropagator = process.env.BINARY_PROPAGATOR === 'true' ? true : false;
const censusTracer = process.env.CENSUS_TRACER === 'true' ? true : false;

let tracer;
let SpanKind;
if (censusTracer) {
tracer = require('./tracer_census')();
SpanKind = require('@opencensus/core').SpanKind;
} else {
tracer = require('./tracer')('example-grpc-capitalize-server', binaryPropagator);
SpanKind = require('@opentelemetry/api').SpanKind;
}

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;

/** Implements the Capitalize RPC method. */
function capitalize(call, callback) {
if (call.metadata) {
// output the gRPC metadata to see headers e.g. traceparent or grpc-trace-bin
console.dir(call.metadata, { depth: null });
}

let capitalized;
if (censusTracer) {
capitalized = capitalizeWithCensusTracing(call);
} else {
capitalized = capitalizeWithOTelTracing(call);
}

callback(null, { data: Buffer.from(capitalized) });
}

function capitalizeWithCensusTracing(call) {
const currentSpan = tracer.currentRootSpan;
// display traceid in the terminal
console.log(`traceid: ${currentSpan.traceId}`);

const span = tracer.startChildSpan({
name: 'tutorials.FetchImpl.capitalize',
kind: SpanKind.SERVER
});

const data = call.request.data.toString('utf8');
const capitalized = data.toUpperCase();
for (let i = 0; i < 100000000; i += 1) {
// empty
}
span.end();
return capitalized;
}

function capitalizeWithOTelTracing(call) {
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 += 1) {
// empty
}
span.end();
return 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 });
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
server.start();
}

main();
95 changes: 95 additions & 0 deletions examples/grpc-census-prop/combination2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Example Output from Combination 2

OpenTelemetry (with **binary propagator**) used on server, OpenCensus used
on client. Propagation through `grpc-trace-bin` header.

## Client

Note: traceId **1565fbb4d6f042d8880bedb509bf6f2e**

```
$ npm run client:census
> [email protected] client:census /opentelemetry-js-contrib/examples/grpc-census-prop
> cross-env CENSUS_TRACER=true node ./capitalize_client.js
> opentelemetry
Sleeping 5 seconds before shutdown to ensure all records are flushed.
(node:14866) DeprecationWarning: grpc.load: Use the @grpc/proto-loader module with grpc.loadPackageDefinition instead
< OPENTELEMETRY
RootSpan: {traceId: 1565fbb4d6f042d8880bedb509bf6f2e, spanId: a5ccb3c920a18ace, name: tutorialsClient.capitalize }
ChildSpans:
{spanId: c92c3b3f955cdce1, name: grpc.rpc.Fetch/Capitalize}
Completed.
```

## Server

Note: traceId **1565fbb4d6f042d8880bedb509bf6f2e**

```
$ npm run server:otel:binprop
> [email protected] server:otel:binprop /opentelemetry-js-contrib/examples/grpc-census-prop
> cross-env BINARY_PROPAGATOR=true node ./capitalize_server.js
PluginLoader#load: trying to load [email protected]
Metadata {
_internal_repr:
{ 'grpc-trace-bin':
[ Buffer [Uint8Array] [
0,
0,
21,
101,
251,
180,
214,
240,
66,
216,
136,
11,
237,
181,
9,
191,
111,
46,
1,
201,
44,
59,
63,
149,
92,
220,
225,
2,
1 ] ],
'user-agent': [ 'grpc-node/1.24.2 grpc-c/8.0.0 (linux; chttp2; ganges)' ] },
flags: 0 }
traceid: 1565fbb4d6f042d8880bedb509bf6f2e
{ traceId: '1565fbb4d6f042d8880bedb509bf6f2e',
parentId: '891bd1ebb5e44ec8',
name: 'tutorials.FetchImpl.capitalize',
id: '61a1c3bce0364fe2',
kind: 1,
timestamp: 1591295467649290,
duration: 101304,
attributes: {},
status: { code: 0 },
events: [] }
{ traceId: '1565fbb4d6f042d8880bedb509bf6f2e',
parentId: 'c92c3b3f955cdce1',
name: 'grpc.rpc.Fetch/Capitalize',
id: '891bd1ebb5e44ec8',
kind: 1,
timestamp: 1591295467645184,
duration: 106431,
attributes:
{ 'grpc.kind': 1, component: 'grpc', 'grpc.status_code': '0' },
status: { code: 0 },
events:
[ { name: 'received', attributes: undefined, time: [Array] } ] }
```
Loading

0 comments on commit 3b482f4

Please sign in to comment.