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

examples: add gcp-observability examples #9967

Merged
merged 8 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
39 changes: 39 additions & 0 deletions examples/example-gcp-observability/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
gRPC GCP Observability Example
================

The GCP Observability example consists of a Hello World client and a Hello World server instrumented for logs, metrics and tracing.

__Please refer to GCP observability user guide for setting up authorization to acess Google Cloud Platform with Google user credentials.__
Copy link
Contributor

@sanjaypujare sanjaypujare Mar 20, 2023

Choose a reason for hiding this comment

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

Can we provide a link to the UG? We have one for preview and it's not going to change for GA, right?

Also it will be good if the link is to the specific section about credentials and authorization

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 checked with Lisa, she is trying to get a new directory set up. In that case, UG link will be different than what we have now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, we can go with the current link and update it whenever we have the updated link

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated and added current user guide link.

Copy link
Contributor

Choose a reason for hiding this comment

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

Link is correct but the link text "GCP observability user guide" doesn't match it or is not even correct. The user guide says it is for "Microservices observability" - so we could at least say that. In reality the UG is for "gRPC GCP Observability" or "gRPC Microservices on GCP Observability" . "GCP Observability" is too broad.

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 removed the link, as the link is going to be changed when we release.
The product name is "Microservices Observability", should i add "gRPC Microservices Observability" instead here to show connection to gRPC (while we do not have a link). Let me know what you think.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you removed the link, make sure the description is as close to what the actual UG title is (since users will have to search to find it). I think it is "Microservices Observability" so that makes most sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

sanjaypujare marked this conversation as resolved.
Show resolved Hide resolved

### Build the example

Build the Observability hello-world example client & server. From the `grpc-java/examples/examples-gcp-observability`
directory:
```
$ ../gradlew installDist
```

This creates the scripts `build/install/example-gcp-observability/bin/observability-hello-world-client` and
`build/install/example-gcp-observability/bin/observability-hello-world-server`.

### Run the example with configuration

To use Observability, you should first setup and configure authorization and credentials to use gCloud CLI.
sanjaypujare marked this conversation as resolved.
Show resolved Hide resolved

You need to set the `GRPC_GCP_OBSERVABILITY_CONFIG_FILE` environment variable to point to the gRPC GCP Observability configuration file (preferred) or if that
is not set then `GRPC_GCP_OBSERVABILITY_CONFIG` environment variable to gRPC GCP Observability configuration value. This is needed by both
`build/install/example-gcp-observability/bin/observability-hello-world-client` and
`build/install/example-gcp-observability/bin/observability-hello-world-server`.

1. To start the observability-enabled example server on its default port of 50051, run:
```
$ export GRPC_GCP_OBSERVABILITY_CONFIG_FILE=src/main/resources/io/grpc/examples/observabilityHelloWorld/gcp_observability_config.json
sanjaypujare marked this conversation as resolved.
Show resolved Hide resolved
$ ./build/install/example-gcp-observability/bin/observability-hello-world-server
```

2. In a different terminal window, run the observability-enabled example client:
```
$ export GRPC_GCP_OBSERVABILITY_CONFIG_FILE=src/main/resources/io/grpc/examples/observabilityHelloWorld/gcp_observability_config.json
$ ./build/install/example-gcp-observability/bin/observability-hello-world-client
```

68 changes: 68 additions & 0 deletions examples/example-gcp-observability/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
plugins {
// Provide convenience executables for trying out the examples.
id 'application'
// ASSUMES GRADLE 5.6 OR HIGHER. Use plugin version 0.8.10 with earlier gradle versions
id 'com.google.protobuf' version '0.8.17'
// Generate IntelliJ IDEA's .idea & .iml project files
id 'idea'
id 'java'
}

repositories {
maven { // The google mirror is less flaky than mavenCentral()
url "https://maven-central.storage-download.googleapis.com/maven2/"
}
mavenCentral()
mavenLocal()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

// IMPORTANT: You probably want the non-SNAPSHOT version of gRPC. Make sure you
// are looking at a tagged version of the example and not "master"!

// Feel free to delete the comment at the next line. It is just for safely
// updating the version in our release process.
def grpcVersion = '1.55.0-SNAPSHOT' // CURRENT_GRPC_VERSION
def protocVersion = '3.21.7'

dependencies {
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
implementation "io.grpc:grpc-gcp-observability:${grpcVersion}"
compileOnly "org.apache.tomcat:annotations-api:6.0.53"
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
}

protobuf {
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
}
generateProtoTasks {
all()*.plugins { grpc {} }
}
}

startScripts.enabled = false

task ObservabilityHelloWorldServer(type: CreateStartScripts) {
mainClass = 'io.grpc.examples.observabilityHelloWorld.ObservabilityHelloWorldServer'
applicationName = 'observability-hello-world-server'
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
classpath = startScripts.classpath
}

task ObservabilityHelloWorldClient(type: CreateStartScripts) {
mainClass = 'io.grpc.examples.observabilityHelloWorld.ObservabilityHelloWorldClient'
applicationName = 'observability-hello-world-client'
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
classpath = startScripts.classpath
}

applicationDistribution.into('bin') {
from(ObservabilityHelloWorldServer)
from(ObservabilityHelloWorldClient)
fileMode = 0755
}
1 change: 1 addition & 0 deletions examples/example-gcp-observability/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'example-gcp-observability'
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2023 The gRPC Authors
*
* 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.
*/

package io.grpc.examples.observabilityHelloWorld;
sanjaypujare marked this conversation as resolved.
Show resolved Hide resolved

import io.grpc.Channel;
import io.grpc.Grpc;
import io.grpc.InsecureChannelCredentials;
import io.grpc.ManagedChannel;
import io.grpc.StatusRuntimeException;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.gcp.observability.GcpObservability;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* A simple observability client that requests a greeting from the {@link HelloWorldServer} and
* generates logs, metrics and traces based on the configuration.
*/
public class ObservabilityHelloWorldClient {
private static final Logger logger = Logger.getLogger(ObservabilityHelloWorldClient.class.getName());
private static final int EXPORT_INTERVAL = 60;

private final GreeterGrpc.GreeterBlockingStub blockingStub;

/** Construct client for accessing HelloWorld server using the existing channel. */
public ObservabilityHelloWorldClient(Channel channel) {
// 'channel' here is a Channel, not a ManagedChannel, so it is not this code's responsibility to
// shut it down.

// Passing Channels to code makes code easier to test and makes it easier to reuse Channels.
blockingStub = GreeterGrpc.newBlockingStub(channel);
}

/** Say hello to server. */
public void greet(String name) {
logger.info("Will try to greet " + name + " ...");
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello(request);
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}
logger.info("Greeting: " + response.getMessage());
}

/**
* Greet server. If provided, the first element of {@code args} is the name to use in the
* greeting. The second argument is the target server.
*/
public static void main(String[] args) throws Exception {
String user = "world";
// Access a service running on the local machine on port 50051
String target = "localhost:50051";
// Allow passing in the user and target strings as command line arguments
if (args.length > 0) {
if ("--help".equals(args[0])) {
System.err.println("Usage: [name [target]]");
System.err.println("");
System.err.println(" name The name you wish to be greeted by. Defaults to " + user);
System.err.println(" target The server to connect to. Defaults to " + target);
System.exit(1);
}
user = args[0];
}
if (args.length > 1) {
target = args[1];
}

// Initialize observability
try (GcpObservability observability = GcpObservability.grpcInit()) {
// Create a communication channel to the server, known as a Channel. Channels are thread-safe
// and reusable. It is common to create channels at the beginning of your application and reuse
// them until the application shuts down.
//
// For the example we use plaintext insecure credentials to avoid needing TLS certificates. To
// use TLS, use TlsChannelCredentials instead.
ManagedChannel channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create())
.build();
try {
ObservabilityHelloWorldClient client = new ObservabilityHelloWorldClient(channel);
client.greet(user);
} finally {
// ManagedChannels use resources like threads and TCP connections. To prevent leaking these
// resources the channel should be shut down when it will no longer be used. If it may be used
// again leave it running.
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
}
System.out.println(
String.format(
"Sleeping %d seconds before shutdown to ensure all records are flushed.",
EXPORT_INTERVAL));
sanjaypujare marked this conversation as resolved.
Show resolved Hide resolved
Thread.sleep(TimeUnit.MILLISECONDS.convert(EXPORT_INTERVAL, TimeUnit.SECONDS));
Copy link
Member

Choose a reason for hiding this comment

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

If a sleep is still necessary, we should have that sleep in gcp-observability. The user shouldn't have to deal with stuff like this. Once we fix the issue the user's shutdown would naturally run faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree, will add sleep to gcp-observability in a follow up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added in PR #9972

} // observability.close() called implicitly
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2023 The gRPC Authors
*
* 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.
*/

package io.grpc.examples.observabilityHelloWorld;

import io.grpc.Grpc;
import io.grpc.InsecureServerCredentials;
import io.grpc.Server;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.gcp.observability.GcpObservability;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

/**
* Observability server that manages startup/shutdown of a {@code Greeter} server and generates
* logs, metrics and traces based on the configuration.
*/
public class ObservabilityHelloWorldServer {
private static final Logger logger = Logger.getLogger(ObservabilityHelloWorldServer.class.getName());

private Server server;

private void start() throws IOException {
/* The port on which the server should run */
int port = 50051;
server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
.addService(new GreeterImpl())
.build()
.start();
logger.info("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
System.err.println("*** shutting down gRPC server since JVM is shutting down");
try {
ObservabilityHelloWorldServer.this.stop();
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
System.err.println("*** server shut down");
}
});
}

private void stop() throws InterruptedException {
if (server != null) {
server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
}
}

/**
* Await termination on the main thread since the grpc library uses daemon threads.
*/
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}

/**
* Main launches the server from the command line.
*/
public static void main(String[] args) throws IOException, InterruptedException {
// Initialize observability
try (GcpObservability observability = GcpObservability.grpcInit()) {
final ObservabilityHelloWorldServer server = new ObservabilityHelloWorldServer();
server.start();
server.blockUntilShutdown();
} // observability.close() called implicitly
}

static class GreeterImpl extends GreeterGrpc.GreeterImplBase {

@Override
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2023 The gRPC Authors
*
* 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.
*/
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
sanjaypujare marked this conversation as resolved.
Show resolved Hide resolved
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"cloud_monitoring": {},
"cloud_trace": {
"sampling_rate": 1.00
},
"cloud_logging": {
"client_rpc_events": [{
"methods": ["helloworld.Greeter/*"]
}],
"server_rpc_events": [{
"methods": ["helloworld.Greeter/*"]
}]
},
"labels": {
"environment" : "observability-example"
}
}