Compare commits

..

No commits in common. 'main' and '3.1.x' have entirely different histories.
main ... 3.1.x

@ -2,16 +2,6 @@
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="LombokGetterMayBeUsed" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NullableProblems" enabled="false" level="WARNING" enabled_by_default="false">
<option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
<option name="REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL" value="true" />
<option name="REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE" value="true" />
<option name="REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL" value="true" />
<option name="REPORT_NOT_ANNOTATED_GETTER" value="true" />
<option name="REPORT_NOT_ANNOTATED_SETTER_PARAMETER" value="true" />
<option name="REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS" value="true" />
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
</inspection_tool>
<inspection_tool class="UnqualifiedFieldAccess" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
</profile>
</component>
</component>

@ -4,7 +4,6 @@
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.

@ -1,4 +1,4 @@
= Spring Boot image:https://ci.spring.io/api/v1/teams/spring-boot/pipelines/spring-boot-3.2.x/jobs/build/badge["Build Status", link="https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-3.2.x?groups=Build"] image:https://badges.gitter.im/Join Chat.svg["Chat",link="https://gitter.im/spring-projects/spring-boot?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?&search.rootProjectNames=Spring%20Boot%20Build&search.rootProjectNames=spring-boot-build"]
= Spring Boot image:https://ci.spring.io/api/v1/teams/spring-boot/pipelines/spring-boot-3.1.x/jobs/build/badge["Build Status", link="https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-3.1.x?groups=Build"] image:https://badges.gitter.im/Join Chat.svg["Chat",link="https://gitter.im/spring-projects/spring-boot?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?&search.rootProjectNames=Spring%20Boot%20Build&search.rootProjectNames=spring-boot-build"]
:docs: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference
:github: https://github.com/spring-projects/spring-boot
@ -10,7 +10,6 @@ We also provide a command-line tool that runs Spring scripts.
Our primary goals are:
* Provide a radically faster and widely accessible getting started experience for all Spring development.
* Be opinionated, but get out of the way quickly as requirements start to diverge from the defaults.
* Provide a range of non-functional features common to large classes of projects (for example, embedded servers, security, metrics, health checks, externalized configuration).

@ -6,8 +6,6 @@ plugins {
description = "Spring Boot Build"
defaultTasks 'build'
nohttp {

@ -18,11 +18,10 @@ new File(projectDir.parentFile, "gradle.properties").withInputStream {
def properties = new Properties()
properties.load(it)
["assertj", "commonsCodec", "hamcrest", "jackson", "junitJupiter",
"kotlin", "maven"].each {
"kotlin", "maven", "springFramework"].each {
versions[it] = properties[it + "Version"]
}
}
versions["springFramework"] = "6.0.12"
ext.set("versions", versions)
if (versions.springFramework.contains("-")) {
repositories {

@ -134,7 +134,7 @@ class AsciidoctorConventions {
private String determineGitHubTag(Project project) {
String version = "v" + project.getVersion();
return (version.endsWith("-SNAPSHOT")) ? "main" : version;
return (version.endsWith("-SNAPSHOT")) ? "3.1.x" : version;
}
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) {

@ -19,11 +19,13 @@ package org.springframework.boot.build.bom.bomr;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -94,14 +96,19 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
getLaterVersionsForModule(group.getId(), plugin, library));
}
}
return moduleVersions.values()
List<DependencyVersion> allVersions = moduleVersions.values()
.stream()
.flatMap(SortedSet::stream)
.distinct()
.filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion))
.map((version) -> (VersionOption) new VersionOption.ResolvedVersionOption(version,
getMissingModules(moduleVersions, version)))
.toList();
if (allVersions.isEmpty()) {
return Collections.emptyList();
}
return allVersions.stream()
.map((version) -> new VersionOption.ResolvedVersionOption(version,
getMissingModules(moduleVersions, version)))
.collect(Collectors.toList());
}
private List<String> getMissingModules(Map<String, SortedSet<DependencyVersion>> moduleVersions,

@ -106,7 +106,6 @@ public class DocumentConfigurationProperties extends DefaultTask {
config.accept("spring.reactor");
config.accept("spring.ssl");
config.accept("spring.task");
config.accept("spring.threads");
config.accept("spring.mandatory-file-encoding");
config.accept("info");
config.accept("spring.output.ansi.enabled");
@ -171,7 +170,6 @@ public class DocumentConfigurationProperties extends DefaultTask {
prefix.accept("spring.integration");
prefix.accept("spring.jms");
prefix.accept("spring.kafka");
prefix.accept("spring.pulsar");
prefix.accept("spring.rabbitmq");
prefix.accept("spring.hazelcast");
prefix.accept("spring.webservices");

@ -105,8 +105,8 @@ public class ApplicationRunner extends DefaultTask {
}
public void normalizeTomcatPort() {
this.normalizations.put("(Tomcat started on port )[\\d]+( \\(http\\))", "$18080$2");
this.normalizations.put("(Tomcat initialized with port )[\\d]+( \\(http\\))", "$18080$2");
this.normalizations.put("(Tomcat started on port\\(s\\): )[\\d]+( \\(http\\))", "$18080$2");
this.normalizations.put("(Tomcat initialized with port\\(s\\): )[\\d]+( \\(http\\))", "$18080$2");
}
public void normalizeLiveReloadPort() {

@ -11,7 +11,7 @@ The pipeline can be deployed using the following command:
[source]
----
$ fly -t spring-boot set-pipeline -p spring-boot-3.2.x -c ci/pipeline.yml -l ci/parameters.yml
$ fly -t spring-boot set-pipeline -p spring-boot-3.1.x -c ci/pipeline.yml -l ci/parameters.yml
----
NOTE: This assumes that you have credhub integration configured with the appropriate

@ -6,9 +6,6 @@ ADD get-docker-url.sh /get-docker-url.sh
ADD get-docker-compose-url.sh /get-docker-compose-url.sh
RUN ./setup.sh java17 java21
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh

@ -6,9 +6,6 @@ ADD get-docker-url.sh /get-docker-url.sh
ADD get-docker-compose-url.sh /get-docker-compose-url.sh
RUN ./setup.sh java17
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Collectors;
@ -104,7 +105,7 @@ class SonatypeServiceTests {
.filter((artifact) -> !artifact.startsWith("build-info.json"))
.map((artifact) -> requestTo(
"/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString()))
.collect(Collectors.toSet());
.collect(Collectors.toCollection(HashSet::new));
AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads);
assertThat(uploadRequestsMatcher.candidates).hasSize(150);
this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT))
@ -132,7 +133,7 @@ class SonatypeServiceTests {
.andRespond(withSuccess());
this.service.publish(getReleaseInfo(), artifactsRoot);
this.server.verify();
assertThat(uploadRequestsMatcher.candidates).isEmpty();
assertThat(uploadRequestsMatcher.candidates).hasSize(0);
}
}
@ -156,7 +157,7 @@ class SonatypeServiceTests {
.filter((artifact) -> !"build-info.json".equals(artifact.toString()))
.map((artifact) -> requestTo(
"/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString()))
.collect(Collectors.toSet());
.collect(Collectors.toCollection(HashSet::new));
AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads);
assertThat(uploadRequestsMatcher.candidates).hasSize(150);
this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT))
@ -184,7 +185,7 @@ class SonatypeServiceTests {
.isThrownBy(() -> this.service.publish(getReleaseInfo(), artifactsRoot))
.withMessage("Close failed");
this.server.verify();
assertThat(uploadRequestsMatcher.candidates).isEmpty();
assertThat(uploadRequestsMatcher.candidates).hasSize(0);
}
}

@ -2,13 +2,12 @@
set -ex
###########################################################
# OS and UTILS
# UTILS
###########################################################
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install --no-install-recommends -y locales tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq
locale-gen en_US.utf8
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
dpkg-reconfigure --frontend noninteractive tzdata
rm -rf /var/lib/apt/lists/*

@ -3,8 +3,8 @@ github-repo-name: "spring-projects/spring-boot"
homebrew-tap-repo: "https://github.com/spring-io/homebrew-tap.git"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "main"
milestone: "3.2.x"
branch: "3.1.x"
milestone: "3.1.x"
build-name: "spring-boot"
concourse-url: "https://ci.spring.io"
task-timeout: 2h00m

@ -179,8 +179,8 @@ resources:
type: registry-image
icon: docker
source:
repository: paketobuildpacks/builder-jammy-base
tag: latest
repository: paketobuildpacks/builder
tag: base
- name: artifactory-repo
type: artifactory-resource
icon: package-variant
@ -594,7 +594,7 @@ jobs:
<<: *sdkman-task-params
RELEASE_TYPE: RELEASE
BRANCH: ((branch))
LATEST_GA: false
LATEST_GA: true
- name: update-homebrew-tap
serial: true
plan:
@ -610,7 +610,7 @@ jobs:
image: ci-image
file: git-repo/ci/tasks/update-homebrew-tap.yml
params:
LATEST_GA: false
LATEST_GA: true
- put: homebrew-tap-repo
params:
repository: updated-homebrew-tap-repo

@ -11,8 +11,8 @@
xmlns:setup.workingsets="http://www.eclipse.org/oomph/setup/workingsets/1.0"
xmlns:workingsets="http://www.eclipse.org/oomph/workingsets/1.0"
xsi:schemaLocation="http://www.eclipse.org/oomph/setup/jdt/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/JDT.ecore http://www.eclipse.org/buildship/oomph/1.0 https://raw.githubusercontent.com/eclipse/buildship/master/org.eclipse.buildship.oomph/model/GradleImport-1.0.ecore http://www.eclipse.org/oomph/predicates/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Predicates.ecore http://www.eclipse.org/oomph/setup/workingsets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupWorkingSets.ecore http://www.eclipse.org/oomph/workingsets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/WorkingSets.ecore"
name="spring.boot.3.2.x"
label="Spring Boot 3.2.x">
name="spring.boot.3.1.x"
label="Spring Boot 3.1.x">
<setupTask
xsi:type="setup:VariableTask"
type="FOLDER"
@ -136,7 +136,7 @@
name="spring-boot-tools">
<predicate
xsi:type="predicates:NamePredicate"
pattern="spring-boot-(tools|antlib|configuration-.*|loader|loader-classic|.*-tools|.*-layertools|.*-plugin|autoconfigure-processor|buildpack.*)"/>
pattern="spring-boot-(tools|antlib|configuration-.*|loader|.*-tools|.*-layertools|.*-plugin|autoconfigure-processor|buildpack.*)"/>
</workingSet>
<workingSet
name="spring-boot-starters">

@ -4,7 +4,7 @@ require 'net/http'
require 'yaml'
require 'logger'
$main_branch = "3.2.x"
$main_branch = "3.1.x"
$log = Logger.new(STDOUT)
$log.level = Logger::WARN

@ -1,18 +1,18 @@
version=3.2.0-SNAPSHOT
version=3.1.5-SNAPSHOT
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
assertjVersion=3.24.2
commonsCodecVersion=1.16.0
commonsCodecVersion=1.15
hamcrestVersion=2.2
jacksonVersion=2.15.2
junitJupiterVersion=5.10.0
kotlinVersion=1.9.10
junitJupiterVersion=5.9.3
kotlinVersion=1.8.22
mavenVersion=3.9.4
nativeBuildToolsVersion=0.9.27
springFrameworkVersion=6.1.0-SNAPSHOT
springFrameworkVersion=6.0.13-SNAPSHOT
tomcatVersion=10.1.13
kotlin.stdlib.default.dependency=false

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

@ -5,7 +5,6 @@ pluginManagement {
if (version.endsWith('-SNAPSHOT')) {
maven { url "https://repo.spring.io/snapshot" }
}
}
resolutionStrategy {
eachPlugin {
@ -54,13 +53,11 @@ include "spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-process
include "spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform"
include "spring-boot-project:spring-boot-tools:spring-boot-cli"
include "spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata"
include "spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata-changelog-generator"
include "spring-boot-project:spring-boot-tools:spring-boot-configuration-processor"
include "spring-boot-project:spring-boot-tools:spring-boot-gradle-plugin"
include "spring-boot-project:spring-boot-tools:spring-boot-gradle-test-support"
include "spring-boot-project:spring-boot-tools:spring-boot-jarmode-layertools"
include "spring-boot-project:spring-boot-tools:spring-boot-loader"
include "spring-boot-project:spring-boot-tools:spring-boot-loader-classic"
include "spring-boot-project:spring-boot-tools:spring-boot-loader-tools"
include "spring-boot-project:spring-boot-tools:spring-boot-maven-plugin"
include "spring-boot-project:spring-boot-tools:spring-boot-properties-migrator"
@ -78,7 +75,6 @@ include "spring-boot-project:spring-boot-test-autoconfigure"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-configuration-processor-tests"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-launch-script-tests"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-loader-tests"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-loader-classic-tests"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-server-tests"
include "spring-boot-system-tests:spring-boot-deployment-tests"
include "spring-boot-system-tests:spring-boot-image-tests"

@ -37,7 +37,7 @@ dependencies {
optional("io.dropwizard.metrics:metrics-jmx")
optional("io.lettuce:lettuce-core")
optional("io.micrometer:micrometer-observation")
optional("io.micrometer:micrometer-jakarta9")
optional("io.micrometer:micrometer-core")
optional("io.micrometer:micrometer-tracing")
optional("io.micrometer:micrometer-tracing-bridge-brave")
optional("io.micrometer:micrometer-tracing-bridge-otel")
@ -74,7 +74,6 @@ dependencies {
optional("io.opentelemetry:opentelemetry-exporter-otlp")
optional("io.projectreactor.netty:reactor-netty-http")
optional("io.r2dbc:r2dbc-pool")
optional("io.r2dbc:r2dbc-proxy")
optional("io.r2dbc:r2dbc-spi")
optional("jakarta.jms:jakarta.jms-api")
optional("jakarta.persistence:jakarta.persistence-api")
@ -108,7 +107,6 @@ dependencies {
optional("org.hibernate.validator:hibernate-validator")
optional("org.influxdb:influxdb-java")
optional("org.liquibase:liquibase-core") {
exclude group: "javax.activation", module: "javax.activation-api"
exclude group: "javax.xml.bind", module: "jaxb-api"
}
optional("org.mongodb:mongodb-driver-reactivestreams")
@ -162,7 +160,9 @@ dependencies {
testImplementation("org.assertj:assertj-core")
testImplementation("org.awaitility:awaitility")
testImplementation("org.cache2k:cache2k-api")
testImplementation("org.eclipse.jetty.ee10:jetty-ee10-webapp")
testImplementation("org.eclipse.jetty:jetty-webapp") {
exclude group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api"
}
testImplementation("org.glassfish.jersey.ext:jersey-spring6")
testImplementation("org.glassfish.jersey.media:jersey-media-json-jackson")
testImplementation("org.hamcrest:hamcrest")

@ -53,6 +53,8 @@ class ReactiveCloudFoundrySecurityService {
private final String cloudControllerUrl;
private Mono<String> uaaUrl;
ReactiveCloudFoundrySecurityService(WebClient.Builder webClientBuilder, String cloudControllerUrl,
boolean skipSslValidation) {
Assert.notNull(webClientBuilder, "WebClient must not be null");
@ -147,7 +149,7 @@ class ReactiveCloudFoundrySecurityService {
* @return the UAA url Mono
*/
Mono<String> getUaaUrl() {
return this.webClient.get()
this.uaaUrl = this.webClient.get()
.uri(this.cloudControllerUrl + "/info")
.retrieve()
.bodyToMono(Map.class)
@ -155,6 +157,7 @@ class ReactiveCloudFoundrySecurityService {
.cache()
.onErrorMap((ex) -> new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
"Unable to fetch token keys from UAA."));
return this.uaaUrl;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -86,7 +86,7 @@ class CloudFoundrySecurityInterceptor {
return SecurityResponse.success();
}
private void check(HttpServletRequest request, EndpointId endpointId) {
private void check(HttpServletRequest request, EndpointId endpointId) throws Exception {
Token token = getToken(request);
this.tokenValidator.validate(token);
AccessLevel accessLevel = this.cloudFoundrySecurityService.getAccessLevel(token.toString(), this.applicationId);

@ -143,8 +143,11 @@ class OnAvailableEndpointCondition extends SpringBootCondition {
}
private Boolean isEnabledByDefault(Environment environment) {
Optional<Boolean> enabledByDefault = enabledByDefaultCache.computeIfAbsent(environment,
(ignore) -> Optional.ofNullable(environment.getProperty(ENABLED_BY_DEFAULT_KEY, Boolean.class)));
Optional<Boolean> enabledByDefault = enabledByDefaultCache.get(environment);
if (enabledByDefault == null) {
enabledByDefault = Optional.ofNullable(environment.getProperty(ENABLED_BY_DEFAULT_KEY, Boolean.class));
enabledByDefaultCache.put(environment, enabledByDefault);
}
return enabledByDefault.orElse(null);
}

@ -150,7 +150,7 @@ public class IncludeExcludeEndpointFilter<E extends ExposableEndpoint<?>> implem
private final Set<EndpointId> endpointIds;
EndpointPatterns(String[] patterns) {
this((patterns != null) ? Arrays.asList(patterns) : null);
this((patterns != null) ? Arrays.asList(patterns) : (Collection<String>) null);
}
EndpointPatterns(Collection<String> patterns) {

@ -103,8 +103,7 @@ class JerseyWebEndpointManagementContextConfiguration {
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HEALTH_ENDPOINT_ID))
.findFirst()
.orElseThrow(
() -> new IllegalStateException("No endpoint with id '%s' found".formatted(HEALTH_ENDPOINT_ID)));
.get();
return new JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(health, healthEndpointGroups);
}

@ -120,8 +120,7 @@ public class WebFluxEndpointManagementContextConfiguration {
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID))
.findFirst()
.orElseThrow(
() -> new IllegalStateException("No endpoint with id '%s' found".formatted(HealthEndpoint.ID)));
.get();
return new AdditionalHealthEndpointPathsWebFluxHandlerMapping(new EndpointMapping(""), health,
groups.getAllWithAdditionalPath(WebServerNamespace.MANAGEMENT));
}
@ -163,16 +162,16 @@ public class WebFluxEndpointManagementContextConfiguration {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ServerCodecConfigurer serverCodecConfigurer) {
process(serverCodecConfigurer);
if (bean instanceof ServerCodecConfigurer) {
process((ServerCodecConfigurer) bean);
}
return bean;
}
private void process(ServerCodecConfigurer configurer) {
for (HttpMessageWriter<?> writer : configurer.getWriters()) {
if (writer instanceof EncoderHttpMessageWriter<?> encoderHttpMessageWriter) {
process((encoderHttpMessageWriter).getEncoder());
if (writer instanceof EncoderHttpMessageWriter) {
process(((EncoderHttpMessageWriter<?>) writer).getEncoder());
}
}
}

@ -115,8 +115,7 @@ public class WebMvcEndpointManagementContextConfiguration {
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID))
.findFirst()
.orElseThrow(
() -> new IllegalStateException("No endpoint with id '%s' found".formatted(HealthEndpoint.ID)));
.get();
return new AdditionalHealthEndpointPathsWebMvcHandlerMapping(health,
groups.getAllWithAdditionalPath(WebServerNamespace.MANAGEMENT));
}
@ -158,8 +157,8 @@ public class WebMvcEndpointManagementContextConfiguration {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
configure(mappingJackson2HttpMessageConverter);
if (converter instanceof MappingJackson2HttpMessageConverter) {
configure((MappingJackson2HttpMessageConverter) converter);
}
}
}

@ -16,9 +16,12 @@
package org.springframework.boot.actuate.autoconfigure.health;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/**
@ -36,6 +39,18 @@ public abstract class AbstractCompositeHealthContributorConfiguration<C, I exten
private final Function<B, I> indicatorFactory;
/**
* Creates a {@code AbstractCompositeHealthContributorConfiguration} that will use
* reflection to create health indicator instances.
* @deprecated since 3.0.0 in favor of
* {@link #AbstractCompositeHealthContributorConfiguration(Function)}
*/
@Deprecated(since = "3.0.0", forRemoval = true)
protected AbstractCompositeHealthContributorConfiguration() {
this.indicatorFactory = new ReflectionIndicatorFactory(
ResolvableType.forClass(AbstractCompositeHealthContributorConfiguration.class, getClass()));
}
/**
* Creates a {@code AbstractCompositeHealthContributorConfiguration} that will use the
* given {@code indicatorFactory} to create health indicator instances.
@ -60,4 +75,34 @@ public abstract class AbstractCompositeHealthContributorConfiguration<C, I exten
return this.indicatorFactory.apply(bean);
}
private class ReflectionIndicatorFactory implements Function<B, I> {
private final Class<?> indicatorType;
private final Class<?> beanType;
ReflectionIndicatorFactory(ResolvableType type) {
this.indicatorType = type.resolveGeneric(1);
this.beanType = type.resolveGeneric(2);
}
@Override
public I apply(B bean) {
try {
return BeanUtils.instantiateClass(getConstructor(), bean);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create health indicator %s for bean type %s"
.formatted(this.indicatorType, this.beanType), ex);
}
}
@SuppressWarnings("unchecked")
private Constructor<I> getConstructor() throws NoSuchMethodException {
return (Constructor<I>) this.indicatorType.getDeclaredConstructor(this.beanType);
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,6 +36,18 @@ import org.springframework.boot.actuate.health.HealthIndicator;
public abstract class CompositeHealthContributorConfiguration<I extends HealthIndicator, B>
extends AbstractCompositeHealthContributorConfiguration<HealthContributor, I, B> {
/**
* Creates a {@code CompositeHealthContributorConfiguration} that will use reflection
* to create {@link HealthIndicator} instances.
* @deprecated since 3.0.0 in favor of
* {@link #CompositeHealthContributorConfiguration(Function)}
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.0.0", forRemoval = true)
public CompositeHealthContributorConfiguration() {
super();
}
/**
* Creates a {@code CompositeHealthContributorConfiguration} that will use the given
* {@code indicatorFactory} to create {@link HealthIndicator} instances.

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,6 +36,18 @@ import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
public abstract class CompositeReactiveHealthContributorConfiguration<I extends ReactiveHealthIndicator, B>
extends AbstractCompositeHealthContributorConfiguration<ReactiveHealthContributor, I, B> {
/**
* Creates a {@code CompositeReactiveHealthContributorConfiguration} that will use
* reflection to create {@link ReactiveHealthIndicator} instances.
* @deprecated since 3.0.0 in favor of
* {@link #CompositeReactiveHealthContributorConfiguration(Function)}
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.0.0", forRemoval = true)
public CompositeReactiveHealthContributorConfiguration() {
super();
}
/**
* Creates a {@code CompositeReactiveHealthContributorConfiguration} that will use the
* given {@code indicatorFactory} to create {@link ReactiveHealthIndicator} instances.

@ -70,8 +70,7 @@ class HealthEndpointReactiveWebExtensionConfiguration {
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID))
.findFirst()
.orElseThrow(
() -> new IllegalStateException("No endpoint with id '%s' found".formatted(HealthEndpoint.ID)));
.get();
return new AdditionalHealthEndpointPathsWebFluxHandlerMapping(new EndpointMapping(""), health,
groups.getAllWithAdditionalPath(WebServerNamespace.SERVER));
}

@ -81,8 +81,7 @@ class HealthEndpointWebExtensionConfiguration {
return webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID))
.findFirst()
.orElseThrow(
() -> new IllegalStateException("No endpoint with id '%s' found".formatted(HealthEndpoint.ID)));
.get();
}
@ConditionalOnBean(DispatcherServlet.class)

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,16 +37,11 @@ import org.springframework.context.annotation.Bean;
*
* @author Eddú Meléndez
* @since 2.0.0
* @deprecated since 3.2.0 for removal in 3.4.0 in favor of the
* <a href="https://github.com/influxdata/influxdb-client-java">new client</a> and its own
* Spring Boot integration.
*/
@SuppressWarnings("removal")
@AutoConfiguration(after = InfluxDbAutoConfiguration.class)
@ConditionalOnClass(InfluxDB.class)
@ConditionalOnBean(InfluxDB.class)
@ConditionalOnEnabledHealthIndicator("influxdb")
@Deprecated(since = "3.2.0", forRemoval = true)
public class InfluxDbHealthContributorAutoConfiguration
extends CompositeHealthContributorConfiguration<InfluxDbHealthIndicator, InfluxDB> {

@ -1,60 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics;
import io.micrometer.core.aop.CountedAspect;
import io.micrometer.core.aop.MeterTagAnnotationHandler;
import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
import org.aspectj.weaver.Advice;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Micrometer-based metrics
* aspects.
*
* @author Jonatan Ivanov
* @since 3.2.0
*/
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
@ConditionalOnClass({ MeterRegistry.class, Advice.class })
@ConditionalOnBean(MeterRegistry.class)
public class MetricsAspectsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
CountedAspect countedAspect(MeterRegistry registry) {
return new CountedAspect(registry);
}
@Bean
@ConditionalOnMissingBean
TimedAspect timedAspect(MeterRegistry registry,
ObjectProvider<MeterTagAnnotationHandler> meterTagAnnotationHandler) {
TimedAspect timedAspect = new TimedAspect(registry);
meterTagAnnotationHandler.ifAvailable(timedAspect::setMeterTagAnnotationHandler);
return timedAspect;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -79,8 +79,6 @@ public class MetricsProperties {
return this.enable;
}
@Deprecated(since = "3.2.0", forRemoval = true)
@DeprecatedConfigurationProperty(replacement = "management.observations.key-values", since = "3.2.0")
public Map<String, String> getTags() {
return this.tags;
}
@ -117,6 +115,8 @@ public class MetricsProperties {
public static class Client {
private final ClientRequest request = new ClientRequest();
/**
* Maximum number of unique URI tag values allowed. After the max number of
* tag values is reached, metrics with additional tag values are denied by
@ -124,6 +124,10 @@ public class MetricsProperties {
*/
private int maxUriTags = 100;
public ClientRequest getRequest() {
return this.request;
}
public int getMaxUriTags() {
return this.maxUriTags;
}
@ -132,10 +136,32 @@ public class MetricsProperties {
this.maxUriTags = maxUriTags;
}
public static class ClientRequest {
/**
* Name of the metric for sent requests.
*/
private String metricName = "http.client.requests";
@Deprecated(since = "3.0.0", forRemoval = true)
@DeprecatedConfigurationProperty(replacement = "management.observations.http.client.requests.name")
public String getMetricName() {
return this.metricName;
}
@Deprecated(since = "3.0.0", forRemoval = true)
public void setMetricName(String metricName) {
this.metricName = metricName;
}
}
}
public static class Server {
private final ServerRequest request = new ServerRequest();
/**
* Maximum number of unique URI tag values allowed. After the max number of
* tag values is reached, metrics with additional tag values are denied by
@ -143,6 +169,10 @@ public class MetricsProperties {
*/
private int maxUriTags = 100;
public ServerRequest getRequest() {
return this.request;
}
public int getMaxUriTags() {
return this.maxUriTags;
}
@ -151,6 +181,27 @@ public class MetricsProperties {
this.maxUriTags = maxUriTags;
}
public static class ServerRequest {
/**
* Name of the metric for received requests.
*/
private String metricName = "http.server.requests";
@Deprecated(since = "3.0.0", forRemoval = true)
@DeprecatedConfigurationProperty(replacement = "management.observations.http.server.requests.name")
public String getMetricName() {
return this.metricName;
}
@Deprecated(since = "3.0.0", forRemoval = true)
@DeprecatedConfigurationProperty(replacement = "management.observations.http.server.requests.name")
public void setMetricName(String metricName) {
this.metricName = metricName;
}
}
}
}

@ -50,7 +50,6 @@ public class PropertiesMeterFilter implements MeterFilter {
private final MeterFilter mapFilter;
@SuppressWarnings("removal")
public PropertiesMeterFilter(MetricsProperties properties) {
Assert.notNull(properties, "Properties must not be null");
this.properties = properties;

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -140,12 +140,6 @@ public class DynatraceProperties extends StepRegistryProperties {
*/
private boolean useDynatraceSummaryInstruments = true;
/**
* Whether to export meter metadata (unit and description) to the Dynatrace
* backend.
*/
private boolean exportMeterMetadata = true;
public Map<String, String> getDefaultDimensions() {
return this.defaultDimensions;
}
@ -178,14 +172,6 @@ public class DynatraceProperties extends StepRegistryProperties {
this.useDynatraceSummaryInstruments = useDynatraceSummaryInstruments;
}
public boolean isExportMeterMetadata() {
return this.exportMeterMetadata;
}
public void setExportMeterMetadata(boolean exportMeterMetadata) {
this.exportMeterMetadata = exportMeterMetadata;
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -95,11 +95,6 @@ class DynatracePropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapt
return get(v2(V2::isUseDynatraceSummaryInstruments), DynatraceConfig.super::useDynatraceSummaryInstruments);
}
@Override
public boolean exportMeterMetadata() {
return (get(v2(V2::isExportMeterMetadata), DynatraceConfig.super::exportMeterMetadata));
}
private <V> Function<DynatraceProperties, V> v1(Function<V1, V> getter) {
return (properties) -> getter.apply(properties.getV1());
}

@ -1,35 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
/**
* Details required to establish a connection to a OpenTelemetry Collector service.
*
* @author Eddú Meléndez
* @since 3.2.0
*/
public interface OtlpMetricsConnectionDetails extends ConnectionDetails {
/**
* Address to where metrics will be published.
* @return the address to where metrics will be published
*/
String getUrl();
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,7 +24,6 @@ import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegi
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -32,13 +31,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to OTLP.
*
* @author Eddú Meléndez
* @author Moritz Halbritter
* @since 3.0.0
*/
@AutoConfiguration(
@ -47,27 +44,19 @@ import org.springframework.core.env.Environment;
@ConditionalOnBean(Clock.class)
@ConditionalOnClass(OtlpMeterRegistry.class)
@ConditionalOnEnabledMetricsExport("otlp")
@EnableConfigurationProperties({ OtlpProperties.class, OpenTelemetryProperties.class })
@EnableConfigurationProperties(OtlpProperties.class)
public class OtlpMetricsExportAutoConfiguration {
private final OtlpProperties properties;
OtlpMetricsExportAutoConfiguration(OtlpProperties properties) {
public OtlpMetricsExportAutoConfiguration(OtlpProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(OtlpMetricsConnectionDetails.class)
OtlpMetricsConnectionDetails otlpMetricsConnectionDetails() {
return new PropertiesOtlpMetricsConnectionDetails(this.properties);
}
@Bean
@ConditionalOnMissingBean
OtlpConfig otlpConfig(OpenTelemetryProperties openTelemetryProperties,
OtlpMetricsConnectionDetails connectionDetails, Environment environment) {
return new OtlpPropertiesConfigAdapter(this.properties, openTelemetryProperties, connectionDetails,
environment);
public OtlpConfig otlpConfig() {
return new OtlpPropertiesConfigAdapter(this.properties);
}
@Bean
@ -76,22 +65,4 @@ public class OtlpMetricsExportAutoConfiguration {
return new OtlpMeterRegistry(otlpConfig, clock);
}
/**
* Adapts {@link OtlpProperties} to {@link OtlpMetricsConnectionDetails}.
*/
static class PropertiesOtlpMetricsConnectionDetails implements OtlpMetricsConnectionDetails {
private final OtlpProperties properties;
PropertiesOtlpMetricsConnectionDetails(OtlpProperties properties) {
this.properties = properties;
}
@Override
public String getUrl() {
return this.properties.getUrl();
}
}
}

@ -17,13 +17,11 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.micrometer.registry.otlp.AggregationTemporality;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for configuring OTLP metrics
@ -57,11 +55,6 @@ public class OtlpProperties extends StepRegistryProperties {
*/
private Map<String, String> headers;
/**
* Time unit for exported metrics.
*/
private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS;
public String getUrl() {
return this.url;
}
@ -78,13 +71,10 @@ public class OtlpProperties extends StepRegistryProperties {
this.aggregationTemporality = aggregationTemporality;
}
@Deprecated(since = "3.2.0", forRemoval = true)
@DeprecatedConfigurationProperty(replacement = "management.opentelemetry.resource-attributes", since = "3.2.0")
public Map<String, String> getResourceAttributes() {
return this.resourceAttributes;
}
@Deprecated(since = "3.2.0", forRemoval = true)
public void setResourceAttributes(Map<String, String> resourceAttributes) {
this.resourceAttributes = resourceAttributes;
}
@ -97,12 +87,4 @@ public class OtlpProperties extends StepRegistryProperties {
this.headers = headers;
}
public TimeUnit getBaseTimeUnit() {
return this.baseTimeUnit;
}
public void setBaseTimeUnit(TimeUnit baseTimeUnit) {
this.baseTimeUnit = baseTimeUnit;
}
}

@ -16,45 +16,23 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.micrometer.registry.otlp.AggregationTemporality;
import io.micrometer.registry.otlp.OtlpConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter;
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
/**
* Adapter to convert {@link OtlpProperties} to an {@link OtlpConfig}.
*
* @author Eddú Meléndez
* @author Jonatan Ivanov
* @author Moritz Halbritter
*/
class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<OtlpProperties> implements OtlpConfig {
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
private final OpenTelemetryProperties openTelemetryProperties;
private final OtlpMetricsConnectionDetails connectionDetails;
private final Environment environment;
OtlpPropertiesConfigAdapter(OtlpProperties properties, OpenTelemetryProperties openTelemetryProperties,
OtlpMetricsConnectionDetails connectionDetails, Environment environment) {
OtlpPropertiesConfigAdapter(OtlpProperties properties) {
super(properties);
this.connectionDetails = connectionDetails;
this.openTelemetryProperties = openTelemetryProperties;
this.environment = environment;
}
@Override
@ -64,7 +42,7 @@ class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<Ot
@Override
public String url() {
return get((properties) -> this.connectionDetails.getUrl(), OtlpConfig.super::url);
return get(OtlpProperties::getUrl, OtlpConfig.super::url);
}
@Override
@ -73,17 +51,8 @@ class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<Ot
}
@Override
@SuppressWarnings("removal")
public Map<String, String> resourceAttributes() {
Map<String, String> resourceAttributes = this.openTelemetryProperties.getResourceAttributes();
Map<String, String> result = new HashMap<>((!CollectionUtils.isEmpty(resourceAttributes)) ? resourceAttributes
: get(OtlpProperties::getResourceAttributes, OtlpConfig.super::resourceAttributes));
result.computeIfAbsent("service.name", (key) -> getApplicationName());
return Collections.unmodifiableMap(result);
}
private String getApplicationName() {
return this.environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
return get(OtlpProperties::getResourceAttributes, OtlpConfig.super::resourceAttributes);
}
@Override
@ -91,9 +60,4 @@ class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<Ot
return get(OtlpProperties::getHeaders, OtlpConfig.super::headers);
}
@Override
public TimeUnit baseTimeUnit() {
return get(OtlpProperties::getBaseTimeUnit, OtlpConfig.super::baseTimeUnit);
}
}

@ -16,7 +16,6 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront;
import com.wavefront.sdk.common.clients.service.token.TokenService.Type;
import io.micrometer.wavefront.WavefrontConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PushRegistryPropertiesConfigAdapter;
@ -85,9 +84,4 @@ public class WavefrontPropertiesConfigAdapter
return get(Export::isReportDayDistribution, WavefrontConfig.super::reportDayDistribution);
}
@Override
public Type apiTokenType() {
return this.properties.getWavefrontApiTokenType();
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,9 +29,9 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server;
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -57,12 +57,13 @@ import org.springframework.core.annotation.Order;
@ConditionalOnClass({ ResourceConfig.class, MetricsApplicationEventListener.class })
@ConditionalOnBean({ MeterRegistry.class, ResourceConfig.class })
@EnableConfigurationProperties(MetricsProperties.class)
@SuppressWarnings("removal")
public class JerseyServerMetricsAutoConfiguration {
private final ObservationProperties observationProperties;
private final MetricsProperties properties;
public JerseyServerMetricsAutoConfiguration(ObservationProperties observationProperties) {
this.observationProperties = observationProperties;
public JerseyServerMetricsAutoConfiguration(MetricsProperties properties) {
this.properties = properties;
}
@Bean
@ -74,19 +75,19 @@ public class JerseyServerMetricsAutoConfiguration {
@Bean
public ResourceConfigCustomizer jerseyServerMetricsResourceConfigCustomizer(MeterRegistry meterRegistry,
JerseyTagsProvider tagsProvider) {
String metricName = this.observationProperties.getHttp().getServer().getRequests().getName();
return (config) -> config.register(new MetricsApplicationEventListener(meterRegistry, tagsProvider, metricName,
true, new AnnotationUtilsAnnotationFinder()));
Server server = this.properties.getWeb().getServer();
return (config) -> config.register(new MetricsApplicationEventListener(meterRegistry, tagsProvider,
server.getRequest().getMetricName(), true, new AnnotationUtilsAnnotationFinder()));
}
@Bean
@Order(0)
public MeterFilter jerseyMetricsUriTagFilter(MetricsProperties metricsProperties) {
String metricName = this.observationProperties.getHttp().getServer().getRequests().getName();
public MeterFilter jerseyMetricsUriTagFilter() {
String metricName = this.properties.getWeb().getServer().getRequest().getMetricName();
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> String.format("Reached the maximum number of URI tags for '%s'.", metricName));
return MeterFilter.maximumAllowableTags(metricName, "uri",
metricsProperties.getWeb().getServer().getMaxUriTags(), filter);
return MeterFilter.maximumAllowableTags(metricName, "uri", this.properties.getWeb().getServer().getMaxUriTags(),
filter);
}
/**

@ -27,11 +27,9 @@ import io.micrometer.observation.ObservationFilter;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.handler.TracingObservationHandler;
import org.aspectj.weaver.Advice;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
@ -45,7 +43,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
@ -53,7 +50,6 @@ import org.springframework.core.annotation.Order;
* @author Moritz Halbritter
* @author Brian Clozel
* @author Jonatan Ivanov
* @author Vedran Pavic
* @since 3.0.0
*/
@AutoConfiguration(after = { CompositeMeterRegistryAutoConfiguration.class, MicrometerTracingAutoConfiguration.class })
@ -79,12 +75,6 @@ public class ObservationAutoConfiguration {
return ObservationRegistry.create();
}
@Bean
@Order(0)
PropertiesObservationFilterPredicate propertiesObservationFilter(ObservationProperties properties) {
return new PropertiesObservationFilterPredicate(properties);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class)
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")
@ -152,16 +142,4 @@ public class ObservationAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class ObservedAspectConfiguration {
@Bean
@ConditionalOnMissingBean
ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
package org.springframework.boot.actuate.autoconfigure.observation;
import java.util.ArrayList;
import java.util.List;
import io.micrometer.observation.ObservationHandler;
@ -31,7 +30,6 @@ import org.springframework.util.MultiValueMap;
* Groups {@link ObservationHandler ObservationHandlers} by type.
*
* @author Andy Wilkinson
* @author Moritz Halbritter
*/
@SuppressWarnings("rawtypes")
class ObservationHandlerGrouping {
@ -48,14 +46,13 @@ class ObservationHandlerGrouping {
void apply(List<ObservationHandler<?>> handlers, ObservationConfig config) {
MultiValueMap<Class<? extends ObservationHandler>, ObservationHandler<?>> groupings = new LinkedMultiValueMap<>();
List<ObservationHandler<?>> handlersWithoutCategory = new ArrayList<>();
for (ObservationHandler<?> handler : handlers) {
Class<? extends ObservationHandler> category = findCategory(handler);
if (category != null) {
groupings.add(category, handler);
}
else {
handlersWithoutCategory.add(handler);
config.observationHandler(handler);
}
}
for (Class<? extends ObservationHandler> category : this.categories) {
@ -64,9 +61,6 @@ class ObservationHandlerGrouping {
config.observationHandler(new FirstMatchingCompositeObservationHandler(handlerGroup));
}
}
for (ObservationHandler<?> observationHandler : handlersWithoutCategory) {
config.observationHandler(observationHandler);
}
}
private Class<? extends ObservationHandler> findCategory(ObservationHandler<?> handler) {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,9 +16,6 @@
package org.springframework.boot.actuate.autoconfigure.observation;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@ -26,7 +23,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* observations.
*
* @author Brian Clozel
* @author Moritz Halbritter
* @since 3.0.0
*/
@ConfigurationProperties("management.observations")
@ -34,37 +30,10 @@ public class ObservationProperties {
private final Http http = new Http();
/**
* Common key-values that are applied to every observation.
*/
private Map<String, String> keyValues = new LinkedHashMap<>();
/**
* Whether observations starting with the specified name should be enabled. The
* longest match wins, the key 'all' can also be used to configure all observations.
*/
private Map<String, Boolean> enable = new LinkedHashMap<>();
public Map<String, Boolean> getEnable() {
return this.enable;
}
public void setEnable(Map<String, Boolean> enable) {
this.enable = enable;
}
public Http getHttp() {
return this.http;
}
public Map<String, String> getKeyValues() {
return this.keyValues;
}
public void setKeyValues(Map<String, String> keyValues) {
this.keyValues = keyValues;
}
public static class Http {
private final Client client = new Client();
@ -90,9 +59,10 @@ public class ObservationProperties {
public static class ClientRequests {
/**
* Name of the observation for client requests.
* Name of the observation for client requests. If empty, will use the
* default "http.client.requests".
*/
private String name = "http.client.requests";
private String name;
public String getName() {
return this.name;
@ -117,9 +87,10 @@ public class ObservationProperties {
public static class ServerRequests {
/**
* Name of the observation for server requests.
* Name of the observation for server requests. If empty, will use the
* default "http.server.requests".
*/
private String name = "http.server.requests";
private String name;
public String getName() {
return this.name;

@ -1,83 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Supplier;
import io.micrometer.common.KeyValues;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationFilter;
import io.micrometer.observation.ObservationPredicate;
import org.springframework.util.StringUtils;
/**
* {@link ObservationFilter} to apply settings from {@link ObservationProperties}.
*
* @author Moritz Halbritter
*/
class PropertiesObservationFilterPredicate implements ObservationFilter, ObservationPredicate {
private final ObservationFilter commonKeyValuesFilter;
private final ObservationProperties properties;
PropertiesObservationFilterPredicate(ObservationProperties properties) {
this.properties = properties;
this.commonKeyValuesFilter = createCommonKeyValuesFilter(properties);
}
@Override
public Context map(Context context) {
return this.commonKeyValuesFilter.map(context);
}
@Override
public boolean test(String name, Context context) {
return lookupWithFallbackToAll(this.properties.getEnable(), name, true);
}
private static <T> T lookupWithFallbackToAll(Map<String, T> values, String name, T defaultValue) {
if (values.isEmpty()) {
return defaultValue;
}
return doLookup(values, name, () -> values.getOrDefault("all", defaultValue));
}
private static <T> T doLookup(Map<String, T> values, String name, Supplier<T> defaultValue) {
while (StringUtils.hasLength(name)) {
T result = values.get(name);
if (result != null) {
return result;
}
int lastDot = name.lastIndexOf('.');
name = (lastDot != -1) ? name.substring(0, lastDot) : "";
}
return defaultValue.get();
}
private static ObservationFilter createCommonKeyValuesFilter(ObservationProperties properties) {
if (properties.getKeyValues().isEmpty()) {
return (context) -> context;
}
KeyValues keyValues = KeyValues.of(properties.getKeyValues().entrySet(), Entry::getKey, Entry::getValue);
return (context) -> context.addLowCardinalityKeyValues(keyValues);
}
}

@ -43,6 +43,7 @@ import org.springframework.graphql.observation.GraphQlObservationInstrumentation
@AutoConfiguration(after = ObservationAutoConfiguration.class)
@ConditionalOnBean(ObservationRegistry.class)
@ConditionalOnClass({ GraphQL.class, GraphQlSource.class, Observation.class })
@SuppressWarnings("removal")
public class GraphQlObservationAutoConfiguration {
@Bean

@ -1,72 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation.jms;
import io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.Message;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.core.JmsTemplate;
/**
* {@link EnableAutoConfiguration Auto-configuration} for instrumenting
* {@link JmsTemplate} beans for Observability.
*
* @author Brian Clozel
* @since 3.2.0
*/
@AutoConfiguration(after = { JmsAutoConfiguration.class, ObservationAutoConfiguration.class })
@ConditionalOnBean({ ObservationRegistry.class, JmsTemplate.class })
@ConditionalOnClass({ Observation.class, Message.class, JmsTemplate.class, JmsPublishObservationContext.class })
public class JmsTemplateObservationAutoConfiguration {
@Bean
static JmsTemplateObservationPostProcessor jmsTemplateObservationPostProcessor(
ObjectProvider<ObservationRegistry> observationRegistry) {
return new JmsTemplateObservationPostProcessor(observationRegistry);
}
static class JmsTemplateObservationPostProcessor implements BeanPostProcessor {
private final ObjectProvider<ObservationRegistry> observationRegistry;
JmsTemplateObservationPostProcessor(ObjectProvider<ObservationRegistry> observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof JmsTemplate jmsTemplate) {
this.observationRegistry.ifAvailable(jmsTemplate::setObservationRegistry);
}
return bean;
}
}
}

@ -1,20 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Auto-configuration for JMS observations.
*/
package org.springframework.boot.actuate.autoconfigure.observation.jms;

@ -0,0 +1,62 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation.web.client;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.Tag;
import org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTagsProvider;
import org.springframework.http.client.observation.ClientRequestObservationContext;
import org.springframework.http.client.observation.ClientRequestObservationConvention;
/**
* Adapter class that applies {@link RestTemplateExchangeTagsProvider} tags as a
* {@link ClientRequestObservationConvention}.
*
* @author Brian Clozel
*/
@SuppressWarnings({ "removal" })
class ClientHttpObservationConventionAdapter implements ClientRequestObservationConvention {
private final String metricName;
private final RestTemplateExchangeTagsProvider tagsProvider;
ClientHttpObservationConventionAdapter(String metricName, RestTemplateExchangeTagsProvider tagsProvider) {
this.metricName = metricName;
this.tagsProvider = tagsProvider;
}
@Override
@SuppressWarnings("deprecation")
public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) {
Iterable<Tag> tags = this.tagsProvider.getTags(context.getUriTemplate(), context.getCarrier(),
context.getResponse());
return KeyValues.of(tags, Tag::getKey, Tag::getValue);
}
@Override
public KeyValues getHighCardinalityKeyValues(ClientRequestObservationContext context) {
return KeyValues.empty();
}
@Override
public String getName() {
return this.metricName;
}
}

@ -0,0 +1,76 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation.web.client;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.Tag;
import io.micrometer.observation.Observation;
import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider;
import org.springframework.core.Conventions;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientRequestObservationContext;
import org.springframework.web.reactive.function.client.ClientRequestObservationConvention;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Adapter class that applies {@link WebClientExchangeTagsProvider} tags as a
* {@link ClientRequestObservationConvention}.
*
* @author Brian Clozel
*/
@SuppressWarnings("removal")
class ClientObservationConventionAdapter implements ClientRequestObservationConvention {
private static final String URI_TEMPLATE_ATTRIBUTE = Conventions.getQualifiedAttributeName(WebClient.class,
"uriTemplate");
private final String metricName;
private final WebClientExchangeTagsProvider tagsProvider;
ClientObservationConventionAdapter(String metricName, WebClientExchangeTagsProvider tagsProvider) {
this.metricName = metricName;
this.tagsProvider = tagsProvider;
}
@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof ClientRequestObservationContext;
}
@Override
public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) {
ClientRequest request = context.getRequest();
if (request == null) {
request = context.getCarrier().attribute(URI_TEMPLATE_ATTRIBUTE, context.getUriTemplate()).build();
}
Iterable<Tag> tags = this.tagsProvider.tags(request, context.getResponse(), context.getError());
return KeyValues.of(tags, Tag::getKey, Tag::getValue);
}
@Override
public KeyValues getHighCardinalityKeyValues(ClientRequestObservationContext context) {
return KeyValues.empty();
}
@Override
public String getName() {
return this.metricName;
}
}

@ -65,10 +65,13 @@ public class HttpClientObservationsAutoConfiguration {
@Bean
@Order(0)
@SuppressWarnings("removal")
MeterFilter metricsHttpClientUriTagFilter(ObservationProperties observationProperties,
MetricsProperties metricsProperties) {
Client clientProperties = metricsProperties.getWeb().getClient();
String name = observationProperties.getHttp().getClient().getRequests().getName();
String metricName = clientProperties.getRequest().getMetricName();
String observationName = observationProperties.getHttp().getClient().getRequests().getName();
String name = (observationName != null) ? observationName : metricName;
MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter(
() -> "Reached the maximum number of URI tags for '%s'. Are you using 'uriVariables'?"
.formatted(name));

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.actuate.metrics.web.client.ObservationRestTemplateCustomizer;
import org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTagsProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.web.client.RestTemplateBuilder;
@ -39,16 +40,39 @@ import org.springframework.web.client.RestTemplate;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(RestTemplateBuilder.class)
@SuppressWarnings("removal")
class RestTemplateObservationConfiguration {
@Bean
ObservationRestTemplateCustomizer observationRestTemplateCustomizer(ObservationRegistry observationRegistry,
ObjectProvider<ClientRequestObservationConvention> customConvention,
ObservationProperties observationProperties, MetricsProperties metricsProperties) {
String name = observationProperties.getHttp().getClient().getRequests().getName();
ClientRequestObservationConvention observationConvention = customConvention
.getIfAvailable(() -> new DefaultClientRequestObservationConvention(name));
ObservationProperties observationProperties, MetricsProperties metricsProperties,
ObjectProvider<RestTemplateExchangeTagsProvider> optionalTagsProvider) {
String name = observationName(observationProperties, metricsProperties);
ClientRequestObservationConvention observationConvention = createConvention(customConvention.getIfAvailable(),
name, optionalTagsProvider.getIfAvailable());
return new ObservationRestTemplateCustomizer(observationRegistry, observationConvention);
}
private static String observationName(ObservationProperties observationProperties,
MetricsProperties metricsProperties) {
String metricName = metricsProperties.getWeb().getClient().getRequest().getMetricName();
String observationName = observationProperties.getHttp().getClient().getRequests().getName();
return (observationName != null) ? observationName : metricName;
}
private static ClientRequestObservationConvention createConvention(
ClientRequestObservationConvention customConvention, String name,
RestTemplateExchangeTagsProvider tagsProvider) {
if (customConvention != null) {
return customConvention;
}
else if (tagsProvider != null) {
return new ClientHttpObservationConventionAdapter(name, tagsProvider);
}
else {
return new DefaultClientRequestObservationConvention(name);
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.actuate.metrics.web.reactive.client.ObservationWebClientCustomizer;
import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -36,16 +37,39 @@ import org.springframework.web.reactive.function.client.WebClient;
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebClient.class)
@SuppressWarnings("removal")
class WebClientObservationConfiguration {
@Bean
ObservationWebClientCustomizer observationWebClientCustomizer(ObservationRegistry observationRegistry,
ObjectProvider<ClientRequestObservationConvention> customConvention,
ObservationProperties observationProperties, MetricsProperties metricsProperties) {
String name = observationProperties.getHttp().getClient().getRequests().getName();
ClientRequestObservationConvention observationConvention = customConvention
.getIfAvailable(() -> new DefaultClientRequestObservationConvention(name));
ObservationProperties observationProperties, ObjectProvider<WebClientExchangeTagsProvider> tagsProvider,
MetricsProperties metricsProperties) {
String name = observationName(observationProperties, metricsProperties);
ClientRequestObservationConvention observationConvention = createConvention(customConvention.getIfAvailable(),
tagsProvider.getIfAvailable(), name);
return new ObservationWebClientCustomizer(observationRegistry, observationConvention);
}
private static ClientRequestObservationConvention createConvention(
ClientRequestObservationConvention customConvention, WebClientExchangeTagsProvider tagsProvider,
String name) {
if (customConvention != null) {
return customConvention;
}
else if (tagsProvider != null) {
return new ClientObservationConventionAdapter(name, tagsProvider);
}
else {
return new DefaultClientRequestObservationConvention(name);
}
}
private static String observationName(ObservationProperties observationProperties,
MetricsProperties metricsProperties) {
String metricName = metricsProperties.getWeb().getClient().getRequest().getMetricName();
String observationName = observationProperties.getHttp().getClient().getRequests().getName();
return (observationName != null) ? observationName : metricName;
}
}

@ -0,0 +1,79 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation.web.reactive;
import java.util.List;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.Tag;
import org.springframework.boot.actuate.metrics.web.reactive.server.DefaultWebFluxTagsProvider;
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsContributor;
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsProvider;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver;
import org.springframework.web.server.i18n.LocaleContextResolver;
import org.springframework.web.server.session.DefaultWebSessionManager;
import org.springframework.web.server.session.WebSessionManager;
/**
* Adapter class that applies {@link WebFluxTagsProvider} tags as a
* {@link ServerRequestObservationConvention}.
*
* @author Brian Clozel
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.0.0", forRemoval = true)
class ServerRequestObservationConventionAdapter implements ServerRequestObservationConvention {
private final WebSessionManager webSessionManager = new DefaultWebSessionManager();
private final ServerCodecConfigurer serverCodecConfigurer = ServerCodecConfigurer.create();
private final LocaleContextResolver localeContextResolver = new AcceptHeaderLocaleContextResolver();
private final String name;
private final WebFluxTagsProvider tagsProvider;
ServerRequestObservationConventionAdapter(String name, WebFluxTagsProvider tagsProvider) {
this.name = name;
this.tagsProvider = tagsProvider;
}
ServerRequestObservationConventionAdapter(String name, List<WebFluxTagsContributor> contributors) {
this(name, new DefaultWebFluxTagsProvider(contributors));
}
@Override
public String getName() {
return this.name;
}
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
DefaultServerWebExchange serverWebExchange = new DefaultServerWebExchange(context.getCarrier(),
context.getResponse(), this.webSessionManager, this.serverCodecConfigurer, this.localeContextResolver);
serverWebExchange.getAttributes().putAll(context.getAttributes());
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(serverWebExchange, context.getError());
return KeyValues.of(tags, Tag::getKey, Tag::getValue);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,24 +16,37 @@
package org.springframework.boot.actuate.autoconfigure.observation.web.reactive;
import java.util.List;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsContributor;
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
/**
* {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring
@ -44,22 +57,75 @@ import org.springframework.core.annotation.Order;
* @author Dmytro Nosan
* @since 3.0.0
*/
@AutoConfiguration(after = { SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class })
@ConditionalOnClass({ Observation.class, MeterRegistry.class })
@ConditionalOnBean({ ObservationRegistry.class, MeterRegistry.class })
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class })
@ConditionalOnClass(Observation.class)
@ConditionalOnBean(ObservationRegistry.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class })
@SuppressWarnings("removal")
public class WebFluxObservationAutoConfiguration {
@Bean
@Order(0)
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties,
private final MetricsProperties metricsProperties;
private final ObservationProperties observationProperties;
public WebFluxObservationAutoConfiguration(MetricsProperties metricsProperties,
ObservationProperties observationProperties) {
String name = observationProperties.getHttp().getServer().getRequests().getName();
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> "Reached the maximum number of URI tags for '%s'.".formatted(name));
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(),
filter);
this.metricsProperties = metricsProperties;
this.observationProperties = observationProperties;
}
@Bean
@ConditionalOnMissingBean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry,
ObjectProvider<ServerRequestObservationConvention> customConvention,
ObjectProvider<WebFluxTagsProvider> tagConfigurer,
ObjectProvider<WebFluxTagsContributor> contributorsProvider) {
String observationName = this.observationProperties.getHttp().getServer().getRequests().getName();
String metricName = this.metricsProperties.getWeb().getServer().getRequest().getMetricName();
String name = (observationName != null) ? observationName : metricName;
WebFluxTagsProvider tagsProvider = tagConfigurer.getIfAvailable();
List<WebFluxTagsContributor> tagsContributors = contributorsProvider.orderedStream().toList();
ServerRequestObservationConvention convention = createConvention(customConvention.getIfAvailable(), name,
tagsProvider, tagsContributors);
return new ServerHttpObservationFilter(registry, convention);
}
private static ServerRequestObservationConvention createConvention(
ServerRequestObservationConvention customConvention, String name, WebFluxTagsProvider tagsProvider,
List<WebFluxTagsContributor> tagsContributors) {
if (customConvention != null) {
return customConvention;
}
if (tagsProvider != null) {
return new ServerRequestObservationConventionAdapter(name, tagsProvider);
}
if (!tagsContributors.isEmpty()) {
return new ServerRequestObservationConventionAdapter(name, tagsContributors);
}
return new DefaultServerRequestObservationConvention(name);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class)
@ConditionalOnBean(MeterRegistry.class)
static class MeterFilterConfiguration {
@Bean
@Order(0)
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties,
ObservationProperties observationProperties) {
String observationName = observationProperties.getHttp().getServer().getRequests().getName();
String name = (observationName != null) ? observationName
: metricsProperties.getWeb().getServer().getRequest().getMetricName();
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> "Reached the maximum number of URI tags for '%s'.".formatted(name));
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(),
filter);
}
}
}

@ -0,0 +1,76 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation.web.servlet;
import java.util.List;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.Tag;
import io.micrometer.observation.Observation;
import org.springframework.boot.actuate.metrics.web.servlet.DefaultWebMvcTagsProvider;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsContributor;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationConvention;
import org.springframework.util.Assert;
import org.springframework.web.servlet.HandlerMapping;
/**
* Adapter class that applies {@link WebMvcTagsProvider} tags as a
* {@link ServerRequestObservationConvention}.
*
* @author Brian Clozel
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.0.0", forRemoval = true)
class ServerRequestObservationConventionAdapter implements ServerRequestObservationConvention {
private final String observationName;
private final WebMvcTagsProvider tagsProvider;
ServerRequestObservationConventionAdapter(String observationName, WebMvcTagsProvider tagsProvider,
List<WebMvcTagsContributor> contributors) {
Assert.state((tagsProvider != null) || (contributors != null),
"adapter should adapt to a WebMvcTagsProvider or a list of contributors");
this.observationName = observationName;
this.tagsProvider = (tagsProvider != null) ? tagsProvider : new DefaultWebMvcTagsProvider(contributors);
}
@Override
public String getName() {
return this.observationName;
}
@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof ServerRequestObservationContext;
}
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
Iterable<Tag> tags = this.tagsProvider.getTags(context.getCarrier(), context.getResponse(), getHandler(context),
context.getError());
return KeyValues.of(tags, Tag::getKey, Tag::getValue);
}
private Object getHandler(ServerRequestObservationContext context) {
return context.getCarrier().getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.boot.actuate.autoconfigure.observation.web.servlet;
import java.util.List;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.observation.Observation;
@ -30,6 +32,8 @@ import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDen
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsContributor;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -62,16 +66,28 @@ import org.springframework.web.servlet.DispatcherServlet;
@ConditionalOnClass({ DispatcherServlet.class, Observation.class })
@ConditionalOnBean(ObservationRegistry.class)
@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class })
@SuppressWarnings("removal")
public class WebMvcObservationAutoConfiguration {
private final MetricsProperties metricsProperties;
private final ObservationProperties observationProperties;
public WebMvcObservationAutoConfiguration(ObservationProperties observationProperties,
MetricsProperties metricsProperties) {
this.observationProperties = observationProperties;
this.metricsProperties = metricsProperties;
}
@Bean
@ConditionalOnMissingFilterBean
public FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(ObservationRegistry registry,
ObjectProvider<ServerRequestObservationConvention> customConvention,
ObservationProperties observationProperties) {
String name = observationProperties.getHttp().getServer().getRequests().getName();
ServerRequestObservationConvention convention = customConvention
.getIfAvailable(() -> new DefaultServerRequestObservationConvention(name));
ObjectProvider<WebMvcTagsProvider> customTagsProvider,
ObjectProvider<WebMvcTagsContributor> contributorsProvider) {
String name = httpRequestsMetricName(this.observationProperties, this.metricsProperties);
ServerRequestObservationConvention convention = createConvention(customConvention.getIfAvailable(), name,
customTagsProvider.getIfAvailable(), contributorsProvider.orderedStream().toList());
ServerHttpObservationFilter filter = new ServerHttpObservationFilter(registry, convention);
FilterRegistrationBean<ServerHttpObservationFilter> registration = new FilterRegistrationBean<>(filter);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
@ -79,6 +95,27 @@ public class WebMvcObservationAutoConfiguration {
return registration;
}
private static ServerRequestObservationConvention createConvention(
ServerRequestObservationConvention customConvention, String name, WebMvcTagsProvider tagsProvider,
List<WebMvcTagsContributor> contributors) {
if (customConvention != null) {
return customConvention;
}
else if (tagsProvider != null || contributors.size() > 0) {
return new ServerRequestObservationConventionAdapter(name, tagsProvider, contributors);
}
else {
return new DefaultServerRequestObservationConvention(name);
}
}
private static String httpRequestsMetricName(ObservationProperties observationProperties,
MetricsProperties metricsProperties) {
String observationName = observationProperties.getHttp().getServer().getRequests().getName();
return (observationName != null) ? observationName
: metricsProperties.getWeb().getServer().getRequest().getMetricName();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class)
@ConditionalOnBean(MeterRegistry.class)
@ -86,9 +123,9 @@ public class WebMvcObservationAutoConfiguration {
@Bean
@Order(0)
MeterFilter metricsHttpServerUriTagFilter(ObservationProperties observationProperties,
MetricsProperties metricsProperties) {
String name = observationProperties.getHttp().getServer().getRequests().getName();
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties,
ObservationProperties observationProperties) {
String name = httpRequestsMetricName(observationProperties, metricsProperties);
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> String.format("Reached the maximum number of URI tags for '%s'.", name));
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(),

@ -1,86 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.OpenTelemetrySdkBuilder;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.resources.ResourceBuilder;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
*
* @author Moritz Halbritter
* @since 3.2.0
*/
@AutoConfiguration
@ConditionalOnClass(OpenTelemetrySdk.class)
@EnableConfigurationProperties(OpenTelemetryProperties.class)
public class OpenTelemetryAutoConfiguration {
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
static final AttributeKey<String> ATTRIBUTE_KEY_SERVICE_NAME = AttributeKey.stringKey("service.name");
@Bean
@ConditionalOnMissingBean(OpenTelemetry.class)
OpenTelemetrySdk openTelemetry(ObjectProvider<SdkTracerProvider> tracerProvider,
ObjectProvider<ContextPropagators> propagators, ObjectProvider<SdkLoggerProvider> loggerProvider,
ObjectProvider<SdkMeterProvider> meterProvider) {
OpenTelemetrySdkBuilder builder = OpenTelemetrySdk.builder();
tracerProvider.ifAvailable(builder::setTracerProvider);
propagators.ifAvailable(builder::setPropagators);
loggerProvider.ifAvailable(builder::setLoggerProvider);
meterProvider.ifAvailable(builder::setMeterProvider);
return builder.build();
}
@Bean
@ConditionalOnMissingBean
Resource openTelemetryResource(Environment environment, OpenTelemetryProperties properties) {
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
return Resource.getDefault()
.merge(Resource.create(Attributes.of(ATTRIBUTE_KEY_SERVICE_NAME, applicationName)))
.merge(toResource(properties));
}
private static Resource toResource(OpenTelemetryProperties properties) {
ResourceBuilder builder = Resource.builder();
properties.getResourceAttributes().forEach(builder::put);
return builder.build();
}
}

@ -1,46 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for OpenTelemetry.
*
* @author Moritz Halbritter
* @since 3.2.0
*/
@ConfigurationProperties(prefix = "management.opentelemetry")
public class OpenTelemetryProperties {
/**
* Resource attributes.
*/
private Map<String, String> resourceAttributes = new HashMap<>();
public Map<String, String> getResourceAttributes() {
return this.resourceAttributes;
}
public void setResourceAttributes(Map<String, String> resourceAttributes) {
this.resourceAttributes = resourceAttributes;
}
}

@ -1,20 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Auto-configuration for OpenTelemetry.
*/
package org.springframework.boot.actuate.autoconfigure.opentelemetry;

@ -1,87 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.r2dbc;
import io.micrometer.observation.ObservationRegistry;
import io.r2dbc.proxy.ProxyConnectionFactory;
import io.r2dbc.proxy.observation.ObservationProxyExecutionListener;
import io.r2dbc.proxy.observation.QueryObservationConvention;
import io.r2dbc.proxy.observation.QueryParametersTagProvider;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.r2dbc.ConnectionFactoryDecorator;
import org.springframework.boot.r2dbc.OptionsCapableConnectionFactory;
import org.springframework.context.annotation.Bean;
/**
* {@link EnableAutoConfiguration Auto-configuration} for R2DBC observability support.
*
* @author Moritz Halbritter
* @since 3.2.0
*/
@AutoConfiguration(after = ObservationAutoConfiguration.class)
@ConditionalOnClass({ ConnectionFactory.class, ProxyConnectionFactory.class })
@EnableConfigurationProperties(R2dbcObservationProperties.class)
public class R2dbcObservationAutoConfiguration {
@Bean
@ConditionalOnBean(ObservationRegistry.class)
ConnectionFactoryDecorator connectionFactoryDecorator(R2dbcObservationProperties properties,
ObservationRegistry observationRegistry,
ObjectProvider<QueryObservationConvention> queryObservationConvention,
ObjectProvider<QueryParametersTagProvider> queryParametersTagProvider) {
return (connectionFactory) -> {
HostAndPort hostAndPort = extractHostAndPort(connectionFactory);
ObservationProxyExecutionListener listener = new ObservationProxyExecutionListener(observationRegistry,
connectionFactory, hostAndPort.host(), hostAndPort.port());
listener.setIncludeParameterValues(properties.isIncludeParameterValues());
queryObservationConvention.ifAvailable(listener::setQueryObservationConvention);
queryParametersTagProvider.ifAvailable(listener::setQueryParametersTagProvider);
return ProxyConnectionFactory.builder(connectionFactory).listener(listener).build();
};
}
private HostAndPort extractHostAndPort(ConnectionFactory connectionFactory) {
OptionsCapableConnectionFactory optionsCapableConnectionFactory = OptionsCapableConnectionFactory
.unwrapFrom(connectionFactory);
if (optionsCapableConnectionFactory == null) {
return HostAndPort.empty();
}
ConnectionFactoryOptions options = optionsCapableConnectionFactory.getOptions();
Object host = options.getValue(ConnectionFactoryOptions.HOST);
Object port = options.getValue(ConnectionFactoryOptions.PORT);
if (!(host instanceof String hostAsString) || !(port instanceof Integer portAsInt)) {
return HostAndPort.empty();
}
return new HostAndPort(hostAsString, portAsInt);
}
private record HostAndPort(String host, Integer port) {
static HostAndPort empty() {
return new HostAndPort(null, null);
}
}
}

@ -1,43 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.r2dbc;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for R2DBC observability.
*
* @author Moritz Halbritter
* @since 3.2.0
*/
@ConfigurationProperties("management.observations.r2dbc")
public class R2dbcObservationProperties {
/**
* Whether to tag actual query parameter values.
*/
private boolean includeParameterValues;
public boolean isIncludeParameterValues() {
return this.includeParameterValues;
}
public void setIncludeParameterValues(boolean includeParameterValues) {
this.includeParameterValues = includeParameterValues;
}
}

@ -1,63 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.scheduling;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
/**
* {@link EnableAutoConfiguration Auto-configuration} to enable observability for
* scheduled tasks.
*
* @author Moritz Halbritter
* @since 3.2.0
*/
@AutoConfiguration(after = ObservationAutoConfiguration.class)
@ConditionalOnBean(ObservationRegistry.class)
@ConditionalOnClass(ThreadPoolTaskScheduler.class)
public class ScheduledTasksObservabilityAutoConfiguration {
@Bean
ObservabilitySchedulingConfigurer observabilitySchedulingConfigurer(ObservationRegistry observationRegistry) {
return new ObservabilitySchedulingConfigurer(observationRegistry);
}
static final class ObservabilitySchedulingConfigurer implements SchedulingConfigurer {
private final ObservationRegistry observationRegistry;
ObservabilitySchedulingConfigurer(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setObservationRegistry(this.observationRegistry);
}
}
}

@ -79,6 +79,7 @@ import org.springframework.core.env.Environment;
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@ConditionalOnClass({ Tracer.class, BraveTracer.class })
@EnableConfigurationProperties(TracingProperties.class)
@ConditionalOnEnabledTracing
public class BraveAutoConfiguration {
private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager();

@ -138,7 +138,7 @@ class CompositePropagationFactory extends Propagation.Factory {
* @return the B3 propagation factory
*/
private Propagation.Factory b3Single() {
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE).build();
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
}
/**

@ -1,69 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.tracing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.ClassUtils;
/**
* {@link EnvironmentPostProcessor} to add a {@link PropertySource} to support log
* correlation IDs when Micrometer Tracing is present. Adds support for the
* {@value LoggingSystem#EXPECT_CORRELATION_ID_PROPERTY} property by delegating to
* {@code management.tracing.enabled}.
*
* @author Jonatan Ivanov
* @author Phillip Webb
*/
class LogCorrelationEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (ClassUtils.isPresent("io.micrometer.tracing.Tracer", application.getClassLoader())) {
environment.getPropertySources().addLast(new LogCorrelationPropertySource(this, environment));
}
}
/**
* Log correlation {@link PropertySource}.
*/
private static class LogCorrelationPropertySource extends PropertySource<Object> {
private static final String NAME = "logCorrelation";
private final Environment environment;
LogCorrelationPropertySource(Object source, Environment environment) {
super(NAME, source);
this.environment = environment;
}
@Override
public Object getProperty(String name) {
if (name.equals(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY)) {
return this.environment.getProperty("management.tracing.enabled", Boolean.class, Boolean.TRUE);
}
return null;
}
}
}

@ -17,26 +17,17 @@
package org.springframework.boot.actuate.autoconfigure.tracing;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.annotation.DefaultNewSpanParser;
import io.micrometer.tracing.annotation.ImperativeMethodInvocationProcessor;
import io.micrometer.tracing.annotation.MethodInvocationProcessor;
import io.micrometer.tracing.annotation.NewSpanParser;
import io.micrometer.tracing.annotation.SpanAspect;
import io.micrometer.tracing.annotation.SpanTagAnnotationHandler;
import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler;
import io.micrometer.tracing.propagation.Propagator;
import org.aspectj.weaver.Advice;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@ -44,12 +35,11 @@ import org.springframework.core.annotation.Order;
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Tracing API.
*
* @author Moritz Halbritter
* @author Jonatan Ivanov
* @since 3.0.0
*/
@AutoConfiguration
@ConditionalOnClass(Tracer.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnEnabledTracing
public class MicrometerTracingAutoConfiguration {
/**
@ -71,6 +61,7 @@ public class MicrometerTracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Tracer.class)
@Order(DEFAULT_TRACING_OBSERVATION_HANDLER_ORDER)
public DefaultTracingObservationHandler defaultTracingObservationHandler(Tracer tracer) {
return new DefaultTracingObservationHandler(tracer);
@ -78,7 +69,7 @@ public class MicrometerTracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Propagator.class)
@ConditionalOnBean({ Tracer.class, Propagator.class })
@Order(SENDER_TRACING_OBSERVATION_HANDLER_ORDER)
public PropagatingSenderTracingObservationHandler<?> propagatingSenderTracingObservationHandler(Tracer tracer,
Propagator propagator) {
@ -87,39 +78,11 @@ public class MicrometerTracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Propagator.class)
@ConditionalOnBean({ Tracer.class, Propagator.class })
@Order(RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER)
public PropagatingReceiverTracingObservationHandler<?> propagatingReceiverTracingObservationHandler(Tracer tracer,
Propagator propagator) {
return new PropagatingReceiverTracingObservationHandler<>(tracer, propagator);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class SpanAspectConfiguration {
@Bean
@ConditionalOnMissingBean
DefaultNewSpanParser newSpanParser() {
return new DefaultNewSpanParser();
}
@Bean
@ConditionalOnMissingBean
ImperativeMethodInvocationProcessor imperativeMethodInvocationProcessor(NewSpanParser newSpanParser,
Tracer tracer, ObjectProvider<SpanTagAnnotationHandler> spanTagAnnotationHandler) {
ImperativeMethodInvocationProcessor methodInvocationProcessor = new ImperativeMethodInvocationProcessor(
newSpanParser, tracer);
spanTagAnnotationHandler.ifAvailable(methodInvocationProcessor::setSpanTagAnnotationHandler);
return methodInvocationProcessor;
}
@Bean
@ConditionalOnMissingBean
SpanAspect spanAspect(MethodInvocationProcessor methodInvocationProcessor) {
return new SpanAspect(methodInvocationProcessor);
}
}
}

@ -36,19 +36,20 @@ import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.SpringBootVersion;
@ -60,20 +61,27 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry tracing.
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
*
* @author Moritz Halbritter
* @author Marcin Grzejszczak
* @author Yanming Zhou
* @since 3.0.0
*/
@AutoConfiguration(value = "openTelemetryTracingAutoConfiguration", before = MicrometerTracingAutoConfiguration.class)
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@ConditionalOnEnabledTracing
@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class })
@EnableConfigurationProperties(TracingProperties.class)
public class OpenTelemetryAutoConfiguration {
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
private final TracingProperties tracingProperties;
OpenTelemetryAutoConfiguration(TracingProperties tracingProperties) {
@ -82,10 +90,23 @@ public class OpenTelemetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
SdkTracerProvider otelSdkTracerProvider(Resource resource, SpanProcessors spanProcessors, Sampler sampler,
ObjectProvider<SdkTracerProviderBuilderCustomizer> customizers) {
SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler).setResource(resource);
spanProcessors.forEach(builder::addSpanProcessor);
OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) {
return OpenTelemetrySdk.builder()
.setTracerProvider(sdkTracerProvider)
.setPropagators(contextPropagators)
.build();
}
@Bean
@ConditionalOnMissingBean
SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider<SpanProcessor> spanProcessors,
Sampler sampler, ObjectProvider<SdkTracerProviderBuilderCustomizer> customizers) {
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
Resource springResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName));
SdkTracerProviderBuilder builder = SdkTracerProvider.builder()
.setSampler(sampler)
.setResource(Resource.getDefault().merge(springResource));
spanProcessors.orderedStream().forEach(builder::addSpanProcessor);
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}
@ -104,26 +125,14 @@ public class OpenTelemetryAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean
SpanProcessors spanProcessors(ObjectProvider<SpanProcessor> spanProcessors) {
return SpanProcessors.of(spanProcessors.orderedStream().toList());
}
@Bean
BatchSpanProcessor otelSpanProcessor(SpanExporters spanExporters,
SpanProcessor otelSpanProcessor(ObjectProvider<SpanExporter> spanExporters,
ObjectProvider<SpanExportingPredicate> spanExportingPredicates, ObjectProvider<SpanReporter> spanReporters,
ObjectProvider<SpanFilter> spanFilters, ObjectProvider<MeterProvider> meterProvider) {
BatchSpanProcessorBuilder builder = BatchSpanProcessor
.builder(new CompositeSpanExporter(spanExporters.list(), spanExportingPredicates.orderedStream().toList(),
spanReporters.orderedStream().toList(), spanFilters.orderedStream().toList()));
meterProvider.ifAvailable(builder::setMeterProvider);
return builder.build();
}
@Bean
@ConditionalOnMissingBean
SpanExporters spanExporters(ObjectProvider<SpanExporter> spanExporters) {
return SpanExporters.of(spanExporters.orderedStream().toList());
ObjectProvider<SpanFilter> spanFilters) {
return BatchSpanProcessor
.builder(new CompositeSpanExporter(spanExporters.orderedStream().toList(),
spanExportingPredicates.orderedStream().toList(), spanReporters.orderedStream().toList(),
spanFilters.orderedStream().toList()))
.build();
}
@Bean

@ -1,76 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import org.springframework.util.Assert;
/**
* A collection of {@link SpanExporter span exporters}.
*
* @author Moritz Halbritter
* @since 3.2.0
*/
@FunctionalInterface
public interface SpanExporters extends Iterable<SpanExporter> {
/**
* Returns the list of {@link SpanExporter span exporters}.
* @return the list of span exporters
*/
List<SpanExporter> list();
@Override
default Iterator<SpanExporter> iterator() {
return list().iterator();
}
@Override
default Spliterator<SpanExporter> spliterator() {
return list().spliterator();
}
/**
* Constructs a {@link SpanExporters} instance with the given {@link SpanExporter span
* exporters}.
* @param spanExporters the span exporters
* @return the constructed {@link SpanExporters} instance
*/
static SpanExporters of(SpanExporter... spanExporters) {
return of(Arrays.asList(spanExporters));
}
/**
* Constructs a {@link SpanExporters} instance with the given list of
* {@link SpanExporter span exporters}.
* @param spanExporters the list of span exporters
* @return the constructed {@link SpanExporters} instance
*/
static SpanExporters of(Collection<? extends SpanExporter> spanExporters) {
Assert.notNull(spanExporters, "SpanExporters must not be null");
List<SpanExporter> copy = List.copyOf(spanExporters);
return () -> copy;
}
}

@ -1,76 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import io.opentelemetry.sdk.trace.SpanProcessor;
import org.springframework.util.Assert;
/**
* A collection of {@link SpanProcessor span processors}.
*
* @author Moritz Halbritter
* @since 3.2.0
*/
@FunctionalInterface
public interface SpanProcessors extends Iterable<SpanProcessor> {
/**
* Returns the list of {@link SpanProcessor span processors}.
* @return the list of span processors
*/
List<SpanProcessor> list();
@Override
default Iterator<SpanProcessor> iterator() {
return list().iterator();
}
@Override
default Spliterator<SpanProcessor> spliterator() {
return list().spliterator();
}
/**
* Constructs a {@link SpanProcessors} instance with the given {@link SpanProcessor
* span processors}.
* @param spanProcessors the span processors
* @return the constructed {@link SpanProcessors} instance
*/
static SpanProcessors of(SpanProcessor... spanProcessors) {
return of(Arrays.asList(spanProcessors));
}
/**
* Constructs a {@link SpanProcessors} instance with the given list of
* {@link SpanProcessor span processors}.
* @param spanProcessors the list of span processors
* @return the constructed {@link SpanProcessors} instance
*/
static SpanProcessors of(Collection<? extends SpanProcessor> spanProcessors) {
Assert.notNull(spanProcessors, "SpanProcessors must not be null");
List<SpanProcessor> copy = List.copyOf(spanProcessors);
return () -> copy;
}
}

@ -16,17 +16,22 @@
package org.springframework.boot.actuate.autoconfigure.tracing.otlp;
import java.util.Map.Entry;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Bean;
/**
* {@link EnableAutoConfiguration Auto-configuration} for OTLP. Brave does not support
@ -40,14 +45,26 @@ import org.springframework.context.annotation.Import;
* define an {@link OtlpGrpcSpanExporter} and this auto-configuration will back off.
*
* @author Jonatan Ivanov
* @author Moritz Halbritter
* @author Eddú Meléndez
* @since 3.1.0
*/
@AutoConfiguration
@ConditionalOnEnabledTracing
@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class, OtlpHttpSpanExporter.class })
@EnableConfigurationProperties(OtlpProperties.class)
@Import({ OtlpTracingConfigurations.ConnectionDetails.class, OtlpTracingConfigurations.Exporters.class })
public class OtlpAutoConfiguration {
@Bean
@ConditionalOnMissingBean(value = OtlpHttpSpanExporter.class,
type = "io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter")
OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties) {
OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder()
.setEndpoint(properties.getEndpoint())
.setTimeout(properties.getTimeout())
.setCompression(properties.getCompression().name().toLowerCase());
for (Entry<String, String> header : properties.getHeaders().entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
return builder.build();
}
}

@ -34,7 +34,7 @@ public class OtlpProperties {
/**
* URL to the OTel collector's HTTP API.
*/
private String endpoint;
private String endpoint = "http://localhost:4318/v1/traces";
/**
* Call timeout for the OTel Collector to process an exported batch of data. This

@ -1,93 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.tracing.otlp;
import java.util.Map.Entry;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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;
/**
* Configurations imported by {@link OtlpAutoConfiguration}.
*
* @author Moritz Halbritter
*/
final class OtlpTracingConfigurations {
private OtlpTracingConfigurations() {
}
@Configuration(proxyBeanMethods = false)
static class ConnectionDetails {
@Bean
@ConditionalOnMissingBean(OtlpTracingConnectionDetails.class)
@ConditionalOnProperty(prefix = "management.otlp.tracing", name = "endpoint")
OtlpTracingConnectionDetails otlpTracingConnectionDetails(OtlpProperties properties) {
return new PropertiesOtlpTracingConnectionDetails(properties);
}
/**
* Adapts {@link OtlpProperties} to {@link OtlpTracingConnectionDetails}.
*/
static class PropertiesOtlpTracingConnectionDetails implements OtlpTracingConnectionDetails {
private final OtlpProperties properties;
PropertiesOtlpTracingConnectionDetails(OtlpProperties properties) {
this.properties = properties;
}
@Override
public String getUrl() {
return this.properties.getEndpoint();
}
}
}
@Configuration(proxyBeanMethods = false)
static class Exporters {
@Bean
@ConditionalOnMissingBean(value = OtlpHttpSpanExporter.class,
type = "io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter")
@ConditionalOnBean(OtlpTracingConnectionDetails.class)
@ConditionalOnEnabledTracing
OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties,
OtlpTracingConnectionDetails connectionDetails) {
OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder()
.setEndpoint(connectionDetails.getUrl())
.setTimeout(properties.getTimeout())
.setCompression(properties.getCompression().name().toLowerCase());
for (Entry<String, String> header : properties.getHeaders().entrySet()) {
builder.addHeader(header.getKey(), header.getValue());
}
return builder.build();
}
}
}

@ -1,35 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.tracing.otlp;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
/**
* Details required to establish a connection to a OpenTelemetry service.
*
* @author Eddú Meléndez
* @since 3.2.0
*/
public interface OtlpTracingConnectionDetails extends ConnectionDetails {
/**
* Address to where metrics will be published.
* @return the address to where metrics will be published
*/
String getUrl();
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -42,6 +43,7 @@ import org.springframework.util.function.SingletonSupplier;
after = MicrometerTracingAutoConfiguration.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnClass({ Tracer.class, SpanContextSupplier.class })
@ConditionalOnEnabledTracing
public class PrometheusExemplarsAutoConfiguration {
@Bean

@ -52,6 +52,7 @@ import org.springframework.context.annotation.Import;
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
WavefrontAutoConfiguration.class })
@ConditionalOnClass({ WavefrontSender.class, WavefrontSpanHandler.class })
@ConditionalOnEnabledTracing
@EnableConfigurationProperties(WavefrontProperties.class)
@Import(WavefrontSenderConfiguration.class)
public class WavefrontTracingAutoConfiguration {
@ -59,7 +60,6 @@ public class WavefrontTracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(WavefrontSender.class)
@ConditionalOnEnabledTracing
WavefrontSpanHandler wavefrontSpanHandler(WavefrontProperties properties, WavefrontSender wavefrontSender,
SpanMetrics spanMetrics, ApplicationTags applicationTags) {
return new WavefrontSpanHandler(properties.getSender().getMaxQueueSize(), wavefrontSender, spanMetrics,
@ -96,7 +96,6 @@ public class WavefrontTracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledTracing
WavefrontBraveSpanHandler wavefrontBraveSpanHandler(WavefrontSpanHandler wavefrontSpanHandler) {
return new WavefrontBraveSpanHandler(wavefrontSpanHandler);
}
@ -109,7 +108,6 @@ public class WavefrontTracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledTracing
WavefrontOtelSpanExporter wavefrontOtelSpanExporter(WavefrontSpanHandler wavefrontSpanHandler) {
return new WavefrontOtelSpanExporter(wavefrontSpanHandler);
}

@ -21,6 +21,7 @@ import zipkin2.codec.BytesEncoder;
import zipkin2.codec.SpanBytesEncoder;
import zipkin2.reporter.Sender;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.BraveConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.OpenTelemetryConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.ReporterConfiguration;
@ -47,6 +48,7 @@ import org.springframework.context.annotation.Import;
@ConditionalOnClass(Sender.class)
@Import({ SenderConfiguration.class, ReporterConfiguration.class, BraveConfiguration.class,
OpenTelemetryConfiguration.class })
@ConditionalOnEnabledTracing
@EnableConfigurationProperties(ZipkinProperties.class)
public class ZipkinAutoConfiguration {

@ -26,7 +26,6 @@ import zipkin2.reporter.brave.ZipkinSpanHandler;
import zipkin2.reporter.urlconnection.URLConnectionSender;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -119,8 +118,7 @@ class ZipkinConfigurations {
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
WebClient.Builder builder = WebClient.builder();
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return new ZipkinWebClientSender(connectionDetails.getSpanEndpoint(), builder.build(),
properties.getConnectTimeout().plus(properties.getReadTimeout()));
return new ZipkinWebClientSender(connectionDetails.getSpanEndpoint(), builder.build());
}
}
@ -144,7 +142,6 @@ class ZipkinConfigurations {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Reporter.class)
@ConditionalOnEnabledTracing
ZipkinSpanHandler zipkinSpanHandler(Reporter<Span> spanReporter) {
return (ZipkinSpanHandler) ZipkinSpanHandler.newBuilder(spanReporter).build();
}
@ -158,7 +155,6 @@ class ZipkinConfigurations {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Sender.class)
@ConditionalOnEnabledTracing
ZipkinSpanExporter zipkinSpanExporter(BytesEncoder<Span> encoder, Sender sender) {
return ZipkinSpanExporter.builder().setEncoder(encoder).setSender(sender).build();
}

@ -16,8 +16,6 @@
package org.springframework.boot.actuate.autoconfigure.tracing.zipkin;
import java.time.Duration;
import reactor.core.publisher.Mono;
import zipkin2.Call;
import zipkin2.Callback;
@ -30,7 +28,6 @@ import org.springframework.web.reactive.function.client.WebClient;
* An {@link HttpSender} which uses {@link WebClient} for HTTP communication.
*
* @author Stefan Bratanov
* @author Moritz Halbritter
*/
class ZipkinWebClientSender extends HttpSender {
@ -38,17 +35,14 @@ class ZipkinWebClientSender extends HttpSender {
private final WebClient webClient;
private final Duration timeout;
ZipkinWebClientSender(String endpoint, WebClient webClient, Duration timeout) {
ZipkinWebClientSender(String endpoint, WebClient webClient) {
this.endpoint = endpoint;
this.webClient = webClient;
this.timeout = timeout;
}
@Override
public HttpPostCall sendSpans(byte[] batchedEncodedSpans) {
return new WebClientHttpPostCall(this.endpoint, batchedEncodedSpans, this.webClient, this.timeout);
return new WebClientHttpPostCall(this.endpoint, batchedEncodedSpans, this.webClient);
}
private static class WebClientHttpPostCall extends HttpPostCall {
@ -57,18 +51,15 @@ class ZipkinWebClientSender extends HttpSender {
private final WebClient webClient;
private final Duration timeout;
WebClientHttpPostCall(String endpoint, byte[] body, WebClient webClient, Duration timeout) {
WebClientHttpPostCall(String endpoint, byte[] body, WebClient webClient) {
super(body);
this.endpoint = endpoint;
this.webClient = webClient;
this.timeout = timeout;
}
@Override
public Call<Void> clone() {
return new WebClientHttpPostCall(this.endpoint, getUncompressedBody(), this.webClient, this.timeout);
return new WebClientHttpPostCall(this.endpoint, getUncompressedBody(), this.webClient);
}
@Override
@ -88,8 +79,7 @@ class ZipkinWebClientSender extends HttpSender {
.headers(this::addDefaultHeaders)
.bodyValue(getBody())
.retrieve()
.toBodilessEntity()
.timeout(this.timeout);
.toBodilessEntity();
}
private void addDefaultHeaders(HttpHeaders headers) {

@ -25,8 +25,6 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.wavefront.sdk.common.clients.service.token.TokenService.Type;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PushRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
@ -59,11 +57,6 @@ public class WavefrontProperties {
*/
private String apiToken;
/**
* Type of the API token.
*/
private TokenType apiTokenType;
/**
* Application configuration.
*/
@ -139,7 +132,7 @@ public class WavefrontProperties {
* @return the API token
*/
public String getApiTokenOrThrow() {
if (this.apiTokenType != TokenType.NO_TOKEN && this.apiToken == null && !usesProxy()) {
if (this.apiToken == null && !usesProxy()) {
throw new InvalidConfigurationPropertyValueException("management.wavefront.api-token", null,
"This property is mandatory whenever publishing directly to the Wavefront API");
}
@ -174,31 +167,6 @@ public class WavefrontProperties {
this.traceDerivedCustomTagKeys = traceDerivedCustomTagKeys;
}
public TokenType getApiTokenType() {
return this.apiTokenType;
}
public void setApiTokenType(TokenType apiTokenType) {
this.apiTokenType = apiTokenType;
}
/**
* Returns the {@link Type Wavefront token type}.
* @return the Wavefront token type
* @since 3.2.0
*/
public Type getWavefrontApiTokenType() {
if (this.apiTokenType == null) {
return usesProxy() ? Type.NO_TOKEN : Type.WAVEFRONT_API_TOKEN;
}
return switch (this.apiTokenType) {
case NO_TOKEN -> Type.NO_TOKEN;
case WAVEFRONT_API_TOKEN -> Type.WAVEFRONT_API_TOKEN;
case CSP_API_TOKEN -> Type.CSP_API_TOKEN;
case CSP_CLIENT_CREDENTIALS -> Type.CSP_CLIENT_CREDENTIALS;
};
}
public static class Application {
/**
@ -417,30 +385,4 @@ public class WavefrontProperties {
}
/**
* Wavefront token type.
*
* @since 3.2.0
*/
public enum TokenType {
/**
* No token.
*/
NO_TOKEN,
/**
* Wavefront API token.
*/
WAVEFRONT_API_TOKEN,
/**
* CSP API token.
*/
CSP_API_TOKEN,
/**
* CSP client credentials.
*/
CSP_CLIENT_CREDENTIALS
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,17 +21,13 @@ import java.time.Duration;
import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.clients.WavefrontClient.Builder;
import org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport;
import org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.actuate.autoconfigure.tracing.wavefront.WavefrontTracingAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;
@ -50,10 +46,8 @@ public class WavefrontSenderConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(WavefrontTracingOrMetricsCondition.class)
public WavefrontSender wavefrontSender(WavefrontProperties properties) {
Builder builder = new Builder(properties.getEffectiveUri().toString(), properties.getWavefrontApiTokenType(),
properties.getApiTokenOrThrow());
Builder builder = new Builder(properties.getEffectiveUri().toString(), properties.getApiTokenOrThrow());
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
WavefrontProperties.Sender sender = properties.getSender();
map.from(sender.getMaxQueueSize()).to(builder::maxQueueSize);
@ -63,22 +57,4 @@ public class WavefrontSenderConfiguration {
return builder.build();
}
static final class WavefrontTracingOrMetricsCondition extends AnyNestedCondition {
WavefrontTracingOrMetricsCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnEnabledTracing
static class TracingCondition {
}
@ConditionalOnEnabledMetricsExport("wavefront")
static class MetricsCondition {
}
}
}

@ -60,8 +60,8 @@ public final class ManagementContextFactory {
Environment parentEnvironment = parentContext.getEnvironment();
ConfigurableEnvironment childEnvironment = ApplicationContextFactory.DEFAULT
.createEnvironment(this.webApplicationType);
if (parentEnvironment instanceof ConfigurableEnvironment configurableEnvironment) {
childEnvironment.setConversionService((configurableEnvironment).getConversionService());
if (parentEnvironment instanceof ConfigurableEnvironment) {
childEnvironment.setConversionService(((ConfigurableEnvironment) parentEnvironment).getConversionService());
}
ConfigurableApplicationContext managementContext = ApplicationContextFactory.DEFAULT
.create(this.webApplicationType);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,10 +26,8 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServe
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.embedded.JettyVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.JettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.NettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer;
@ -78,10 +76,8 @@ public class ReactiveManagementChildContextConfiguration {
ReactiveManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
super(beanFactory, ReactiveWebServerFactoryCustomizer.class, TomcatWebServerFactoryCustomizer.class,
TomcatReactiveWebServerFactoryCustomizer.class,
TomcatVirtualThreadsWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class,
JettyVirtualThreadsWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class,
NettyWebServerFactoryCustomizer.class);
TomcatReactiveWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class,
UndertowWebServerFactoryCustomizer.class, NettyWebServerFactoryCustomizer.class);
}
}

@ -217,8 +217,8 @@ class ChildManagementContextInitializer
}
static void addIfPossible(ApplicationContext parentContext, ConfigurableApplicationContext childContext) {
if (parentContext instanceof ConfigurableApplicationContext configurableApplicationContext) {
add(configurableApplicationContext, childContext);
if (parentContext instanceof ConfigurableApplicationContext) {
add((ConfigurableApplicationContext) parentContext, childContext);
}
}

@ -39,9 +39,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.embedded.JettyVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.JettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer;
@ -124,8 +122,7 @@ class ServletManagementChildContextConfiguration {
ServletManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
super(beanFactory, ServletWebServerFactoryCustomizer.class, TomcatServletWebServerFactoryCustomizer.class,
TomcatWebServerFactoryCustomizer.class, TomcatVirtualThreadsWebServerFactoryCustomizer.class,
JettyWebServerFactoryCustomizer.class, JettyVirtualThreadsWebServerFactoryCustomizer.class,
TomcatWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class,
UndertowServletWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class);
}

@ -1987,19 +1987,11 @@
"reason": "Should be applied at the ObservationRegistry level."
}
},
{
"name": "management.metrics.web.client.request.metric-name",
"type": "java.lang.String",
"deprecation": {
"replacement": "management.observations.http.client.requests.name",
"level": "error"
}
},
{
"name": "management.metrics.web.client.requests-metric-name",
"type": "java.lang.String",
"deprecation": {
"replacement": "management.observations.http.client.requests.name",
"replacement": "management.metrics.web.client.request.metric-name",
"level": "error"
}
},
@ -2045,26 +2037,14 @@
"reason": "Not needed anymore, direct instrumentation in Spring MVC."
}
},
{
"name": "management.metrics.web.server.request.metric-name",
"type": "java.lang.String",
"deprecation": {
"replacement": "management.observations.http.server.requests.name",
"level": "error"
}
},
{
"name": "management.metrics.web.server.requests-metric-name",
"type": "java.lang.String",
"deprecation": {
"replacement": "management.observations.http.server.requests.name",
"replacement": "management.metrics.web.server.request.metric-name",
"level": "error"
}
},
{
"name": "management.otlp.metrics.export.base-time-unit",
"defaultValue": "milliseconds"
},
{
"name": "management.otlp.tracing.compression",
"defaultValue": "none"

@ -1,7 +1,3 @@
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.actuate.autoconfigure.metrics.ValidationFailureAnalyzer
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.actuate.autoconfigure.tracing.LogCorrelationEnvironmentPostProcessor

@ -43,7 +43,6 @@ org.springframework.boot.actuate.autoconfigure.metrics.JvmMetricsAutoConfigurati
org.springframework.boot.actuate.autoconfigure.metrics.KafkaMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.Log4J2MetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.LogbackMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.MetricsAspectsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.SystemMetricsAutoConfiguration
@ -71,7 +70,6 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdMetri
org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.batch.BatchObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.graphql.GraphQlObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.jms.JmsTemplateObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.integration.IntegrationMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.jersey.JerseyServerMetricsAutoConfiguration
@ -90,14 +88,11 @@ org.springframework.boot.actuate.autoconfigure.data.mongo.MongoReactiveHealthCon
org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration
org.springframework.boot.actuate.autoconfigure.quartz.QuartzEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.r2dbc.ConnectionFactoryHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.r2dbc.R2dbcObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.data.redis.RedisHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.data.redis.RedisReactiveHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksObservabilityAutoConfiguration
org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration
@ -116,4 +111,4 @@ org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesEndpoi
org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementContextAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration

@ -35,13 +35,10 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import static org.assertj.core.api.Assertions.assertThat;
@ -55,14 +52,15 @@ class CloudFoundryReactiveHealthEndpointWebExtensionTests {
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withPropertyValues("VCAP_APPLICATION={}")
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
WebFluxAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
ReactiveUserDetailsServiceAutoConfiguration.class, WebFluxAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
ReactiveCloudFoundryActuatorAutoConfigurationTests.WebClientCustomizerConfig.class,
WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
ReactiveCloudFoundryActuatorAutoConfiguration.class))
.withUserConfiguration(TestHealthIndicator.class, UserDetailsServiceConfiguration.class);
.withUserConfiguration(TestHealthIndicator.class);
@Test
void healthComponentsAlwaysPresent() {
@ -84,15 +82,4 @@ class CloudFoundryReactiveHealthEndpointWebExtensionTests {
}
@Configuration(proxyBeanMethods = false)
static class UserDetailsServiceConfiguration {
@Bean
MapReactiveUserDetailsService userDetailsService() {
return new MapReactiveUserDetailsService(
User.withUsername("alice").password("secret").roles("admin").build());
}
}
}

@ -50,6 +50,7 @@ import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConf
import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
@ -60,8 +61,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.test.util.ReflectionTestUtils;
@ -85,16 +84,15 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
private static final String V3_JSON = ApiVersion.V3.getProducedMimeType().toString();
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class, WebFluxAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebClientCustomizerConfig.class,
WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class))
.withUserConfiguration(UserDetailsServiceConfiguration.class);
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
ReactiveUserDetailsServiceAutoConfiguration.class, WebFluxAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebClientCustomizerConfig.class,
WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class));
private static final String BASE_PATH = "/cloudfoundryapplication";
@ -360,15 +358,4 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class UserDetailsServiceConfiguration {
@Bean
MapReactiveUserDetailsService userDetailsService() {
return new MapReactiveUserDetailsService(
User.withUsername("alice").password("secret").roles("admin").build());
}
}
}

@ -51,11 +51,13 @@ class ReactiveCloudFoundrySecurityServiceTests {
private MockWebServer server;
private WebClient.Builder builder;
@BeforeEach
void setup() {
this.server = new MockWebServer();
WebClient.Builder builder = WebClient.builder().baseUrl(this.server.url("/").toString());
this.securityService = new ReactiveCloudFoundrySecurityService(builder, CLOUD_CONTROLLER, false);
this.builder = WebClient.builder().baseUrl(this.server.url("/").toString());
this.securityService = new ReactiveCloudFoundrySecurityService(this.builder, CLOUD_CONTROLLER, false);
}
@AfterEach
@ -181,7 +183,7 @@ class ReactiveCloudFoundrySecurityServiceTests {
response.setHeader("Content-Type", "application/json");
});
StepVerifier.create(this.securityService.fetchTokenKeys())
.consumeNextWith((tokenKeys) -> assertThat(tokenKeys).isEmpty())
.consumeNextWith((tokenKeys) -> assertThat(tokenKeys).hasSize(0))
.expectComplete()
.verify();
expectRequest((request) -> assertThat(request.getPath()).isEqualTo("/my-cloud-controller.com/info"));

@ -0,0 +1,57 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.health;
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfigurationReflectionTests.TestHealthIndicator;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.boot.actuate.health.HealthContributor;
/**
* Tests for {@link CompositeHealthContributorConfiguration} using reflection to create
* indicator instances.
*
* @author Phillip Webb
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.0.0", forRemoval = true)
class CompositeHealthContributorConfigurationReflectionTests
extends AbstractCompositeHealthContributorConfigurationTests<HealthContributor, TestHealthIndicator> {
@Override
protected AbstractCompositeHealthContributorConfiguration<HealthContributor, TestHealthIndicator, TestBean> newComposite() {
return new ReflectiveTestCompositeHealthContributorConfiguration();
}
static class ReflectiveTestCompositeHealthContributorConfiguration
extends CompositeHealthContributorConfiguration<TestHealthIndicator, TestBean> {
}
static class TestHealthIndicator extends AbstractHealthIndicator {
TestHealthIndicator(TestBean testBean) {
}
@Override
protected void doHealthCheck(Builder builder) throws Exception {
builder.up();
}
}
}

@ -0,0 +1,60 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.health;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthContributorConfigurationReflectionTests.TestReactiveHealthIndicator;
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
/**
* Tests for {@link CompositeReactiveHealthContributorConfiguration} using reflection to
* create indicator instances.
*
* @author Phillip Webb
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.0.0", forRemoval = true)
class CompositeReactiveHealthContributorConfigurationReflectionTests extends
AbstractCompositeHealthContributorConfigurationTests<ReactiveHealthContributor, TestReactiveHealthIndicator> {
@Override
protected AbstractCompositeHealthContributorConfiguration<ReactiveHealthContributor, TestReactiveHealthIndicator, TestBean> newComposite() {
return new TestCompositeReactiveHealthContributorConfiguration();
}
static class TestCompositeReactiveHealthContributorConfiguration
extends CompositeReactiveHealthContributorConfiguration<TestReactiveHealthIndicator, TestBean> {
}
static class TestReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
TestReactiveHealthIndicator(TestBean testBean) {
}
@Override
protected Mono<Health> doHealthCheck(Builder builder) {
return Mono.just(builder.up().build());
}
}
}

@ -32,8 +32,6 @@ import static org.mockito.Mockito.mock;
*
* @author Eddú Meléndez
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.2.0", forRemoval = true)
class InfluxDbHealthContributorAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()

@ -19,8 +19,6 @@ package org.springframework.boot.actuate.autoconfigure.integrationtest;
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration;
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -42,7 +40,6 @@ import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfigurati
import org.springframework.boot.context.annotation.UserConfigurations;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import static org.assertj.core.api.Assertions.assertThat;
@ -60,7 +57,6 @@ class WebEndpointsAutoConfigurationIntegrationTests {
}
@Test
@ClassPathExclusions({ "spring-security-oauth2-client-*.jar", "spring-security-oauth2-resource-server-*.jar" })
void healthEndpointReactiveWebExtensionIsAutoConfigured() {
reactiveWebRunner()
.run((context) -> assertThat(context).hasSingleBean(ReactiveHealthEndpointWebExtension.class));
@ -84,8 +80,7 @@ class WebEndpointsAutoConfigurationIntegrationTests {
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class, HazelcastAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class, RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class, BraveAutoConfiguration.class,
OpenTelemetryAutoConfiguration.class })
RedisRepositoriesAutoConfiguration.class })
@SpringBootConfiguration
static class WebEndpointTestApplication {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save