Skip to content

Commit f29adf6

Browse files
authored
Add manually instrumented spring sample (#368)
* Add new example to project * Udpate spring service name for spring sleuth example * Add implementation for spring sample * Update task name to run app The name 'bootRun' conflicts with the name of tasks in a certain gradle versions. * Add comments for clarity * Add EOF line * Add cleanup instructions * Address feedback * update cleanup instructions to include deletion for Artifact Repository * Update script to use shorthands for env verification
1 parent 2a30e85 commit f29adf6

9 files changed

Lines changed: 360 additions & 1 deletion

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG
2-
spring.application.name=gcp-otel-spring-demo
2+
spring.application.name=gcp-otel-spring-sleuth-demo
33
spring.main.banner-mode=off
44
spring.sleuth.sampler.probability=1.0

examples/spring/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Instrumenting Spring Application using OpenTelemetry with Resource Detection
2+
3+
This example shows how to export metrics and traces to GCP generated from a manually instrumented Spring Boot application.
4+
Opentelemetry Autoconfiguration module is used to configure exporters and resource detection.
5+
6+
#### Prerequisites
7+
8+
##### Get Google Cloud Credentials on your machine
9+
10+
```shell
11+
gcloud auth application-default login
12+
```
13+
Executing this command will save your application credentials to default path which will depend on the type of machine -
14+
- Linux, macOS: `$HOME/.config/gcloud/application_default_credentials.json`
15+
- Windows: `%APPDATA%\gcloud\application_default_credentials.json`
16+
17+
##### Export the Google Cloud Project ID to `GOOGLE_CLOUD_PROJECT` environment variable:
18+
19+
```shell
20+
export GOOGLE_CLOUD_PROJECT="my-awesome-gcp-project-id"
21+
```
22+
23+
#### Run the application locally
24+
25+
You can run the example application via gradle. From the project root:
26+
27+
##### Build and run the executable JAR
28+
29+
```shell
30+
cd examples/spring && gradle runApp
31+
```
32+
33+
The application is now running. To generate traces, head to `http://localhost:8080` in your browser.
34+
*You can also try hitting other supported endpoints - `http://localhost:8080/greeting` to generate additional traces.*
35+
36+
You should now see the exported traces in your Google Cloud project.
37+
38+
#### Run the application in Cloud Run
39+
40+
To run this example in Google Cloud Run, you need to run the convenience script provided. After following the prerequisites,
41+
42+
First, export the Google Cloud Region for cloud run to `GOOGLE_CLOUD_RUN_REGION` environment variable:
43+
44+
```shell
45+
# This can be any supported Google cloud region
46+
export GOOGLE_CLOUD_RUN_REGION=us-central1
47+
```
48+
49+
Then, from the root of the repository,
50+
```shell
51+
cd examples/spring && ./run_in_cloud-run.sh
52+
```
53+
54+
This will deploy the containerized application to Cloud Run and create a proxy endpoint on your localhost which you can then call to reach your deployed Cloud Run service.
55+
You should be able to hit the following endpoints from your browser:
56+
57+
```text
58+
http://localhost:8080/
59+
http://localhost:8080/greeting
60+
```
61+
62+
The metrics and traces from this example can be viewed in Google Cloud Console.
63+
64+
##### Cleaning up
65+
66+
Keeping services running in Cloud Run can incur costs. Services deployed on Cloud Run can either
67+
be deleted from the GCP console or you can run the following `gcloud` command:
68+
69+
```shell
70+
# Delete the Cloud Run Service
71+
gcloud run services delete spring-java-cloud-run
72+
73+
# Delete Artifact Registry
74+
gcloud artifacts repositories delete cloud-run-applications --location=$GOOGLE_CLOUD_RUN_REGION
75+
```

examples/spring/build.gradle

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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 'org.springframework.boot' version '2.4.5'
18+
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
19+
id 'java'
20+
id 'application'
21+
id 'com.google.cloud.tools.jib'
22+
}
23+
24+
repositories {
25+
mavenCentral()
26+
}
27+
28+
description = 'Java Spring application instrumented with OpenTelemetry and enabled resource detection'
29+
30+
jar {
31+
manifest {
32+
attributes(
33+
'Main-Class': 'com.google.cloud.opentelemetry.example.spring.Main'
34+
)
35+
}
36+
}
37+
38+
// OpenTelemetry Autoconfigure module can be configured using system properties
39+
def autoconf_config = [
40+
'-Dotel.resource.attributes=gcp.project_id=otel-test-sharmapranav',
41+
'-Dotel.traces.exporter=google_cloud_trace',
42+
'-Dotel.metrics.exporter=google_cloud_monitoring,logging',
43+
'-Dotel.logs.exporter=none',
44+
'-Dotel.java.global-autoconfigure.enabled=true',
45+
'-Dotel.service.name=spring-example-service',
46+
]
47+
48+
jib {
49+
from.image = "gcr.io/distroless/java-debian10:11"
50+
containerizingMode = "packaged"
51+
container.ports = ["8080"]
52+
container.jvmFlags = autoconf_config as List
53+
}
54+
55+
// convenience task to build and run app with correct system properties
56+
tasks.register('runApp', JavaExec) {
57+
group = "Execution"
58+
description = "Builds and runs the spring application's execuable JAR"
59+
60+
dependsOn tasks.bootJar
61+
classpath = files('build/libs/examples-spring-0.1.0-SNAPSHOT.jar')
62+
jvmArgs = autoconf_config
63+
}
64+
65+
dependencies {
66+
implementation(libraries.opentelemetry_api)
67+
implementation(libraries.opentelemetry_sdk)
68+
implementation(libraries.google_cloud_trace)
69+
implementation(libraries.spring_boot_starter_web)
70+
// using autoconfigure to detect resources
71+
implementation(libraries.opentelemetry_sdk_autoconf)
72+
implementation(libraries.opentelemetry_autoconfigure_spi)
73+
// resource detection module
74+
runtimeOnly(libraries.opentelemetry_gcp_resources)
75+
// exporter dependencies
76+
implementation project(':exporter-trace')
77+
implementation project(':exporter-metrics')
78+
// auto exporter makes Google Cloud exporters available to autoconfigure module
79+
implementation project(':exporter-auto')
80+
// this helps in debugging as it outputs all export to std out
81+
implementation 'io.opentelemetry:opentelemetry-exporter-logging:1.33.0'
82+
}
83+
84+
test {
85+
useJUnitPlatform()
86+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
#
3+
# Copyright 2024 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
CONTAINER_REGISTRY=cloud-run-applications
18+
REGISTRY_LOCATION=us-central1
19+
UNSET_WARNING="Environment variable not set, please set the environment variable"
20+
21+
# Verify necessary environment variables are set
22+
echo "${GOOGLE_CLOUD_PROJECT:?${UNSET_WARNING}}"
23+
echo "${GOOGLE_APPLICATION_CREDENTIALS:?${UNSET_WARNING}}"
24+
echo "${GOOGLE_CLOUD_RUN_REGION:?${UNSET_WARNING}}"
25+
26+
echo "ENVIRONMENT VARIABLES VERIFIED"
27+
set -x
28+
29+
echo "CREATING CLOUD ARTIFACT REPOSITORY"
30+
gcloud artifacts repositories create ${CONTAINER_REGISTRY} --repository-format=docker --location=${REGISTRY_LOCATION} --description="Sample applications to run on Google Cloud Run"
31+
echo "CREATED ${CONTAINER_REGISTRY} in ${REGISTRY_LOCATION}"
32+
33+
echo "BUILDING SAMPLE APP IMAGE"
34+
gradle clean jib --image "${REGISTRY_LOCATION}-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/${CONTAINER_REGISTRY}/spring-java-sample"
35+
36+
echo "DEPLOYING APPLICATION ON CLOUD RUN"
37+
gcloud run deploy spring-java-cloud-run \
38+
--image="${REGISTRY_LOCATION}-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/${CONTAINER_REGISTRY}/spring-java-sample" \
39+
--region="${GOOGLE_CLOUD_RUN_REGION}" \
40+
--no-allow-unauthenticated \
41+
--no-cpu-throttling \
42+
--max-instances=5 \
43+
--min-instances=3
44+
45+
echo "ENABLING SAMPLE APP ON PORT 8080 VIA PROXY"
46+
echo "VISIT http://localhost:8080 TO ACCESS THE APPLICATION OR PRESS CTRL+C TO EXIT"
47+
gcloud beta run services proxy spring-java-cloud-run --port=8080
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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.example.spring;
17+
18+
import io.opentelemetry.api.metrics.LongCounter;
19+
import io.opentelemetry.api.metrics.Meter;
20+
import io.opentelemetry.api.trace.Span;
21+
import io.opentelemetry.api.trace.Tracer;
22+
import io.opentelemetry.context.Scope;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.web.bind.annotation.GetMapping;
25+
import org.springframework.web.bind.annotation.RestController;
26+
27+
@RestController
28+
public class AppController {
29+
private final LongCounter counterGreeting;
30+
private final LongCounter counterHome;
31+
private final Tracer tracer;
32+
33+
@Autowired
34+
public AppController(Meter meter, Tracer tracer) {
35+
counterGreeting =
36+
meter
37+
.counterBuilder("greeting_counter")
38+
.setDescription("Hit /greeting endpoint")
39+
.setUnit("1")
40+
.build();
41+
counterHome =
42+
meter
43+
.counterBuilder("home_counter")
44+
.setDescription("Hit root endpoint")
45+
.setUnit("1")
46+
.build();
47+
this.tracer = tracer;
48+
}
49+
50+
@GetMapping("/greeting")
51+
public String greeting() {
52+
Span span = tracer.spanBuilder("greeting_call").startSpan();
53+
try (Scope scope = span.makeCurrent()) {
54+
span.addEvent("Event A");
55+
span.setAttribute("test_api", true);
56+
counterGreeting.add(1);
57+
} finally {
58+
span.end();
59+
}
60+
return "Hello World";
61+
}
62+
63+
@GetMapping("/")
64+
public String home() {
65+
counterHome.add(1);
66+
return "Welcome to Spring Demo";
67+
}
68+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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.example.spring;
17+
18+
import org.springframework.boot.SpringApplication;
19+
import org.springframework.boot.autoconfigure.SpringBootApplication;
20+
21+
@SpringBootApplication
22+
public class Main {
23+
public static void main(String[] args) {
24+
SpringApplication.run(Main.class, args);
25+
}
26+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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.example.spring;
17+
18+
import io.opentelemetry.api.metrics.Meter;
19+
import io.opentelemetry.api.trace.Tracer;
20+
import io.opentelemetry.sdk.OpenTelemetrySdk;
21+
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
22+
import org.springframework.context.annotation.Bean;
23+
import org.springframework.context.annotation.Configuration;
24+
25+
@Configuration
26+
public class OpenTelemetryConfiguration {
27+
28+
private static final String INSTRUMENTATION_SCOPE_NAME = "spring-demo";
29+
private static final String INSTRUMENTATION_VERSION = "semver:1.0.0";
30+
31+
@Bean
32+
public OpenTelemetrySdk getOpenTelemetrySdk() {
33+
return AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
34+
}
35+
36+
@Bean
37+
public Tracer getTracer(OpenTelemetrySdk openTelemetrySdk) {
38+
return openTelemetrySdk
39+
.tracerBuilder(INSTRUMENTATION_SCOPE_NAME)
40+
.setInstrumentationVersion(INSTRUMENTATION_VERSION)
41+
.build();
42+
}
43+
44+
@Bean
45+
public Meter getMeter(OpenTelemetrySdk openTelemetrySdk) {
46+
return openTelemetrySdk
47+
.meterBuilder(INSTRUMENTATION_SCOPE_NAME)
48+
.setInstrumentationVersion(INSTRUMENTATION_VERSION)
49+
.build();
50+
}
51+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG
2+
spring.application.name=gcp-otel-spring-demo

settings.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ include ":detector-resources"
3838
include ":detector-resources-support"
3939
include ":e2e-test-server"
4040
include ":examples-spring-sleuth"
41+
include ":examples-spring"
4142
include ":propagators-gcp"
4243
include ":shared-resourcemapping"
4344

@@ -88,3 +89,6 @@ project(':shared-resourcemapping').projectDir =
8889

8990
project(':examples-otlp-spring').projectDir =
9091
"$rootDir/examples/otlp-spring" as File
92+
93+
project(':examples-spring').projectDir =
94+
"$rootDir/examples/spring" as File

0 commit comments

Comments
 (0)