-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
Changes from 3 commits
c7cd10b
11d4ada
502e582
0df5bb8
04a70f7
623273a
bce8a0a
48dbdc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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.__ | ||
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 | ||
``` | ||
|
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 | ||
} |
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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, will add sleep to gcp-observability in a follow up PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
} | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done