diff --git a/applications/spring-ai-demo/README.md b/applications/spring-ai-demo/README.md
index 64f9b40..3835ab1 100644
--- a/applications/spring-ai-demo/README.md
+++ b/applications/spring-ai-demo/README.md
@@ -5,20 +5,42 @@
- Java 21+
- Langfuse stack ([Cloud](https://cloud.langfuse.com/) or [Self-Hosted](https://langfuse.com/docs/deployment/self-host))
- Langfuse API Keys
-- An OpenAI Api Key
+- An OpenAI API Key
+
+## Technology Stack
+
+- **Spring Boot**: 4.0.3
+- **Spring AI**: 2.0.0-M2
+- **Java**: 21
+- **OpenTelemetry**: Native support via `spring-boot-starter-opentelemetry`
## How to run
1. Configure environment variables to connect Spring AI demo app with Langfuse.
- ```
+ ```bash
export SPRING_AI_OPENAI_APIKEY="sk-proj-xxx"
- export OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel" # πͺπΊ EU data region
- # export OTEL_EXPORTER_OTLP_ENDPOINT="https://us.cloud.langfuse.com/api/public/otel" # πΊπΈ US data region
- # export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:3000/api/public/otel" # π Local deployment (>= v3.22.0)
- export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $(echo -n "pk-lf-xxx:sk-lf-xxx" | base64)"
+ export OTEL_EXPORTER_OTLP_HEADERS_AUTHORIZATION="Basic $(echo -n "pk-lf-xxx:sk-lf-xxx" | base64)"
```
-2. Run the sample application with `./mvnw clean install spring-boot:run`.
-3. Call the chat endpoint with `curl localhost:8080/v1/chat`.
+
+2. Run the sample application:
+ ```bash
+ ./mvnw clean install spring-boot:run
+ ```
+
+3. Call the chat endpoint:
+ ```bash
+ curl localhost:8080/v1/chat
+ ```
+
4. Observe the new trace in the Langfuse web UI.
+## Configuration
+
+The application uses native OpenTelemetry support introduced in Spring Boot 4.x. The OTLP endpoint is configured in `OtelConfig.java` to point to `http://localhost:3001/api/public/otel/v1/traces`.
+
+For cloud deployments, modify the endpoint in `OtelConfig.java`:
+- πͺπΊ EU: `https://cloud.langfuse.com/api/public/otel/v1/traces`
+- πΊπΈ US: `https://us.cloud.langfuse.com/api/public/otel/v1/traces`
+- π Local: `http://localhost:3000/api/public/otel/v1/traces` (>= v3.22.0)
+

diff --git a/applications/spring-ai-demo/pom.xml b/applications/spring-ai-demo/pom.xml
index cfb171b..18c48e8 100644
--- a/applications/spring-ai-demo/pom.xml
+++ b/applications/spring-ai-demo/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.4.3
+ 4.0.3
com.langfuse
@@ -17,18 +17,11 @@
21
- 1.0.0
+ 2.0.0-M2
-
- io.opentelemetry.instrumentation
- opentelemetry-instrumentation-bom
- 2.17.0
- pom
- import
-
org.springframework.ai
spring-ai-bom
@@ -44,30 +37,30 @@
org.springframework.boot
spring-boot-starter
-
- org.springframework.ai
- spring-ai-starter-model-openai
-
-
+
+
org.springframework.boot
spring-boot-starter-web
+
+
- io.opentelemetry.instrumentation
- opentelemetry-spring-boot-starter
+ org.springframework.ai
+ spring-ai-starter-model-openai
-
+
+
org.springframework.boot
- spring-boot-starter-actuator
+ spring-boot-starter-opentelemetry
-
+
+
- io.micrometer
- micrometer-tracing-bridge-otel
+ org.springframework.boot
+ spring-boot-starter-actuator
-
io.opentelemetry
opentelemetry-exporter-otlp
diff --git a/applications/spring-ai-demo/src/main/java/com/langfuse/springai/OtelConfig.java b/applications/spring-ai-demo/src/main/java/com/langfuse/springai/OtelConfig.java
new file mode 100644
index 0000000..f6fe86e
--- /dev/null
+++ b/applications/spring-ai-demo/src/main/java/com/langfuse/springai/OtelConfig.java
@@ -0,0 +1,28 @@
+package com.langfuse.springai;
+
+import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
+import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
+import io.opentelemetry.sdk.trace.export.SpanExporter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OtelConfig {
+
+ @Value("${otel.exporter.otlp.headers.authorization}")
+ private String authorizationHeader;
+
+ @Bean
+ public SpanExporter spanExporter() {
+ return OtlpHttpSpanExporter.builder()
+ .setEndpoint("http://localhost:3001/api/public/otel/v1/traces")
+ .addHeader("Authorization", authorizationHeader)
+ .build();
+ }
+
+ @Bean
+ public BatchSpanProcessor spanProcessor(SpanExporter exporter) {
+ return BatchSpanProcessor.builder(exporter).build();
+ }
+}
\ No newline at end of file
diff --git a/applications/spring-ai-demo/src/main/resources/application.yml b/applications/spring-ai-demo/src/main/resources/application.yml
index ce31f9e..8ea4efa 100644
--- a/applications/spring-ai-demo/src/main/resources/application.yml
+++ b/applications/spring-ai-demo/src/main/resources/application.yml
@@ -13,3 +13,10 @@ management:
observations:
annotations:
enabled: true # Enable @Observed (if you use observation annotations in code)
+
+otel:
+ exporter:
+ otlp:
+ headers:
+ authorization: {{OTEL_EXPORTER_OTLP_HEADERS_AUTHORIZATION}} # Set via env var for security}
+