Spring Boot auto-configuration module providing observability capabilities for Spring AI vector store operations through Micrometer integration. Automatically configures observation handlers for monitoring, tracing, and logging vector store queries and responses.
Spring Boot auto-configuration module that automatically sets up observability for Spring AI vector store operations. When this module is on the classpath along with Spring AI's vector store support, it automatically registers observation handlers for monitoring, tracing, and optional logging of vector store operations through Micrometer.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-observation</artifactId>
<version>1.1.2</version>
</dependency>This module provides auto-configuration classes:
import org.springframework.ai.vectorstore.observation.autoconfigure.VectorStoreObservationAutoConfiguration;
import org.springframework.ai.vectorstore.observation.autoconfigure.VectorStoreObservationProperties;The module depends on observation types from spring-ai-vector-store and spring-ai-commons, which are automatically available when those dependencies are present.
This auto-configuration activates automatically when both spring-ai-vector-store and Spring Boot's observation infrastructure are on the classpath. No code changes are required for basic observability.
Simply include the dependency and ensure Spring Boot Actuator is present:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-observation</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>The auto-configuration runs after ObservationAutoConfiguration and instruments vector store operations for metrics and tracing.
To log vector store query responses (disabled by default):
spring.ai.vectorstore.observations.log-query-response=trueSecurity Warning: Enabling query response logging may expose sensitive or private information in logs. Use with caution. The observation handler logs this warning message on startup when enabled:
You have enabled logging out of the query response content with the risk of exposing sensitive or private information. Please, be careful!When io.micrometer:micrometer-tracing is on the classpath, the auto-configuration registers a tracing-aware observation handler instead of the standard handler, enabling distributed tracing for vector store operations.
Complete Tracing Setup Example:
<!-- Required: Base auto-configuration -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-observation</artifactId>
<version>1.1.2</version>
</dependency>
<!-- Required: Actuator for observation infrastructure -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Required for tracing: Micrometer Tracing -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>
<!-- Optional: Tracing bridge (e.g., Brave for Zipkin) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<!-- Optional: Tracing reporter (e.g., Zipkin) -->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>Configuration for Tracing:
spring:
ai:
vectorstore:
observations:
log-query-response: true # Enable observation handler
management:
tracing:
sampling:
probability: 1.0 # Sample all traces (adjust for production)
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans # Zipkin endpointThis module uses Spring Boot's conditional auto-configuration to register observation handlers based on the runtime environment.
VectorStoreObservationAutoConfiguration provides two mutually exclusive configurations:
TracerPresentObservationConfiguration: Activates when io.micrometer.tracing.Tracer is available (class and bean present). Registers a TracingAwareLoggingObservationHandler wrapping a VectorStoreQueryResponseObservationHandler to enable tracing-aware logging.
TracerNotPresentObservationConfiguration: Activates when io.micrometer.tracing.Tracer is NOT on the classpath. Registers a standard VectorStoreQueryResponseObservationHandler for basic logging.
Both configurations activate only when spring.ai.vectorstore.observations.log-query-response=true is set, allowing applications to control whether query responses are logged.
The auto-configuration uses these Spring Boot conditional annotations:
@ConditionalOnClass(VectorStore.class) - Requires vector store support from spring-ai-vector-store@ConditionalOnClass(Tracer.class) - Requires Micrometer tracing on classpath (for tracing variant)@ConditionalOnMissingClass("io.micrometer.tracing.Tracer") - Ensures tracing is NOT present (for non-tracing variant)@ConditionalOnBean(Tracer.class) - Ensures Tracer bean exists in context (when class present)@ConditionalOnProperty - Enables handler based on configuration property@ConditionalOnMissingBean - Allows custom handler overridesThe auto-configuration runs after ObservationAutoConfiguration (via @AutoConfiguration(afterName="org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration")), ensuring the observation infrastructure is ready before registering handlers.
ObservationRegistryThe main auto-configuration class that sets up vector store observation handlers.
package org.springframework.ai.vectorstore.observation.autoconfigure;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* Auto-configuration for Spring AI vector store observation support.
*
* <p>This configuration automatically registers observation handlers for monitoring
* and logging vector store operations when the appropriate dependencies and
* configuration properties are present.
*
* <p>Activation requires:
* <ul>
* <li>{@code VectorStore.class} on classpath (from spring-ai-vector-store)</li>
* <li>Runs after {@link ObservationAutoConfiguration} to ensure observation
* infrastructure is initialized</li>
* </ul>
*
* <p>This class contains two mutually exclusive nested configurations:
* <ul>
* <li>{@code TracerPresentObservationConfiguration} - Registers tracing-aware handler
* when Micrometer tracing is available</li>
* <li>{@code TracerNotPresentObservationConfiguration} - Registers standard handler
* when tracing is not available</li>
* </ul>
*
* @see VectorStoreObservationProperties
* @see ObservationAutoConfiguration
*/
@AutoConfiguration(afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration")
@ConditionalOnClass(VectorStore.class)
@EnableConfigurationProperties(VectorStoreObservationProperties.class)
public class VectorStoreObservationAutoConfiguration {
// Contains nested configuration classes that register observation handlers
}Activation Conditions:
VectorStore.class must be on the classpath (from spring-ai-vector-store)ObservationAutoConfigurationVectorStoreObservationPropertiesNested Configuration Classes (package-private static inner classes):
TracerPresentObservationConfiguration - Registers tracing-aware handler when Tracer availableTracerNotPresentObservationConfiguration - Registers standard handler when Tracer not availableBean Scope: Configuration class beans are singletons by default; proxyBeanMethods = false on nested classes means Spring won't create CGLIB proxies, improving startup performance.
Configuration properties for customizing observation behavior.
package org.springframework.ai.vectorstore.observation.autoconfigure;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for vector store observations.
*
* <p>These properties control the behavior of observation handlers registered by
* {@link VectorStoreObservationAutoConfiguration}.
*
* <p>Property prefix: {@value #CONFIG_PREFIX}
*
* @see VectorStoreObservationAutoConfiguration
*/
@ConfigurationProperties(CONFIG_PREFIX)
public class VectorStoreObservationProperties {
/**
* Configuration property prefix for vector store observations.
* Value: "spring.ai.vectorstore.observations"
*/
public static final String CONFIG_PREFIX = "spring.ai.vectorstore.observations";
/**
* Whether to log the search response content in the observations.
*
* <p>When enabled, observation handlers will log the full content of vector
* store query responses, which may include:
* <ul>
* <li>Retrieved document content</li>
* <li>Similarity scores</li>
* <li>Metadata associated with results</li>
* <li>Embedding vectors (if included in response)</li>
* </ul>
*
* <p>Default: {@code false}
*
* <p><strong>Security Warning:</strong> Enabling this may expose sensitive or
* private information in application logs. Only enable in development or when
* you have verified that response content does not contain sensitive data.
* Ensure proper log access controls and retention policies are in place.
*
* <p>When this property is {@code true}, triggers registration of observation
* handler beans that log query responses.
*/
private boolean logQueryResponse = false;
/**
* Returns whether query response logging is enabled.
*
* @return {@code true} if query response logging is enabled, {@code false} otherwise
*/
public boolean isLogQueryResponse() {
return this.logQueryResponse;
}
/**
* Sets whether query response logging is enabled.
*
* @param logQueryResponse {@code true} to enable logging, {@code false} to disable
*/
public void setLogQueryResponse(boolean logQueryResponse) {
this.logQueryResponse = logQueryResponse;
}
}Property:
spring.ai.vectorstore.observations.log-query-response (boolean, default: false)
"true" (string) in properties files, or true (boolean) in YAMLProperty Binding: This class is automatically bound by Spring Boot's @EnableConfigurationProperties annotation on VectorStoreObservationAutoConfiguration.
When Micrometer tracing is NOT available, registers a standard observation handler.
package org.springframework.ai.vectorstore.observation.autoconfigure;
import org.springframework.ai.vectorstore.observation.VectorStoreQueryResponseObservationHandler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration for vector store observation when Micrometer tracing is not available.
*
* <p>This configuration registers a standard {@link VectorStoreQueryResponseObservationHandler}
* that logs query responses without tracing integration.
*
* <p>Activation requires:
* <ul>
* <li>{@code io.micrometer.tracing.Tracer} class NOT on classpath</li>
* <li>{@code spring.ai.vectorstore.observations.log-query-response=true}</li>
* <li>No existing {@link VectorStoreQueryResponseObservationHandler} bean</li>
* </ul>
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")
static class TracerNotPresentObservationConfiguration {
/**
* Registers a VectorStoreQueryResponseObservationHandler when tracing is not available.
*
* <p>This handler logs vector store query responses to help with debugging and
* monitoring. It logs a warning message on startup about potential sensitive data
* exposure.
*
* <p>Bean name: {@code vectorStoreQueryResponseObservationHandler}
* <p>Bean scope: Singleton (default)
* <p>Lazy initialization: No (eager by default)
*
* @return a new instance of VectorStoreQueryResponseObservationHandler
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(
prefix = VectorStoreObservationProperties.CONFIG_PREFIX,
name = "log-query-response",
havingValue = "true"
)
public VectorStoreQueryResponseObservationHandler vectorStoreQueryResponseObservationHandler() {
return new VectorStoreQueryResponseObservationHandler();
}
}Bean: vectorStoreQueryResponseObservationHandler
VectorStoreQueryResponseObservationHandler (from spring-ai-vector-store)"vectorStoreQueryResponseObservationHandler"@Bean methods)io.micrometer.tracing.Tracer class NOT on classpathspring.ai.vectorstore.observations.log-query-response=true (exact value match)VectorStoreQueryResponseObservationHandler bean in contextVectorStoreQueryResponseObservationHandlerObservationRegistryVectorStoreObservationContextHandler Lifecycle:
ObservationRegistry by Spring BootWhen Micrometer tracing IS available, registers a tracing-aware observation handler.
package org.springframework.ai.vectorstore.observation.autoconfigure;
import io.micrometer.tracing.Tracer;
import org.springframework.ai.observation.tracing.TracingAwareLoggingObservationHandler;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.ai.vectorstore.observation.VectorStoreQueryResponseObservationHandler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration for vector store observation when Micrometer tracing is available.
*
* <p>This configuration registers a {@link TracingAwareLoggingObservationHandler} that
* wraps {@link VectorStoreQueryResponseObservationHandler} to provide tracing-aware
* logging with proper log correlation.
*
* <p>Activation requires:
* <ul>
* <li>{@code io.micrometer.tracing.Tracer} class on classpath</li>
* <li>{@code Tracer} bean exists in application context</li>
* <li>{@code spring.ai.vectorstore.observations.log-query-response=true}</li>
* <li>No existing {@link VectorStoreQueryResponseObservationHandler} bean</li>
* </ul>
*
* <p>The tracing-aware handler adds trace and span IDs to log messages, enabling
* correlation between logs and distributed traces in observability platforms.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Tracer.class)
@ConditionalOnBean(Tracer.class)
static class TracerPresentObservationConfiguration {
/**
* Registers a tracing-aware observation handler when Micrometer tracing is available.
*
* <p>This handler wraps a {@link VectorStoreQueryResponseObservationHandler} with
* {@link TracingAwareLoggingObservationHandler} to add trace context (trace ID,
* span ID) to log messages.
*
* <p>The handler logs query responses with full tracing context, allowing logs to
* be correlated with distributed traces in systems like Zipkin, Jaeger, or
* Application Insights.
*
* <p>Bean name: {@code vectorStoreQueryResponseObservationHandler}
* <p>Bean scope: Singleton (default)
* <p>Lazy initialization: No (eager by default)
*
* @param tracer the Micrometer Tracer bean for distributed tracing integration;
* injected by Spring when available in the context
* @return a new instance of TracingAwareLoggingObservationHandler wrapping
* VectorStoreQueryResponseObservationHandler
*/
@Bean
@ConditionalOnMissingBean(
value = VectorStoreQueryResponseObservationHandler.class,
name = "vectorStoreQueryResponseObservationHandler"
)
@ConditionalOnProperty(
prefix = VectorStoreObservationProperties.CONFIG_PREFIX,
name = "log-query-response",
havingValue = "true"
)
public TracingAwareLoggingObservationHandler<VectorStoreObservationContext> vectorStoreQueryResponseObservationHandler(
Tracer tracer) {
return new TracingAwareLoggingObservationHandler<>(
new VectorStoreQueryResponseObservationHandler(),
tracer
);
}
}Bean: vectorStoreQueryResponseObservationHandler
TracingAwareLoggingObservationHandler<VectorStoreObservationContext> (from spring-ai-commons)"vectorStoreQueryResponseObservationHandler"@Bean methods)tracer (Tracer) - Micrometer Tracer for distributed tracing, injected by Springio.micrometer.tracing.Tracer class on classpathTracer bean exists in application contextspring.ai.vectorstore.observations.log-query-response=true (exact value match)VectorStoreQueryResponseObservationHandler bean or bean named vectorStoreQueryResponseObservationHandlerVectorStoreQueryResponseObservationHandler with tracing awarenessObservationRegistryVectorStoreObservationContextHandler Structure:
TracingAwareLoggingObservationHandler
└── wraps: VectorStoreQueryResponseObservationHandler
└── logs: query response content with trace contextTrace Context in Logs: When active, log messages include structured trace information such as:
[traceId=64c3a3c3e8b3a9d1 spanId=64c3a3c3e8b3a9d1] Vector store query response: [content...]The auto-configuration registers observation handler beans that integrate with Spring AI's vector store observation infrastructure. These handlers are from the spring-ai-vector-store and spring-ai-commons modules.
When spring.ai.vectorstore.observations.log-query-response=true is set:
VectorStoreQueryResponseObservationHandler which logs query response content to the application loggerTracingAwareLoggingObservationHandler wrapping the query response handler, enabling log correlation with distributed traces by adding trace and span IDsBoth variants log this warning on startup:
You have enabled logging out of the query response content with the risk of exposing sensitive or private information. Please, be careful!The observation handlers implement Micrometer's ObservationHandler interface for processing observation events.
VectorStoreQueryResponseObservationHandler (from spring-ai-vector-store):
package org.springframework.ai.vectorstore.observation;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
/**
* Handler that logs vector store query responses.
*
* <p>Supports observations with context type {@link VectorStoreObservationContext}.
*/
public class VectorStoreQueryResponseObservationHandler
implements ObservationHandler<VectorStoreObservationContext> {
/**
* Constructor that logs a security warning about sensitive data exposure.
*/
public VectorStoreQueryResponseObservationHandler();
/**
* Indicates this handler supports VectorStoreObservationContext.
*
* @param context the observation context to check
* @return true if context is an instance of VectorStoreObservationContext
*/
@Override
public boolean supportsContext(Observation.Context context);
/**
* Called when an observation stops. Logs the query response content.
*
* @param context the observation context containing query and response data
*/
@Override
public void onStop(VectorStoreObservationContext context);
}TracingAwareLoggingObservationHandler (from spring-ai-commons):
package org.springframework.ai.observation.tracing;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.tracing.Tracer;
/**
* Generic tracing-aware wrapper for observation handlers that adds trace context to logs.
*
* @param <T> the observation context type
*/
public class TracingAwareLoggingObservationHandler<T extends Observation.Context>
implements ObservationHandler<T> {
/**
* Creates a tracing-aware handler wrapping a delegate.
*
* @param delegate the handler to wrap
* @param tracer the tracer for accessing current trace context
*/
public TracingAwareLoggingObservationHandler(ObservationHandler<T> delegate, Tracer tracer);
/**
* Delegates to wrapped handler's supportsContext method.
*
* @param context the observation context to check
* @return true if the delegate supports the context
*/
@Override
public boolean supportsContext(Observation.Context context);
/**
* Called when observation stops. Extracts trace context and delegates to wrapped handler.
*
* @param context the observation context
*/
@Override
public void onStop(T context);
/**
* Additional handler lifecycle methods (onStart, onError, etc.) delegate to wrapped handler.
*/
}The registered handlers work with:
VectorStoreObservationContext - Captures operation metadata including query, filter, topK, similarity threshold, and response (from spring-ai-vector-store)VectorStoreObservationConvention - Defines observation naming and tagging conventions (from spring-ai-vector-store)DefaultVectorStoreObservationConvention - Default implementation of naming conventions (from spring-ai-vector-store)Observation Event Flow:
Vector Store Operation
↓
Observation.createNotStarted()
↓
Observation.start()
↓
VectorStore.similaritySearch() / add() / delete()
↓
Observation.stop()
↓
ObservationHandler.onStop()
↓
VectorStoreQueryResponseObservationHandler logs response
↓
Metrics exported to backend (Prometheus, etc.)
Traces exported to backend (Zipkin, Jaeger, etc.)For details on the observation context, conventions, and metadata keys, refer to the spring-ai-vector-store module documentation.
The observation handlers process contexts containing these key metadata elements:
Query Metadata (captured in VectorStoreObservationContext):
Response Metadata (logged when enabled):
Standard Observation Tags (added by convention):
db.system - Vector store type (e.g., "pinecone", "redis", "chromadb")db.operation - Operation type (e.g., "query", "add", "delete")db.vector_query.top_k - Number of results requesteddb.vector_query.similarity_threshold - Minimum similarity scoreVectorStoreObservationConventionThis auto-configuration module uses types from other Spring AI modules. These are provided for reference but are not part of this package's API.
The following types are used by this auto-configuration:
Observation.ContextVectorStoreObservationContext Key Methods:
package org.springframework.ai.vectorstore.observation;
import io.micrometer.observation.Observation;
import java.util.List;
/**
* Observation context for vector store operations.
*/
public class VectorStoreObservationContext extends Observation.Context {
/**
* Gets the query text or description.
* @return the query string
*/
public String getQuery();
/**
* Sets the query text.
* @param query the query to set
*/
public void setQuery(String query);
/**
* Gets the search filter expression.
* @return the filter criteria
*/
public String getFilter();
/**
* Sets the search filter.
* @param filter the filter to apply
*/
public void setFilter(String filter);
/**
* Gets the number of top results requested.
* @return the topK value
*/
public Integer getTopK();
/**
* Sets the number of top results to retrieve.
* @param topK the maximum number of results
*/
public void setTopK(Integer topK);
/**
* Gets the similarity threshold for filtering results.
* @return the threshold value (typically 0.0 to 1.0)
*/
public Double getSimilarityThreshold();
/**
* Sets the similarity threshold.
* @param threshold the minimum similarity score
*/
public void setSimilarityThreshold(Double threshold);
/**
* Gets the query response/results.
* @return list of search results
*/
public List<?> getQueryResponse();
/**
* Sets the query response.
* @param response the search results
*/
public void setQueryResponse(List<?> response);
/**
* Gets the operation type.
* @return operation name (e.g., "query", "add", "delete")
*/
public String getOperationType();
/**
* Sets the operation type.
* @param operationType the operation name
*/
public void setOperationType(String operationType);
}For complete API documentation of these types, refer to the spring-ai-vector-store module.
TracingAwareLoggingObservationHandler Constructor:
package org.springframework.ai.observation.tracing;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.tracing.Tracer;
/**
* Constructs a tracing-aware wrapper.
*
* @param delegate the handler to wrap; must not be null
* @param tracer the tracer for trace context; must not be null
* @throws IllegalArgumentException if delegate or tracer is null
*/
public TracingAwareLoggingObservationHandler(
ObservationHandler<T> delegate,
Tracer tracer
);For complete API documentation, refer to the spring-ai-commons module.
Controls whether observation handlers are registered to log vector store query responses.
Properties format:
spring.ai.vectorstore.observations.log-query-response=trueYAML format:
spring:
ai:
vectorstore:
observations:
log-query-response: trueEnvironment Variable format:
export SPRING_AI_VECTORSTORE_OBSERVATIONS_LOG_QUERY_RESPONSE=trueCommand-Line Argument format:
java -jar app.jar --spring.ai.vectorstore.observations.log-query-response=truefalsetrue, registers observation handler beans that log query response content@ConditionalOnProperty annotation uses havingValue="true", so the property must be set to the exact string "true" (case-insensitive) in properties filesProperty Precedence (Spring Boot standard order):
--spring.ai.vectorstore.observations.log-query-response=true)System.getProperties())SPRING_AI_VECTORSTORE_OBSERVATIONS_LOG_QUERY_RESPONSE)application-{profile}.properties)application.properties or application.yml)This auto-configuration integrates with Spring Boot's observability infrastructure:
spring-ai-vector-store is on the classpath, auto-configuration activates automatically via Spring Boot's auto-configuration mechanismObservationAutoConfiguration to ensure Micrometer observation support is ready@ConditionalOnClass, @ConditionalOnBean, @ConditionalOnMissingBean, @ConditionalOnProperty, and @ConditionalOnMissingClass to register the appropriate handler variantspring.ai.vectorstore.observations.* properties via VectorStoreObservationPropertiesThis module includes a META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports file that lists:
org.springframework.ai.vectorstore.observation.autoconfigure.VectorStoreObservationAutoConfigurationSpring Boot's auto-configuration mechanism automatically discovers and processes this class during application startup.
Custom observation handlers can be registered by defining beans that satisfy the @ConditionalOnMissingBean conditions:
Option 1: Override with Same Bean Name
import org.springframework.ai.vectorstore.observation.VectorStoreQueryResponseObservationHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomObservationConfig {
@Bean
public VectorStoreQueryResponseObservationHandler vectorStoreQueryResponseObservationHandler() {
// Custom handler implementation or subclass
return new CustomVectorStoreQueryResponseObservationHandler();
}
}Option 2: Override with Custom Implementation
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import io.micrometer.observation.ObservationHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomObservationConfig {
@Bean
public ObservationHandler<VectorStoreObservationContext> customVectorStoreHandler() {
return new ObservationHandler<VectorStoreObservationContext>() {
@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof VectorStoreObservationContext;
}
@Override
public void onStop(VectorStoreObservationContext context) {
// Custom logging, metrics, or processing logic
// Can filter sensitive data before logging
String sanitizedResponse = sanitize(context.getQueryResponse());
log.info("Vector store response: {}", sanitizedResponse);
}
private String sanitize(List<?> response) {
// Remove or redact sensitive information
return "Returned " + response.size() + " results";
}
};
}
}Option 3: Additional Handler (does not override)
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import io.micrometer.observation.ObservationHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AdditionalObservationConfig {
/**
* Registers an additional handler that runs alongside the default handler.
* Both handlers will process observations.
*/
@Bean
public ObservationHandler<VectorStoreObservationContext> metricsOnlyHandler() {
return new ObservationHandler<VectorStoreObservationContext>() {
@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof VectorStoreObservationContext;
}
@Override
public void onStop(VectorStoreObservationContext context) {
// Export custom metrics without logging sensitive data
int resultCount = context.getQueryResponse() != null
? context.getQueryResponse().size()
: 0;
// Record metric...
}
};
}
}The auto-configuration's @ConditionalOnMissingBean ensures custom beans take precedence when the bean name or type matches.
To completely disable vector store observation auto-configuration:
application.properties:
spring.autoconfigure.exclude=org.springframework.ai.vectorstore.observation.autoconfigure.VectorStoreObservationAutoConfigurationapplication.yml:
spring:
autoconfigure:
exclude:
- org.springframework.ai.vectorstore.observation.autoconfigure.VectorStoreObservationAutoConfiguration@SpringBootApplication annotation:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.ai.vectorstore.observation.autoconfigure.VectorStoreObservationAutoConfiguration;
@SpringBootApplication(exclude = VectorStoreObservationAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}org.springframework.ai:spring-ai-vector-store - Provides observation infrastructure, types, and handlers
spring-ai-autoconfigure-vector-store-observation versionVectorStore, VectorStoreObservationContext, VectorStoreQueryResponseObservationHandlerorg.springframework.boot:spring-boot-starter - Spring Boot core and auto-configuration support
@AutoConfiguration, @EnableConfigurationProperties, conditional annotationsio.micrometer:micrometer-tracing - Enables tracing-aware handler variant
TracingAwareLoggingObservationHandler with trace contextVectorStoreQueryResponseObservationHandlerTracer interface for trace context accessorg.springframework.boot:spring-boot-starter-actuator - Provides observation infrastructure via ObservationAutoConfiguration
ObservationRegistry, ObservationAutoConfigurationorg.springframework.boot:spring-boot-configuration-processor - Generates configuration metadata for IDE support
optional or providedMETA-INF/spring-configuration-metadata.jsonorg.springframework.boot:spring-boot-autoconfigure-processor - Generates auto-configuration metadata
optional or providedMETA-INF/spring-autoconfigure-metadata.jsonThe following dependencies are transitively included via spring-ai-vector-store and spring-boot-starter-actuator:
io.micrometer:micrometer-observation - Core observation APIio.micrometer:micrometer-core - Metrics infrastructureorg.springframework:spring-context - Spring Framework coreorg.springframework:spring-boot - Spring Boot coreMinimal Setup (Without Tracing):
<dependencies>
<!-- Spring AI Vector Store Observation Auto-Configuration -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-observation</artifactId>
<version>1.1.2</version>
</dependency>
<!-- Required: Actuator for observation infrastructure -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Required: Vector store implementation (example: ChromaDB) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
</dependency>
</dependencies>Full Setup (With Tracing and Metrics Export):
<dependencies>
<!-- Spring AI Vector Store Observation Auto-Configuration -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-observation</artifactId>
<version>1.1.2</version>
</dependency>
<!-- Required: Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Required: Vector store implementation -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
</dependency>
<!-- Optional: Micrometer Tracing for trace context -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>
<!-- Optional: Tracing bridge (Brave for Zipkin) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<!-- Optional: Zipkin reporter -->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<!-- Optional: Prometheus metrics export -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>Enable query response logging for debugging:
application.yml:
spring:
ai:
vectorstore:
observations:
log-query-response: true
logging:
level:
org.springframework.ai.vectorstore.observation: DEBUGResult: All vector store operations log query responses to the application logger.
Enable tracing to correlate vector store operations with distributed traces:
pom.xml:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>application.yml:
spring:
ai:
vectorstore:
observations:
log-query-response: true
management:
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spansResult: Vector store operations appear as spans in distributed traces with query response logs correlated via trace ID.
Export vector store metrics without logging sensitive responses:
pom.xml:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>application.yml:
spring:
ai:
vectorstore:
observations:
log-query-response: false # Disable logging
management:
endpoints:
web:
exposure:
include: prometheus
metrics:
export:
prometheus:
enabled: trueResult: Metrics like vectorstore.query.duration, vectorstore.query.count exported to Prometheus without logging response content. Access metrics at /actuator/prometheus.
Create a custom handler that filters sensitive data before logging:
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.stream.Collectors;
@Configuration
public class FilteredObservationConfig {
private static final Logger log = LoggerFactory.getLogger(FilteredObservationConfig.class);
/**
* Custom handler that logs response metadata without sensitive content.
*/
@Bean
public ObservationHandler<VectorStoreObservationContext> vectorStoreQueryResponseObservationHandler() {
return new ObservationHandler<VectorStoreObservationContext>() {
@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof VectorStoreObservationContext;
}
@Override
public void onStop(VectorStoreObservationContext context) {
// Log metadata without sensitive content
int resultCount = context.getQueryResponse() != null
? context.getQueryResponse().size()
: 0;
log.info("Vector store query completed: operation={}, topK={}, resultCount={}, similarityThreshold={}",
context.getOperationType(),
context.getTopK(),
resultCount,
context.getSimilarityThreshold()
);
// Optionally log filtered/redacted results
if (context.getQueryResponse() != null && !context.getQueryResponse().isEmpty()) {
List<String> resultSummaries = context.getQueryResponse().stream()
.map(result -> "Document[id=..., score=...]") // Redact actual content
.limit(5) // Limit to first 5
.collect(Collectors.toList());
log.debug("Sample results: {}", resultSummaries);
}
}
};
}
}application.yml:
spring:
ai:
vectorstore:
observations:
log-query-response: true # Enables handler registrationResult: Logs vector store metrics and metadata without exposing document content or queries.
Enable detailed logging only in development:
import org.springframework.ai.vectorstore.observation.VectorStoreQueryResponseObservationHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class ProfileBasedObservationConfig {
/**
* In development, use default handler with full logging.
*/
@Bean
@Profile("dev")
public VectorStoreQueryResponseObservationHandler devObservationHandler() {
return new VectorStoreQueryResponseObservationHandler();
}
/**
* In production, use custom handler with filtered logging.
*/
@Bean
@Profile("prod")
public ObservationHandler<VectorStoreObservationContext> prodObservationHandler() {
return new FilteredLoggingHandler(); // Custom implementation
}
}application-dev.yml:
spring:
ai:
vectorstore:
observations:
log-query-response: true
logging:
level:
org.springframework.ai: DEBUGapplication-prod.yml:
spring:
ai:
vectorstore:
observations:
log-query-response: true
logging:
level:
org.springframework.ai: INFOResult: Development environment logs full responses; production logs only metadata.
Symptom: Vector store operations are not logged or observed.
Causes and Solutions:
Property not set: Ensure spring.ai.vectorstore.observations.log-query-response=true
spring:
ai:
vectorstore:
observations:
log-query-response: trueMissing Actuator dependency: Add Spring Boot Actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>Missing vector store dependency: Ensure a vector store implementation is present
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
</dependency>Auto-configuration excluded: Check that VectorStoreObservationAutoConfiguration is not excluded
# Remove or comment out:
# spring:
# autoconfigure:
# exclude:
# - org.springframework.ai.vectorstore.observation.autoconfigure.VectorStoreObservationAutoConfigurationCustom bean overriding: Check if a custom handler bean is preventing registration
@ConditionalOnMissingBean on custom handlers if you want fallback behaviorVerification: Enable debug logging to see auto-configuration decisions:
logging:
level:
org.springframework.boot.autoconfigure: DEBUGSymptom: Logs don't include trace ID and span ID despite having Micrometer Tracing on classpath.
Causes and Solutions:
Tracer bean not registered: Ensure a Tracer implementation is configured
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>Tracing not enabled: Configure tracing sampling
management:
tracing:
sampling:
probability: 1.0Wrong handler registered: Check which handler is active
# Should show TracingAwareLoggingObservationHandler
curl http://localhost:8080/actuator/beans | jq '.contexts[].beans | to_entries[] | select(.key | contains("vectorStoreQuery"))'Symptom: Application performance degrades with observation enabled.
Causes and Solutions:
Excessive logging: Disable query response logging in production
spring:
ai:
vectorstore:
observations:
log-query-response: false # Disable in productionHigh sampling rate: Reduce tracing sampling for high-throughput applications
management:
tracing:
sampling:
probability: 0.1 # Sample 10% of tracesLarge responses: Implement custom handler that logs summaries instead of full content
@Bean
public ObservationHandler<VectorStoreObservationContext> optimizedHandler() {
return new SummaryOnlyObservationHandler();
}Symptom: Sensitive information appears in logs.
Solutions:
Disable logging: Turn off query response logging
spring:
ai:
vectorstore:
observations:
log-query-response: falseUse custom handler with filtering: Implement a handler that redacts sensitive data
@Bean
public ObservationHandler<VectorStoreObservationContext> secureHandler() {
return new RedactingSensitiveDataHandler();
}Configure log masking: Use logging framework features to mask patterns
<!-- Logback configuration -->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</layout>
<maskingRule>
<regex>email\":\s*\"([^\"]+)\"</regex>
<replacement>email": "***@***.***"</replacement>
</maskingRule>
</encoder>
</appender>
</configuration>Symptom: Vector store metrics don't appear in Prometheus or other backends.
Causes and Solutions:
Missing metrics registry: Add Micrometer registry dependency
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>Endpoint not exposed: Expose metrics endpoint
management:
endpoints:
web:
exposure:
include: prometheus,metricsObservation not started: Ensure vector store operations use Spring AI's observation-enabled implementations
Verify metrics: Check available metrics
curl http://localhost:8080/actuator/metrics | jq '.names[] | select(. | contains("vector"))'Symptom: Application fails to start with missing class errors.
Common Scenarios:
Missing spring-ai-vector-store:
java.lang.ClassNotFoundException: org.springframework.ai.vectorstore.VectorStoreSolution: Add vector store dependency
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
</dependency>Missing Micrometer Observation:
java.lang.NoClassDefFoundError: io/micrometer/observation/ObservationSolution: Add Spring Boot Actuator (includes Micrometer)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>Tracing class missing:
java.lang.ClassNotFoundException: io.micrometer.tracing.TracerSolution: This is expected if tracing is not intended; the auto-configuration handles this via @ConditionalOnClass. If tracing is intended, add:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>Export vector store metrics to multiple backends simultaneously:
management:
metrics:
export:
prometheus:
enabled: true
datadog:
enabled: true
api-key: ${DATADOG_API_KEY}
cloudwatch:
enabled: true
namespace: VectorStoreAppDependencies:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-datadog</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-cloudwatch2</artifactId>
</dependency>Override observation naming and tagging conventions:
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import io.micrometer.observation.Observation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomConventionConfig {
@Bean
public VectorStoreObservationConvention customVectorStoreConvention() {
return new VectorStoreObservationConvention() {
@Override
public String getName() {
return "custom.vectorstore.query"; // Custom observation name
}
@Override
public String getContextualName(VectorStoreObservationContext context) {
return "vectorstore." + context.getOperationType();
}
@Override
public KeyValues getLowCardinalityKeyValues(VectorStoreObservationContext context) {
return KeyValues.of(
"store.type", "custom",
"operation", context.getOperationType(),
"has.filter", context.getFilter() != null ? "true" : "false"
);
}
@Override
public KeyValues getHighCardinalityKeyValues(VectorStoreObservationContext context) {
return KeyValues.of(
"query.topk", String.valueOf(context.getTopK()),
"query.threshold", String.valueOf(context.getSimilarityThreshold())
);
}
};
}
}Create a handler that performs multiple actions:
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.atomic.AtomicInteger;
@Configuration
public class CompositeHandlerConfig {
private final AtomicInteger queryCounter = new AtomicInteger(0);
@Bean
public ObservationHandler<VectorStoreObservationContext> compositeHandler() {
return new ObservationHandler<VectorStoreObservationContext>() {
@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof VectorStoreObservationContext;
}
@Override
public void onStart(VectorStoreObservationContext context) {
// Log query initiation
queryCounter.incrementAndGet();
}
@Override
public void onStop(VectorStoreObservationContext context) {
// Log results summary
int resultCount = context.getQueryResponse() != null
? context.getQueryResponse().size()
: 0;
// Update custom metrics
// recordCustomMetrics(context, resultCount);
// Send to external monitoring
// sendToExternalSystem(context);
// Log sanitized summary
log.info("Query #{}: operation={}, results={}",
queryCounter.get(),
context.getOperationType(),
resultCount
);
}
@Override
public void onError(VectorStoreObservationContext context) {
// Handle and log errors
log.error("Query #{} failed: operation={}",
queryCounter.get(),
context.getOperationType()
);
}
};
}
}Only observe specific types of vector store operations:
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import io.micrometer.observation.ObservationHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SelectiveObservationConfig {
@Bean
public ObservationHandler<VectorStoreObservationContext> selectiveHandler() {
return new ObservationHandler<VectorStoreObservationContext>() {
@Override
public boolean supportsContext(Observation.Context context) {
if (!(context instanceof VectorStoreObservationContext)) {
return false;
}
VectorStoreObservationContext vsContext = (VectorStoreObservationContext) context;
// Only observe query operations, not add/delete
return "query".equals(vsContext.getOperationType());
}
@Override
public void onStop(VectorStoreObservationContext context) {
// Process only query operations
log.info("Query operation completed with {} results",
context.getQueryResponse() != null ? context.getQueryResponse().size() : 0
);
}
};
}
}This module version 1.1.2 is designed to work with Spring AI 1.1.x releases. Ensure all Spring AI modules use compatible versions:
<properties>
<spring-ai.version>1.1.2</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-observation</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
<version>${spring-ai.version}</version>
</dependency>
</dependencies>When enabling query response logging, consider:
spring:
ai:
vectorstore:
observations:
log-query-response: false # Disabled for security
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus # Don't expose sensitive endpoints
metrics:
export:
prometheus:
enabled: true
tracing:
sampling:
probability: 0.01 # Low sampling rate in production
logging:
level:
org.springframework.ai.vectorstore: INFO # Minimal loggingUse custom handlers to log metadata without sensitive content:
@Bean
public ObservationHandler<VectorStoreObservationContext> productionHandler() {
return new MetadataOnlyObservationHandler(); // Logs counts and timing, not content
}tessl i tessl/maven-org-springframework-ai--spring-ai-autoconfigure-vector-store-observation@1.1.1