Skip to content

Commit 97a8f20

Browse files
authored
Add otlp with spring boot sample (#327)
* Add initial setup files for new example * Add functioning spring boot application * add additional mappings to enhance sample * Add spring boot application properties * Update spring application to generate traces * Run application in GKE * Fix bug in credential refresh * Add client application to test credential refresh * Add readme for sample * Add missing EOF lines * Readme style update
1 parent 008cf95 commit 97a8f20

File tree

9 files changed

+541
-0
lines changed

9 files changed

+541
-0
lines changed

examples/otlp-spring/README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# OTLP Trace with Spring Boot and Google Auth
2+
3+
A sample Spring Boot service that exports OTLP traces, protected by Google authentication. The sample uses auto-refreshing credentials.
4+
5+
### Prerequisites
6+
7+
##### Get Google Cloud Credentials on your machine
8+
9+
```shell
10+
gcloud auth application-default login
11+
```
12+
13+
##### Export the Google Cloud Project ID to `GOOGLE_CLOUD_PROJECT` environment variable:
14+
15+
```shell
16+
export GOOGLE_CLOUD_PROJECT="my-awesome-gcp-project-id"
17+
```
18+
19+
##### Update build.gradle to set required arguments
20+
21+
Update [`build.gradle`](build.grade) to set the following:
22+
23+
```
24+
'-Dotel.resource.attributes=gcp.project_id=<YOUR_PROJECT_ID>,
25+
'-Dotel.exporter.otlp.headers=X-Goog-User-Project=<YOUR_QUOTA_PROJECT>',
26+
# Optional - if you want to export using gRPC protocol
27+
'-Dotel.exporter.otlp.protocol=grpc',
28+
```
29+
30+
## Running Locally on your machine
31+
32+
Setup your endpoint with the `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable:
33+
34+
```shell
35+
export OTEL_EXPORTER_OTLP_ENDPOINT="http://your-endpoint:port"
36+
```
37+
38+
To run the spring boot application from project root:
39+
40+
```shell
41+
./gradlew :examples-otlp-spring:run
42+
```
43+
44+
This will start the web application on localhost:8080. The application provides 2 routes:
45+
- http://localhost:8080/ : The index root; does not generate a trace.
46+
- http://localhost:8080/work : This route generates a trace.
47+
48+
Visit these routes to interact with the application.
49+
50+
## Running on Google Kubernetes Engine
51+
52+
> [!NOTE]
53+
> You need to have a GKE cluster already setup in your GCP project before continuing with these steps.
54+
55+
Create artifact registry repository to host your containerized image of the application:
56+
```shell
57+
gcloud artifacts repositories create otlp-samples --repository-format=docker --location=us-central1
58+
59+
gcloud auth configure-docker us-central1-docker.pkg.dev
60+
```
61+
62+
Build and push your image to the Artifact Registry.
63+
```shell
64+
./gradlew :examples:otlp-spring:jib --image="us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/otlp-samples/spring-otlp-trace-example:v1"
65+
```
66+
67+
Deploy the image on your Kubernetes cluster and setup port forwarding to interact with your cluster:
68+
```shell
69+
sed s/%GOOGLE_CLOUD_PROJECT%/$GOOGLE_CLOUD_PROJECT/g \
70+
examples/otlp-spring/k8s/deployment.yaml | kubectl apply -f -
71+
72+
# This connects port 8080 on your machine to port 60000 on the spring-otlp-service
73+
kubectl port-forward service/spring-otlp-service 8080:60000
74+
```
75+
76+
After successfully setting up port-forwarding, you can send requests to your cluster via `curl` or some similar tool:
77+
```shell
78+
curl http://localhost:8080/work?desc=test
79+
```
80+
81+
### Sending continuous requests
82+
83+
The sample comes with a [client program](src/test/java/com/google/cloud/opentelemetry/examples/otlpspring/MainClient.java) which sends requests to deployed application on your cluster at a fixed rate.
84+
Once you have port forwarding setup for your cluster, run this client program to send continuous requests to the Spring service to generate multiple traces.
85+
86+
```shell
87+
./gradlew :examples-otlp-spring:runClient
88+
```
89+
90+
### GKE Cleanup
91+
92+
After running the sample, delete the deployment and the service if you are done with it:
93+
```shell
94+
kubectl delete services spring-otlp-service
95+
kubectl delete deployment spring-otlp-trace-example
96+
```

examples/otlp-spring/build.gradle

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
plugins {
17+
id 'java'
18+
id 'application'
19+
id 'com.google.cloud.tools.jib'
20+
}
21+
22+
description = 'Example showcasing OTLP trace ingest on GCP and Google Auth in a Spring Boot App'
23+
24+
group 'com.google.cloud.opentelemetry.examples'
25+
26+
repositories {
27+
mavenCentral()
28+
}
29+
30+
dependencies {
31+
implementation(libraries.opentelemetry_api)
32+
implementation(libraries.opentelemetry_sdk)
33+
implementation(libraries.opentelemetry_otlp_exporter)
34+
implementation(libraries.opentelemetry_sdk_autoconf)
35+
implementation(libraries.google_auth)
36+
37+
implementation(libraries.spring_boot_starter_web)
38+
}
39+
40+
// Provide headers from env variable
41+
// export OTEL_EXPORTER_OTLP_ENDPOINT="http://path/to/yourendpoint:port"
42+
def autoconf_config = [
43+
'-Dotel.resource.attributes=gcp.project_id=<YOUR_PROJECT>',
44+
'-Dotel.exporter.otlp.headers=X-Goog-User-Project=<YOUR_QUOTA_PROJECT>',
45+
'-Dotel.traces.exporter=otlp',
46+
'-Dotel.metrics.exporter=none',
47+
'-Dotel.exporter.otlp.protocol=http/protobuf',
48+
'-Dotel.java.global-autoconfigure.enabled=true',
49+
'-Dotel.service.name=spring-otlp-trace-example-service',
50+
]
51+
52+
application {
53+
mainClass = "com.google.cloud.opentelemetry.examples.otlpspring.Main"
54+
applicationDefaultJvmArgs = autoconf_config
55+
}
56+
57+
jib {
58+
from.image = "gcr.io/distroless/java-debian10:11"
59+
containerizingMode = "packaged"
60+
container.jvmFlags = autoconf_config as List
61+
container.ports = ["8080"]
62+
container.environment = [
63+
"OTEL_EXPORTER_OTLP_ENDPOINT":"http://path/to/yourendpoint:port",
64+
]
65+
}
66+
67+
tasks.register('runClient', JavaExec) {
68+
classpath = sourceSets.test.runtimeClasspath
69+
mainClass = 'com.google.cloud.opentelemetry.examples.otlpspring.MainClient'
70+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: spring-otlp-trace-example
5+
namespace: default
6+
labels:
7+
app: spring-otlp-trace-example
8+
tier: backend
9+
spec:
10+
selector:
11+
matchLabels:
12+
app: spring-otlp-trace-example
13+
tier: backend
14+
template:
15+
metadata:
16+
labels:
17+
app: spring-otlp-trace-example
18+
tier: backend
19+
spec:
20+
containers:
21+
- name: spring-otlp-trace-example
22+
image: us-central1-docker.pkg.dev/%GOOGLE_CLOUD_PROJECT%/otlp-samples/spring-otlp-trace-example:v1
23+
imagePullPolicy: Always
24+
# required for resource detection in GKE environment
25+
env:
26+
- name: NAMESPACE
27+
valueFrom:
28+
fieldRef:
29+
fieldPath: metadata.namespace
30+
- name: CONTAINER_NAME
31+
valueFrom:
32+
fieldRef:
33+
fieldPath: metadata.name
34+
---
35+
apiVersion: v1
36+
kind: Service
37+
metadata:
38+
name: spring-otlp-service
39+
spec:
40+
selector:
41+
app: spring-otlp-trace-example
42+
tier: backend
43+
ports:
44+
- protocol: TCP
45+
port: 60000
46+
targetPort: 8080
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.examples.otlpspring;
17+
18+
import io.opentelemetry.api.trace.Span;
19+
import io.opentelemetry.context.Scope;
20+
import io.opentelemetry.sdk.OpenTelemetrySdk;
21+
import java.util.Optional;
22+
import java.util.Random;
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
25+
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.web.bind.annotation.GetMapping;
27+
import org.springframework.web.bind.annotation.RequestParam;
28+
import org.springframework.web.bind.annotation.RestController;
29+
30+
@RestController
31+
public class ApplicationController {
32+
private static final String INSTRUMENTATION_SCOPE_NAME = ApplicationController.class.getName();
33+
private static final Random random = new Random();
34+
35+
private static final String INDEX_GREETING =
36+
"Welcome to OTLP Trace sample with Google Auth on Spring";
37+
private static final String WORK_RESPONSE_FMT = "Work finished in %d ms";
38+
39+
private final Logger logger = LoggerFactory.getLogger(ApplicationController.class);
40+
private final OpenTelemetrySdk openTelemetrySdk;
41+
42+
@Autowired
43+
public ApplicationController(OpenTelemetrySdk openTelemetrySdk) {
44+
this.openTelemetrySdk = openTelemetrySdk;
45+
}
46+
47+
@GetMapping("/")
48+
public String index() {
49+
return INDEX_GREETING;
50+
}
51+
52+
@GetMapping("/work")
53+
public String simulateWork(@RequestParam(name = "desc") Optional<String> description) {
54+
String desc = description.orElse("generic");
55+
// Generate a span
56+
Span span =
57+
openTelemetrySdk.getTracer(INSTRUMENTATION_SCOPE_NAME).spanBuilder(desc).startSpan();
58+
long workDurationMillis;
59+
try (Scope scope = span.makeCurrent()) {
60+
span.addEvent("Event A");
61+
// Do some work for the use case
62+
// Simulate work: this could be simulating a network request or an expensive disk operation
63+
workDurationMillis = 100 + random.nextInt(5) * 100;
64+
Thread.sleep(workDurationMillis);
65+
span.addEvent("Event B");
66+
} catch (InterruptedException e) {
67+
logger.debug("Error while sleeping: {}", e.getMessage());
68+
throw new RuntimeException(e);
69+
} finally {
70+
span.end();
71+
}
72+
return String.format(WORK_RESPONSE_FMT, workDurationMillis);
73+
}
74+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.examples.otlpspring;
17+
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
import org.springframework.boot.SpringApplication;
21+
import org.springframework.boot.autoconfigure.SpringBootApplication;
22+
23+
@SpringBootApplication
24+
public class Main {
25+
private static final Logger logger = LoggerFactory.getLogger(Main.class);
26+
27+
public static void main(String[] args) {
28+
logger.info("Starting OTLP with Spring Boot and Google Auth");
29+
SpringApplication.run(Main.class, args);
30+
}
31+
}

0 commit comments

Comments
 (0)