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

feat: Opentelemetry integration #1078

Merged
merged 36 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
af0a95c
Add opentelemetry tracing
sethmaxwl Jul 27, 2020
73835fb
build: rename _toc to toc (#1066)
yoshi-automation Jul 23, 2020
ae1ae81
build: move gitattributes files to node templates (#1070)
yoshi-automation Jul 24, 2020
f8b6602
Add opentelemetry instrumentation
sethmaxwl Aug 4, 2020
c64b1ce
Merge branch 'master' of https://github.com/googleapis/nodejs-pubsub …
sethmaxwl Aug 4, 2020
a8ede4b
Add create span test
sethmaxwl Aug 5, 2020
aec8a5c
Refactor tracing
sethmaxwl Aug 6, 2020
dbe8cdf
Add publisher key test
sethmaxwl Aug 7, 2020
d8ebcf7
Fix linting issues
sethmaxwl Aug 7, 2020
16b9aaa
Add docs
sethmaxwl Aug 7, 2020
e93dceb
Add example for opentelemetry
sethmaxwl Aug 7, 2020
fe14163
Add tracing example
sethmaxwl Aug 7, 2020
11478a7
Update headers
sethmaxwl Aug 7, 2020
3cb49e9
Merge branch 'master' into opentelemetry-integration
bcoe Aug 8, 2020
4c4482c
Merge branch 'master' into opentelemetry-integration
sethmaxwl Aug 10, 2020
dd42a6a
Add microsoft api documenter
sethmaxwl Aug 10, 2020
59b6f62
Merge branch 'master' into opentelemetry-integration
feywind Aug 10, 2020
78ce7f6
Fix linting in samples/package.json
sethmaxwl Aug 10, 2020
99a1fe1
Merge branch 'opentelemetry-integration' of https://github.com/sethma…
sethmaxwl Aug 10, 2020
5ccd513
Merge branch 'master' into opentelemetry-integration
sethmaxwl Aug 10, 2020
39ec18a
Merge branch 'master' into opentelemetry-integration
sethmaxwl Aug 11, 2020
0ad9443
Add optional tracing
sethmaxwl Aug 12, 2020
d4996b9
Merge branch 'opentelemetry-integration' of https://github.com/sethma…
sethmaxwl Aug 12, 2020
0606f83
Fix linting issues
sethmaxwl Aug 12, 2020
c9cd570
Re-add api-documenter
sethmaxwl Aug 12, 2020
c974091
Update package.json
sethmaxwl Aug 12, 2020
e343c52
Update package.json
sethmaxwl Aug 12, 2020
00c0980
Update package.json
sethmaxwl Aug 12, 2020
eccd6eb
Fix docs
sethmaxwl Aug 12, 2020
92c3a09
Merge branch 'opentelemetry-integration' of https://github.com/sethma…
sethmaxwl Aug 12, 2020
f7369e8
Merge branch 'master' into opentelemetry-integration
feywind Aug 12, 2020
c7cc67d
Add more unit tests
sethmaxwl Aug 13, 2020
af46e32
Merge branch 'opentelemetry-integration' of https://github.com/sethma…
sethmaxwl Aug 13, 2020
5fa6274
Fix linting
sethmaxwl Aug 13, 2020
ce35b88
Add disable tracing tests
sethmaxwl Aug 13, 2020
d5ac567
Update opentelemetryTracing sample
sethmaxwl Aug 13, 2020
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
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"presystem-test": "npm run compile",
"system-test": "mocha build/system-test --timeout 600000",
"samples-test": "cd samples/ && npm link ../ && npm install && npm test && cd ../",
"test": "c8 mocha build/test",
"test": "c8 mocha build/test --recursive",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've got a change pending actually that will obsolete this, but I will deal with it when that gets merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added it here to get CI to run the publisher tests too.

"lint": "gts check",
"predocs": "npm run compile",
"docs": "jsdoc -c .jsdoc.js",
Expand All @@ -44,15 +44,17 @@
"predocs-test": "npm run docs",
"benchwrapper": "node bin/benchwrapper.js",
"prelint": "cd samples; npm link ../; npm install",
"precompile": "gts clean",
"api-extractor": "api-extractor run --local",
"api-documenter": "api-documenter yaml --input-folder=temp"
"api-documenter": "api-documenter yaml --input-folder=temp",
"precompile": "gts clean"
},
"dependencies": {
"@google-cloud/paginator": "^3.0.0",
"@google-cloud/precise-date": "^2.0.0",
"@google-cloud/projectify": "^2.0.0",
"@google-cloud/promisify": "^2.0.0",
"@opentelemetry/api": "^0.9.0",
"@opentelemetry/tracing": "^0.9.0",
"@types/duplexify": "^3.6.0",
"@types/long": "^4.0.0",
"arrify": "^2.0.0",
Expand Down
108 changes: 108 additions & 0 deletions samples/opentelemetryTracing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*!
* Copyright 2020 Google LLC
*
* 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.
*/

/**
* This sample demonstrates how to add OpenTelemetry tracing to the
* Google Cloud Pub/Sub API.
*
* For more information, see the README.md under /pubsub and the documentation
* at https://cloud.google.com/pubsub/docs.
*/

'use strict';

// sample-metadata:
// title: OpenTelemetry Tracing
// description: Demonstrates how to enable OpenTelemetry tracing in
// a publisher or subscriber.
// usage: node opentelemetryTracing.js <topic-name> <subscription-name>

const SUBSCRIBER_TIMEOUT = 10;

function main(
topicName = 'YOUR_TOPIC_NAME',
subscriptionName = 'YOUR_SUBSCRIPTION_NAME',
data = {foo: 'bar'}
) {
// [START opentelemetry_tracing]
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
// const topicName = 'my-topic';
// const subscriptionName = 'my-subscription';
// const data = 'Hello, world!";

// Imports the Google Cloud client library
const {PubSub} = require('@google-cloud/pubsub');

// Imports the OpenTelemetry API
const {opentelemetry} = require('@opentelemetry/api');

// Imports the OpenTelemetry span handlers and exporter
const {
SimpleSpanProcessor,
BasicTracerProvider,
ConsoleSpanExporter,
} = require('@opentelemetry/tracing');

// Set up span processing and specify the console as the span exporter
const provider = new BasicTracerProvider();
const exporter = new ConsoleSpanExporter();
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));

provider.register();
opentelemetry.trace.setGlobalTracerProvider(provider);

// OpenTelemetry tracing is an optional feature and can be enabled by setting
// enableOpenTelemetryTraceing as a publisher or subscriber option
const enableOpenTelemetryTracing = {
enableOpenTelemetryTracing: true,
};

// Creates a client; cache this for further use
const pubSubClient = new PubSub();

async function publishMessage() {
// Publishes the message as a string, e.g. "Hello, world!" or JSON.stringify(someObject)
const dataBuffer = Buffer.from(data);

const messageId = await pubSubClient
.topic(topicName, enableOpenTelemetryTracing)
.publish(dataBuffer);
console.log(`Message ${messageId} published.`);
}

async function subscriptionListen() {
// Message handler for subscriber
const messageHandler = message => {
console.log(`Message ${message.id} received.`);
message.ack();
};

// Listens for new messages from the topic
pubSubClient.subscription(subscriptionName).on('message', messageHandler);
setTimeout(() => {
pubSubClient
.subscription(subscriptionName, enableOpenTelemetryTracing)
.removeAllListeners();
}, SUBSCRIBER_TIMEOUT * 1000);
}

publishMessage().then(subscriptionListen());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing a doc end tag, though it may not matter at this point if nothing in the docsite is expecting it to be here. (We would need to canonicalize the the tag name anyway.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to include a link to this example in the blog post. How should we canonicalize the tag name?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see if I can find the right person to ask... there were some changes in that area lately.

// [END opentelemetry_tracing]
}

main(...process.argv.slice(2));
2 changes: 2 additions & 0 deletions samples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"test": "mocha system-test --timeout 600000"
},
"dependencies": {
"@opentelemetry/api": "^0.10.2",
sethmaxwl marked this conversation as resolved.
Show resolved Hide resolved
"@opentelemetry/tracing": "^0.10.2",
"@google-cloud/pubsub": "^2.4.0"
},
"devDependencies": {
Expand Down
43 changes: 43 additions & 0 deletions src/opentelemetry-tracing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*!
* Copyright 2020 Google LLC
* 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.
*/

import {Attributes, SpanContext, Span, trace} from '@opentelemetry/api';
import {Tracer} from '@opentelemetry/tracing';

/**
* Wrapper for creating OpenTelemetry Spans
*
* @class
*/
export class OpenTelemetryTracer {
/**
* Creates a new span with the given properties
*
* @param {string} spanName the name for the span
* @param {Attributes?} attributes an object containing the attributes to be set for the span
* @param {SpanContext?} parent the context of the parent span to link to the span
*/
createSpan(
spanName: string,
attributes?: Attributes,
parent?: SpanContext
): Span {
const tracerProvider: Tracer = trace.getTracer('default') as Tracer;
return tracerProvider.startSpan(spanName, {
parent: parent,
attributes: attributes,
});
}
}
69 changes: 63 additions & 6 deletions src/publisher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
import {promisify, promisifyAll} from '@google-cloud/promisify';
import * as extend from 'extend';
import {CallOptions} from 'google-gax';
import {Span} from '@opentelemetry/api';

import {BatchPublishOptions} from './message-batch';
import {Queue, OrderedQueue} from './message-queues';
import {Topic} from '../topic';
import {RequestCallback, EmptyCallback} from '../pubsub';
import {google} from '../../protos/protos';
import {defaultOptions} from '../default-options';
import {OpenTelemetryTracer} from '../opentelemetry-tracing';

export type PubsubMessage = google.pubsub.v1.IPubsubMessage;

Expand All @@ -37,6 +39,7 @@ export interface PublishOptions {
batching?: BatchPublishOptions;
gaxOpts?: CallOptions;
messageOrdering?: boolean;
enableOpenTelemetryTracing?: boolean;
}

/**
Expand Down Expand Up @@ -72,11 +75,16 @@ export class Publisher {
settings!: PublishOptions;
queue: Queue;
orderedQueues: Map<string, OrderedQueue>;
tracing: OpenTelemetryTracer | undefined;
constructor(topic: Topic, options?: PublishOptions) {
this.setOptions(options);
this.topic = topic;
this.queue = new Queue(this);
this.orderedQueues = new Map();
this.tracing =
this.settings && this.settings.enableOpenTelemetryTracing
? new OpenTelemetryTracer()
: undefined;
}

flush(): Promise<void>;
Expand Down Expand Up @@ -162,8 +170,13 @@ export class Publisher {
}
}

const span: Span | undefined = this.constructSpan(message);

if (!message.orderingKey) {
this.queue.add(message, callback);
if (span) {
span.end();
}
return;
}

Expand All @@ -177,6 +190,10 @@ export class Publisher {

const queue = this.orderedQueues.get(key)!;
queue.add(message, callback);

if (span) {
span.end();
}
}
/**
* Indicates to the publisher that it is safe to continue publishing for the
Expand Down Expand Up @@ -211,13 +228,19 @@ export class Publisher {
gaxOpts: {
isBundling: false,
},
enableOpenTelemetryTracing: false,
};

const {batching, gaxOpts, messageOrdering} = extend(
true,
defaults,
options
);
const {
batching,
gaxOpts,
messageOrdering,
enableOpenTelemetryTracing,
} = extend(true, defaults, options);

this.tracing = enableOpenTelemetryTracing
? new OpenTelemetryTracer()
: undefined;

this.settings = {
batching: {
Expand All @@ -227,11 +250,45 @@ export class Publisher {
},
gaxOpts,
messageOrdering,
enableOpenTelemetryTracing,
};
}

/**
* Constructs an OpenTelemetry span
*
* @private
*
* @param {PubsubMessage} message The message to create a span for
*/
constructSpan(message: PubsubMessage): Span | undefined {
const spanAttributes = {
data: message.data,
};
const span: Span | undefined = this.tracing
? this.tracing.createSpan(`${this.topic.name} publisher`, spanAttributes)
: undefined;
if (span) {
if (
message.attributes &&
message.attributes['googclient_OpenTelemetrySpanContext']
) {
console.warn(
'googclient_OpenTelemetrySpanContext key set as message attribute, but will be overridden.'
);
}
if (!message.attributes) {
message.attributes = {};
}
message.attributes[
'googclient_OpenTelemetrySpanContext'
] = JSON.stringify(span.context());
}
return span;
}
}

promisifyAll(Publisher, {
singular: true,
exclude: ['publish', 'setOptions'],
exclude: ['publish', 'setOptions', 'constructSpan'],
});
Loading