Skip to content

Commit 19ccaf4

Browse files
Remove possible large amount of data from spans
This removes the possible large amount of data that was attached to spans and it logs the data out. This change also removes the direct dependency on the OTel SDK.
1 parent 9de13d1 commit 19ccaf4

File tree

51 files changed

+965
-1540
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+965
-1540
lines changed

auto-configurations/models/chat/client/spring-ai-autoconfigure-model-chat-client/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
<version>${project.parent.version}</version>
3131
</dependency>
3232

33+
<dependency>
34+
<groupId>io.micrometer</groupId>
35+
<artifactId>micrometer-tracing</artifactId>
36+
<optional>true</optional>
37+
</dependency>
38+
3339
<!-- Boot dependencies -->
3440
<dependency>
3541
<groupId>org.springframework.boot</groupId>

auto-configurations/models/chat/client/spring-ai-autoconfigure-model-chat-client/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientAutoConfiguration.java

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,23 @@
1717
package org.springframework.ai.model.chat.client.autoconfigure;
1818

1919
import io.micrometer.observation.ObservationRegistry;
20+
import io.micrometer.tracing.Tracer;
2021
import org.slf4j.Logger;
2122
import org.slf4j.LoggerFactory;
22-
2323
import org.springframework.ai.chat.client.ChatClient;
2424
import org.springframework.ai.chat.client.ChatClientCustomizer;
25+
import org.springframework.ai.chat.client.observation.ChatClientObservationContext;
2526
import org.springframework.ai.chat.client.observation.ChatClientObservationConvention;
26-
import org.springframework.ai.chat.client.observation.ChatClientPromptContentObservationFilter;
27+
import org.springframework.ai.chat.client.observation.ChatClientPromptContentObservationHandler;
2728
import org.springframework.ai.chat.model.ChatModel;
29+
import org.springframework.ai.observation.TracingAwareLoggingObservationHandler;
2830
import org.springframework.beans.factory.ObjectProvider;
2931
import org.springframework.boot.autoconfigure.AutoConfiguration;
3032
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
31-
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
32-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
33-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
33+
import org.springframework.boot.autoconfigure.condition.*;
3434
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3535
import org.springframework.context.annotation.Bean;
36+
import org.springframework.context.annotation.Configuration;
3637
import org.springframework.context.annotation.Scope;
3738

3839
/**
@@ -47,9 +48,11 @@
4748
* @author Josh Long
4849
* @author Arjen Poutsma
4950
* @author Thomas Vitale
51+
* @author Jonatan Ivanov
5052
* @since 1.0.0
5153
*/
52-
@AutoConfiguration
54+
@AutoConfiguration(
55+
afterName = { "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration" })
5356
@ConditionalOnClass(ChatClient.class)
5457
@EnableConfigurationProperties(ChatClientBuilderProperties.class)
5558
@ConditionalOnProperty(prefix = ChatClientBuilderProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
@@ -58,6 +61,11 @@ public class ChatClientAutoConfiguration {
5861

5962
private static final Logger logger = LoggerFactory.getLogger(ChatClientAutoConfiguration.class);
6063

64+
private static void logPromptContentWarning() {
65+
logger.warn(
66+
"You have enabled logging out the ChatClient prompt content with the risk of exposing sensitive or private information. Please, be careful!");
67+
}
68+
6169
@Bean
6270
@ConditionalOnMissingBean
6371
ChatClientBuilderConfigurer chatClientBuilderConfigurer(ObjectProvider<ChatClientCustomizer> customizerProvider) {
@@ -79,14 +87,37 @@ ChatClient.Builder chatClientBuilder(ChatClientBuilderConfigurer chatClientBuild
7987
return chatClientBuilderConfigurer.configure(builder);
8088
}
8189

82-
@Bean
83-
@ConditionalOnMissingBean
84-
@ConditionalOnProperty(prefix = ChatClientBuilderProperties.CONFIG_PREFIX + ".observations",
85-
name = "include-prompt", havingValue = "true")
86-
ChatClientPromptContentObservationFilter chatClientPromptContentObservationFilter() {
87-
logger.warn(
88-
"You have enabled the inclusion of the ChatClient prompt content in the observations, with the risk of exposing sensitive or private information. Please, be careful!");
89-
return new ChatClientPromptContentObservationFilter();
90+
@Configuration(proxyBeanMethods = false)
91+
@ConditionalOnClass(Tracer.class)
92+
@ConditionalOnBean(Tracer.class)
93+
static class TracerPresentObservationConfiguration {
94+
95+
@Bean
96+
@ConditionalOnMissingBean(value = ChatClientPromptContentObservationHandler.class,
97+
name = "chatClientPromptContentObservationHandler")
98+
@ConditionalOnProperty(prefix = ChatClientBuilderProperties.CONFIG_PREFIX + ".observations",
99+
name = "log-prompt", havingValue = "true")
100+
TracingAwareLoggingObservationHandler<ChatClientObservationContext> chatClientPromptContentObservationHandler(
101+
Tracer tracer) {
102+
logPromptContentWarning();
103+
return new TracingAwareLoggingObservationHandler<>(new ChatClientPromptContentObservationHandler(), tracer);
104+
}
105+
106+
}
107+
108+
@Configuration(proxyBeanMethods = false)
109+
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")
110+
static class TracerNotPresentObservationConfiguration {
111+
112+
@Bean
113+
@ConditionalOnMissingBean
114+
@ConditionalOnProperty(prefix = ChatClientBuilderProperties.CONFIG_PREFIX + ".observations",
115+
name = "log-prompt", havingValue = "true")
116+
ChatClientPromptContentObservationHandler chatClientPromptContentObservationHandler() {
117+
logPromptContentWarning();
118+
return new ChatClientPromptContentObservationHandler();
119+
}
120+
90121
}
91122

92123
}

auto-configurations/models/chat/client/spring-ai-autoconfigure-model-chat-client/src/test/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientObservationAutoConfigurationTests.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
package org.springframework.ai.model.chat.client.autoconfigure;
1818

19+
import io.micrometer.tracing.Tracer;
1920
import org.junit.jupiter.api.Test;
20-
21-
import org.springframework.ai.chat.client.observation.ChatClientPromptContentObservationFilter;
21+
import org.springframework.ai.chat.client.observation.ChatClientPromptContentObservationHandler;
2222
import org.springframework.boot.autoconfigure.AutoConfigurations;
23+
import org.springframework.boot.test.context.FilteredClassLoader;
2324
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2425

2526
import static org.assertj.core.api.Assertions.assertThat;
@@ -29,22 +30,24 @@
2930
*
3031
* @author Christian Tzolov
3132
* @author Thomas Vitale
33+
* @author Jonatan Ivanov
3234
*/
3335
class ChatClientObservationAutoConfigurationTests {
3436

3537
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
3638
.withConfiguration(AutoConfigurations.of(ChatClientAutoConfiguration.class));
3739

3840
@Test
39-
void promptContentFilterDefault() {
41+
void promptContentHandlerDefault() {
4042
this.contextRunner
41-
.run(context -> assertThat(context).doesNotHaveBean(ChatClientPromptContentObservationFilter.class));
43+
.run(context -> assertThat(context).doesNotHaveBean(ChatClientPromptContentObservationHandler.class));
4244
}
4345

4446
@Test
45-
void promptContentFilterEnabled() {
46-
this.contextRunner.withPropertyValues("spring.ai.chat.client.observations.include-prompt=true")
47-
.run(context -> assertThat(context).hasSingleBean(ChatClientPromptContentObservationFilter.class));
47+
void promptContentHandlerEnabled() {
48+
this.contextRunner.withClassLoader(new FilteredClassLoader(Tracer.class))
49+
.withPropertyValues("spring.ai.chat.client.observations.log-prompt=true")
50+
.run(context -> assertThat(context).hasSingleBean(ChatClientPromptContentObservationHandler.class));
4851
}
4952

5053
}

auto-configurations/models/chat/observation/spring-ai-autoconfigure-model-chat-observation/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
<dependency>
3434
<groupId>io.micrometer</groupId>
35-
<artifactId>micrometer-tracing-bridge-otel</artifactId>
35+
<artifactId>micrometer-tracing</artifactId>
3636
<optional>true</optional>
3737
</dependency>
3838

auto-configurations/models/chat/observation/spring-ai-autoconfigure-model-chat-observation/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationAutoConfiguration.java

Lines changed: 42 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,41 +16,35 @@
1616

1717
package org.springframework.ai.model.chat.observation.autoconfigure;
1818

19-
import java.util.List;
20-
2119
import io.micrometer.core.instrument.MeterRegistry;
2220
import io.micrometer.tracing.Tracer;
23-
import io.micrometer.tracing.otel.bridge.OtelTracer;
2421
import org.slf4j.Logger;
2522
import org.slf4j.LoggerFactory;
26-
2723
import org.springframework.ai.chat.client.advisor.observation.AdvisorObservationContext;
2824
import org.springframework.ai.chat.client.observation.ChatClientObservationContext;
2925
import org.springframework.ai.chat.model.ChatModel;
30-
import org.springframework.ai.chat.observation.ChatModelCompletionObservationFilter;
3126
import org.springframework.ai.chat.observation.ChatModelCompletionObservationHandler;
3227
import org.springframework.ai.chat.observation.ChatModelMeterObservationHandler;
3328
import org.springframework.ai.chat.observation.ChatModelObservationContext;
34-
import org.springframework.ai.chat.observation.ChatModelPromptContentObservationFilter;
3529
import org.springframework.ai.chat.observation.ChatModelPromptContentObservationHandler;
3630
import org.springframework.ai.embedding.observation.EmbeddingModelObservationContext;
3731
import org.springframework.ai.image.observation.ImageModelObservationContext;
3832
import org.springframework.ai.model.observation.ErrorLoggingObservationHandler;
33+
import org.springframework.ai.observation.TracingAwareLoggingObservationHandler;
3934
import org.springframework.beans.factory.ObjectProvider;
4035
import org.springframework.boot.autoconfigure.AutoConfiguration;
41-
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
42-
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
43-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
44-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
45-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
36+
import org.springframework.boot.autoconfigure.condition.*;
4637
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4738
import org.springframework.context.annotation.Bean;
4839
import org.springframework.context.annotation.Configuration;
4940

41+
import java.util.List;
42+
5043
/**
5144
* Auto-configuration for Spring AI chat model observations.
5245
*
5346
* @author Thomas Vitale
47+
* @author Jonatan Ivanov
5448
* @since 1.0.0
5549
*/
5650
@AutoConfiguration(
@@ -63,12 +57,12 @@ public class ChatObservationAutoConfiguration {
6357

6458
private static void logPromptContentWarning() {
6559
logger.warn(
66-
"You have enabled the inclusion of the prompt content in the observations, with the risk of exposing sensitive or private information. Please, be careful!");
60+
"You have enabled logging out the prompt content with the risk of exposing sensitive or private information. Please, be careful!");
6761
}
6862

6963
private static void logCompletionWarning() {
7064
logger.warn(
71-
"You have enabled the inclusion of the completion content in the observations, with the risk of exposing sensitive or private information. Please, be careful!");
65+
"You have enabled logging out the completion content with the risk of exposing sensitive or private information. Please, be careful!");
7266
}
7367

7468
@Bean
@@ -78,76 +72,66 @@ ChatModelMeterObservationHandler chatModelMeterObservationHandler(ObjectProvider
7872
return new ChatModelMeterObservationHandler(meterRegistry.getObject());
7973
}
8074

81-
/**
82-
* The chat content is typically too big to be included in an observation as span
83-
* attributes. That's why the preferred way to store it is as span events, which are
84-
* supported by OpenTelemetry but not yet surfaced through the Micrometer APIs. This
85-
* primary/fallback configuration is a temporary solution until
86-
* https://github.com/micrometer-metrics/micrometer/issues/5238 is delivered.
87-
*/
8875
@Configuration(proxyBeanMethods = false)
89-
@ConditionalOnClass(OtelTracer.class)
90-
@ConditionalOnBean(OtelTracer.class)
91-
static class PrimaryChatContentObservationConfiguration {
76+
@ConditionalOnClass(Tracer.class)
77+
@ConditionalOnBean(Tracer.class)
78+
static class TracerPresentObservationConfiguration {
9279

9380
@Bean
94-
@ConditionalOnMissingBean
95-
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-prompt",
81+
@ConditionalOnMissingBean(value = ChatModelPromptContentObservationHandler.class,
82+
name = "chatModelPromptContentObservationHandler")
83+
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "log-prompt",
9684
havingValue = "true")
97-
ChatModelPromptContentObservationHandler chatModelPromptContentObservationHandler() {
85+
TracingAwareLoggingObservationHandler<ChatModelObservationContext> chatModelPromptContentObservationHandler(
86+
Tracer tracer) {
9887
logPromptContentWarning();
99-
return new ChatModelPromptContentObservationHandler();
88+
return new TracingAwareLoggingObservationHandler<>(new ChatModelPromptContentObservationHandler(), tracer);
10089
}
10190

10291
@Bean
103-
@ConditionalOnMissingBean
104-
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-completion",
92+
@ConditionalOnMissingBean(value = ChatModelCompletionObservationHandler.class,
93+
name = "chatModelCompletionObservationHandler")
94+
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "log-completion",
10595
havingValue = "true")
106-
ChatModelCompletionObservationHandler chatModelCompletionObservationHandler() {
96+
TracingAwareLoggingObservationHandler<ChatModelObservationContext> chatModelCompletionObservationHandler(
97+
Tracer tracer) {
10798
logCompletionWarning();
108-
return new ChatModelCompletionObservationHandler();
99+
return new TracingAwareLoggingObservationHandler<>(new ChatModelCompletionObservationHandler(), tracer);
100+
}
101+
102+
@Bean
103+
@ConditionalOnMissingBean
104+
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-error-logging",
105+
havingValue = "true")
106+
ErrorLoggingObservationHandler errorLoggingObservationHandler(Tracer tracer) {
107+
return new ErrorLoggingObservationHandler(tracer,
108+
List.of(EmbeddingModelObservationContext.class, ImageModelObservationContext.class,
109+
ChatModelObservationContext.class, ChatClientObservationContext.class,
110+
AdvisorObservationContext.class));
109111
}
110112

111113
}
112114

113115
@Configuration(proxyBeanMethods = false)
114-
@ConditionalOnMissingClass("io.micrometer.tracing.otel.bridge.OtelTracer")
115-
static class FallbackChatContentObservationConfiguration {
116+
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")
117+
static class TracerNotPresentObservationConfiguration {
116118

117119
@Bean
118120
@ConditionalOnMissingBean
119-
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-prompt",
121+
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "log-prompt",
120122
havingValue = "true")
121-
ChatModelPromptContentObservationFilter chatModelPromptObservationFilter() {
123+
ChatModelPromptContentObservationHandler chatModelPromptContentObservationHandler() {
122124
logPromptContentWarning();
123-
return new ChatModelPromptContentObservationFilter();
125+
return new ChatModelPromptContentObservationHandler();
124126
}
125127

126128
@Bean
127129
@ConditionalOnMissingBean
128-
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-completion",
130+
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "log-completion",
129131
havingValue = "true")
130-
ChatModelCompletionObservationFilter chatModelCompletionObservationFilter() {
132+
ChatModelCompletionObservationHandler chatModelCompletionObservationHandler() {
131133
logCompletionWarning();
132-
return new ChatModelCompletionObservationFilter();
133-
}
134-
135-
}
136-
137-
@Configuration(proxyBeanMethods = false)
138-
@ConditionalOnClass(Tracer.class)
139-
@ConditionalOnBean(Tracer.class)
140-
static class TracingChatContentObservationConfiguration {
141-
142-
@Bean
143-
@ConditionalOnMissingBean
144-
@ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-error-logging",
145-
havingValue = "true")
146-
public ErrorLoggingObservationHandler errorLoggingObservationHandler(Tracer tracer) {
147-
return new ErrorLoggingObservationHandler(tracer,
148-
List.of(EmbeddingModelObservationContext.class, ImageModelObservationContext.class,
149-
ChatModelObservationContext.class, ChatClientObservationContext.class,
150-
AdvisorObservationContext.class));
134+
return new ChatModelCompletionObservationHandler();
151135
}
152136

153137
}

0 commit comments

Comments
 (0)