diff --git a/README.md b/README.md index 23d6759a41..cab3c1193b 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ of them assume you have docker running on your local machine. - This module demonstrates using the OpenTelemetry Java Agent with a simple spring boot application. Traces, metrics, and logs are exported to a collector via OTLP. +- [Spring native image telemetry with OpenTelemetry Spring Starter](spring-native) + - This module demonstrates using the OpenTelemetry Spring Boot starter with a + Graal VM native image. Traces and metrics are exported to a collector via OTLP. - [Configuring Log Appenders](log-appender) - This module demonstrates how to configure the Log4j and Logback appenders to bridge logs into the OpenTelemetry Log SDK. diff --git a/settings.gradle b/settings.gradle index d69fae2442..de9db1f48d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,7 +19,8 @@ include ":opentelemetry-examples-autoconfigure", ":opentelemetry-examples-prometheus", ":opentelemetry-examples-sdk-usage", ":opentelemetry-examples-telemetry-testing", - ":opentelemetry-examples-zipkin" + ":opentelemetry-examples-zipkin", + ":opentelemetry-examples-spring-native" rootProject.children.each { it.projectDir = "$rootDir/" + it.name diff --git a/spring-native/README.md b/spring-native/README.md new file mode 100644 index 0000000000..5d5a03bd86 --- /dev/null +++ b/spring-native/README.md @@ -0,0 +1,103 @@ +# OpenTelemetry Java Agent Example + +This example demonstrates usage of the [OpenTelemetry Spring starter](https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/spring/starters/spring-boot-starter) with a Spring Boot application and a GraalVM native image. + +It consists of a Spring Boot application with: + +- A simple web API available at `GET http://localhost:8080/ping`. When called, + the OpenTelemetry Spring starter records spans and metrics. +- A docker compose setup configured to run the application and export to + the [collector](https://opentelemetry.io/docs/collector/) via OTLP. +- The collector is configured with + the [OTLP receiver](https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver/otlpreceiver) + and export it to standard out with + the [logging exporter](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/loggingexporter) + +## Prerequisites + +* GraalVM for Java 17 +* Docker compose + +# How to run + +Build the GraalVM native image +```shell +cd spring-native +../gradlew bootBuildImage --imageName=otel-native-graalvm +``` + +Run the application and the collector with docker compose +```shell +docker-compose up +``` + +In a separate shell, exercise the application by calling its endpoint +```shell +curl http://localhost:8080/ping +``` + +Watch for spans, metrics, and logs in the collector log output. + +Example of the beginning of the collector log after the ping: +``` +graal-native-collector-1 | Span #0 +graal-native-collector-1 | Trace ID : 668c936f4f0580bac9db40162e8159fa +graal-native-collector-1 | Parent ID : +graal-native-collector-1 | ID : 858b9b11341b1bb9 +graal-native-collector-1 | Name : GET /ping +graal-native-collector-1 | Kind : Server +graal-native-collector-1 | Start time : 2023-06-09 13:49:43.270616 +0000 UTC +graal-native-collector-1 | End time : 2023-06-09 13:49:43.271635862 +0000 UTC +graal-native-collector-1 | Status code : Unset +graal-native-collector-1 | Status message : +graal-native-collector-1 | Attributes: +graal-native-collector-1 | -> http.target: Str(/ping) +graal-native-collector-1 | -> net.sock.peer.addr: Str(172.20.0.1) +graal-native-collector-1 | -> user_agent.original: Str(curl/8.0.1) +graal-native-collector-1 | -> net.host.name: Str(localhost) +graal-native-collector-1 | -> net.sock.host.addr: Str(172.20.0.3) +graal-native-collector-1 | -> http.route: Str(/ping) +graal-native-collector-1 | -> net.sock.peer.port: Int(42646) +graal-native-collector-1 | -> http.method: Str(GET) +graal-native-collector-1 | -> http.scheme: Str(http) +graal-native-collector-1 | -> http.status_code: Int(200) +graal-native-collector-1 | -> net.protocol.version: Str(1.1) +graal-native-collector-1 | -> net.host.port: Int(8080) +graal-native-collector-1 | -> net.protocol.name: Str(http) +graal-native-collector-1 | -> http.response_content_length: Int(4) +graal-native-collector-1 | {"kind": "exporter", "data_type": "traces", "name": "logging"} +graal-native-collector-1 | 2023-06-09T13:50:07.529Z info MetricsExporter {"kind": "exporter", "data_type": "metrics", "name": "logging", "resource metrics": 1, "metrics": 3, "data points": 3} +graal-native-collector-1 | 2023-06-09T13:50:07.529Z info ResourceMetrics #0 +graal-native-collector-1 | Resource SchemaURL: +graal-native-collector-1 | Resource attributes: +graal-native-collector-1 | -> service.name: Str(unknown_service:java) +graal-native-collector-1 | -> telemetry.sdk.language: Str(java) +graal-native-collector-1 | -> telemetry.sdk.name: Str(opentelemetry) +graal-native-collector-1 | -> telemetry.sdk.version: Str(1.26.0) +graal-native-collector-1 | ScopeMetrics #0 +graal-native-collector-1 | ScopeMetrics SchemaURL: +graal-native-collector-1 | InstrumentationScope io.opentelemetry.spring-webmvc-6.0 +graal-native-collector-1 | Metric #0 +graal-native-collector-1 | Descriptor: +graal-native-collector-1 | -> Name: http.server.duration +graal-native-collector-1 | -> Description: The duration of the inbound HTTP request +graal-native-collector-1 | -> Unit: ms +graal-native-collector-1 | -> DataType: Histogram +graal-native-collector-1 | -> AggregationTemporality: Cumulative +graal-native-collector-1 | HistogramDataPoints #0 +graal-native-collector-1 | Data point attributes: +graal-native-collector-1 | -> http.method: Str(GET) +graal-native-collector-1 | -> http.route: Str(/ping) +graal-native-collector-1 | -> http.scheme: Str(http) +graal-native-collector-1 | -> http.status_code: Int(200) +graal-native-collector-1 | -> net.host.name: Str(localhost) +graal-native-collector-1 | -> net.host.port: Int(8080) +graal-native-collector-1 | -> net.protocol.name: Str(http) +graal-native-collector-1 | -> net.protocol.version: Str(1.1) +graal-native-collector-1 | StartTimestamp: 2023-06-09 13:49:07.528013 +0000 UTC +graal-native-collector-1 | Timestamp: 2023-06-09 13:50:07.528308 +0000 UTC +graal-native-collector-1 | Count: 1 +graal-native-collector-1 | Sum: 0.991699 +graal-native-collector-1 | Min: 0.991699 +graal-native-collector-1 | Max: 0.991699 +``` \ No newline at end of file diff --git a/spring-native/build.gradle b/spring-native/build.gradle new file mode 100644 index 0000000000..f8e7170a59 --- /dev/null +++ b/spring-native/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.1.0' + id 'io.spring.dependency-management' version '1.1.0' + id 'org.graalvm.buildtools.native' version '0.9.20' +} + +compileJava { + sourceCompatibility = "17" + targetCompatibility = "17" +} +description = 'OpenTelemetry Example for Spring native images' +ext.moduleName = "io.opentelemetry.examples.native" + +dependencyManagement { + imports { + mavenBom 'io.opentelemetry:opentelemetry-bom:1.26.0' + mavenBom 'io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha:1.26.0-alpha' + } +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter") +} \ No newline at end of file diff --git a/spring-native/collector-spring-native-config.yaml b/spring-native/collector-spring-native-config.yaml new file mode 100644 index 0000000000..58d79532e8 --- /dev/null +++ b/spring-native/collector-spring-native-config.yaml @@ -0,0 +1,18 @@ +receivers: + otlp: + protocols: + grpc: +exporters: + logging: + verbosity: detailed +service: + pipelines: + metrics: + receivers: [otlp] + exporters: [logging] + traces: + receivers: [otlp] + exporters: [logging] + logs: + receivers: [otlp] + exporters: [logging] diff --git a/spring-native/docker-compose.yml b/spring-native/docker-compose.yml new file mode 100644 index 0000000000..2f749dd3ee --- /dev/null +++ b/spring-native/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + app: + image: otel-native-graalvm + environment: + OTEL_SERVICE_NAME: "graal-native-example-app" + OTEL_EXPORTER_OTLP_ENDPOINT: "http://collector:4317" + # Logs are disabled by default + OTEL_LOGS_EXPORTER: "otlp" + ports: + - "8080:8080" + depends_on: + - collector + collector: + image: otel/opentelemetry-collector-contrib:0.77.0 + volumes: + - ./collector-spring-native-config.yaml:/collector-spring-native-config.yaml + command: ["--config=/collector-spring-native-config.yaml"] + expose: + - "4317" + ports: + - "4317:4317" diff --git a/spring-native/src/main/java/io/opentelemetry/example/graal/Application.java b/spring-native/src/main/java/io/opentelemetry/example/graal/Application.java new file mode 100644 index 0000000000..d3ee7315fd --- /dev/null +++ b/spring-native/src/main/java/io/opentelemetry/example/graal/Application.java @@ -0,0 +1,12 @@ +package io.opentelemetry.example.graal; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-native/src/main/java/io/opentelemetry/example/graal/Controller.java b/spring-native/src/main/java/io/opentelemetry/example/graal/Controller.java new file mode 100644 index 0000000000..a11833452d --- /dev/null +++ b/spring-native/src/main/java/io/opentelemetry/example/graal/Controller.java @@ -0,0 +1,13 @@ +package io.opentelemetry.example.graal; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class Controller { + + @GetMapping("/ping") + public String ping() { + return "pong"; + } +}