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"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="LombokGetterMayBeUsed" enabled="false" level="WARNING" enabled_by_default="false" /> <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" /> <inspection_tool class="UnqualifiedFieldAccess" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
</profile> </profile>
</component> </component>

@ -5,7 +5,6 @@
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions. 1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, "License" shall mean the terms and conditions for use, reproduction,

@ -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 :docs: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference
:github: https://github.com/spring-projects/spring-boot :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: Our primary goals are:
* Provide a radically faster and widely accessible getting started experience for all Spring development. * 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. * 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). * 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" description = "Spring Boot Build"
defaultTasks 'build' defaultTasks 'build'
nohttp { nohttp {

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

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

@ -19,11 +19,13 @@ package org.springframework.boot.build.bom.bomr;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -94,14 +96,19 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
getLaterVersionsForModule(group.getId(), plugin, library)); getLaterVersionsForModule(group.getId(), plugin, library));
} }
} }
return moduleVersions.values() List<DependencyVersion> allVersions = moduleVersions.values()
.stream() .stream()
.flatMap(SortedSet::stream) .flatMap(SortedSet::stream)
.distinct() .distinct()
.filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion)) .filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion))
.map((version) -> (VersionOption) new VersionOption.ResolvedVersionOption(version,
getMissingModules(moduleVersions, version)))
.toList(); .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, 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.reactor");
config.accept("spring.ssl"); config.accept("spring.ssl");
config.accept("spring.task"); config.accept("spring.task");
config.accept("spring.threads");
config.accept("spring.mandatory-file-encoding"); config.accept("spring.mandatory-file-encoding");
config.accept("info"); config.accept("info");
config.accept("spring.output.ansi.enabled"); config.accept("spring.output.ansi.enabled");
@ -171,7 +170,6 @@ public class DocumentConfigurationProperties extends DefaultTask {
prefix.accept("spring.integration"); prefix.accept("spring.integration");
prefix.accept("spring.jms"); prefix.accept("spring.jms");
prefix.accept("spring.kafka"); prefix.accept("spring.kafka");
prefix.accept("spring.pulsar");
prefix.accept("spring.rabbitmq"); prefix.accept("spring.rabbitmq");
prefix.accept("spring.hazelcast"); prefix.accept("spring.hazelcast");
prefix.accept("spring.webservices"); prefix.accept("spring.webservices");

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

@ -11,7 +11,7 @@ The pipeline can be deployed using the following command:
[source] [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 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 ADD get-docker-compose-url.sh /get-docker-compose-url.sh
RUN ./setup.sh java17 java21 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 JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh 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 ADD get-docker-compose-url.sh /get-docker-compose-url.sh
RUN ./setup.sh java17 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 JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -104,7 +105,7 @@ class SonatypeServiceTests {
.filter((artifact) -> !artifact.startsWith("build-info.json")) .filter((artifact) -> !artifact.startsWith("build-info.json"))
.map((artifact) -> requestTo( .map((artifact) -> requestTo(
"/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString())) "/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString()))
.collect(Collectors.toSet()); .collect(Collectors.toCollection(HashSet::new));
AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads); AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads);
assertThat(uploadRequestsMatcher.candidates).hasSize(150); assertThat(uploadRequestsMatcher.candidates).hasSize(150);
this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT)) this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT))
@ -132,7 +133,7 @@ class SonatypeServiceTests {
.andRespond(withSuccess()); .andRespond(withSuccess());
this.service.publish(getReleaseInfo(), artifactsRoot); this.service.publish(getReleaseInfo(), artifactsRoot);
this.server.verify(); 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())) .filter((artifact) -> !"build-info.json".equals(artifact.toString()))
.map((artifact) -> requestTo( .map((artifact) -> requestTo(
"/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString())) "/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString()))
.collect(Collectors.toSet()); .collect(Collectors.toCollection(HashSet::new));
AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads); AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads);
assertThat(uploadRequestsMatcher.candidates).hasSize(150); assertThat(uploadRequestsMatcher.candidates).hasSize(150);
this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT)) this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT))
@ -184,7 +185,7 @@ class SonatypeServiceTests {
.isThrownBy(() -> this.service.publish(getReleaseInfo(), artifactsRoot)) .isThrownBy(() -> this.service.publish(getReleaseInfo(), artifactsRoot))
.withMessage("Close failed"); .withMessage("Close failed");
this.server.verify(); this.server.verify();
assertThat(uploadRequestsMatcher.candidates).isEmpty(); assertThat(uploadRequestsMatcher.candidates).hasSize(0);
} }
} }

@ -2,13 +2,12 @@
set -ex set -ex
########################################################### ###########################################################
# OS and UTILS # UTILS
########################################################### ###########################################################
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt-get update 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 apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq
locale-gen en_US.utf8
ln -fs /usr/share/zoneinfo/UTC /etc/localtime ln -fs /usr/share/zoneinfo/UTC /etc/localtime
dpkg-reconfigure --frontend noninteractive tzdata dpkg-reconfigure --frontend noninteractive tzdata
rm -rf /var/lib/apt/lists/* 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" homebrew-tap-repo: "https://github.com/spring-io/homebrew-tap.git"
docker-hub-organization: "springci" docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io" artifactory-server: "https://repo.spring.io"
branch: "main" branch: "3.1.x"
milestone: "3.2.x" milestone: "3.1.x"
build-name: "spring-boot" build-name: "spring-boot"
concourse-url: "https://ci.spring.io" concourse-url: "https://ci.spring.io"
task-timeout: 2h00m task-timeout: 2h00m

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

@ -11,8 +11,8 @@
xmlns:setup.workingsets="http://www.eclipse.org/oomph/setup/workingsets/1.0" xmlns:setup.workingsets="http://www.eclipse.org/oomph/setup/workingsets/1.0"
xmlns:workingsets="http://www.eclipse.org/oomph/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" 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" name="spring.boot.3.1.x"
label="Spring Boot 3.2.x"> label="Spring Boot 3.1.x">
<setupTask <setupTask
xsi:type="setup:VariableTask" xsi:type="setup:VariableTask"
type="FOLDER" type="FOLDER"
@ -136,7 +136,7 @@
name="spring-boot-tools"> name="spring-boot-tools">
<predicate <predicate
xsi:type="predicates:NamePredicate" 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>
<workingSet <workingSet
name="spring-boot-starters"> name="spring-boot-starters">

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

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

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

@ -5,7 +5,6 @@ pluginManagement {
if (version.endsWith('-SNAPSHOT')) { if (version.endsWith('-SNAPSHOT')) {
maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/snapshot" }
} }
} }
resolutionStrategy { resolutionStrategy {
eachPlugin { 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-buildpack-platform"
include "spring-boot-project:spring-boot-tools:spring-boot-cli" 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"
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-configuration-processor"
include "spring-boot-project:spring-boot-tools:spring-boot-gradle-plugin" 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-gradle-test-support"
include "spring-boot-project:spring-boot-tools:spring-boot-jarmode-layertools" 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"
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-loader-tools"
include "spring-boot-project:spring-boot-tools:spring-boot-maven-plugin" include "spring-boot-project:spring-boot-tools:spring-boot-maven-plugin"
include "spring-boot-project:spring-boot-tools:spring-boot-properties-migrator" 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-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-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-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-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-deployment-tests"
include "spring-boot-system-tests:spring-boot-image-tests" include "spring-boot-system-tests:spring-boot-image-tests"

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

@ -53,6 +53,8 @@ class ReactiveCloudFoundrySecurityService {
private final String cloudControllerUrl; private final String cloudControllerUrl;
private Mono<String> uaaUrl;
ReactiveCloudFoundrySecurityService(WebClient.Builder webClientBuilder, String cloudControllerUrl, ReactiveCloudFoundrySecurityService(WebClient.Builder webClientBuilder, String cloudControllerUrl,
boolean skipSslValidation) { boolean skipSslValidation) {
Assert.notNull(webClientBuilder, "WebClient must not be null"); Assert.notNull(webClientBuilder, "WebClient must not be null");
@ -147,7 +149,7 @@ class ReactiveCloudFoundrySecurityService {
* @return the UAA url Mono * @return the UAA url Mono
*/ */
Mono<String> getUaaUrl() { Mono<String> getUaaUrl() {
return this.webClient.get() this.uaaUrl = this.webClient.get()
.uri(this.cloudControllerUrl + "/info") .uri(this.cloudControllerUrl + "/info")
.retrieve() .retrieve()
.bodyToMono(Map.class) .bodyToMono(Map.class)
@ -155,6 +157,7 @@ class ReactiveCloudFoundrySecurityService {
.cache() .cache()
.onErrorMap((ex) -> new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, .onErrorMap((ex) -> new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
"Unable to fetch token keys from UAA.")); "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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -86,7 +86,7 @@ class CloudFoundrySecurityInterceptor {
return SecurityResponse.success(); return SecurityResponse.success();
} }
private void check(HttpServletRequest request, EndpointId endpointId) { private void check(HttpServletRequest request, EndpointId endpointId) throws Exception {
Token token = getToken(request); Token token = getToken(request);
this.tokenValidator.validate(token); this.tokenValidator.validate(token);
AccessLevel accessLevel = this.cloudFoundrySecurityService.getAccessLevel(token.toString(), this.applicationId); AccessLevel accessLevel = this.cloudFoundrySecurityService.getAccessLevel(token.toString(), this.applicationId);

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

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

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

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

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

@ -16,9 +16,12 @@
package org.springframework.boot.actuate.autoconfigure.health; package org.springframework.boot.actuate.autoconfigure.health;
import java.lang.reflect.Constructor;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -36,6 +39,18 @@ public abstract class AbstractCompositeHealthContributorConfiguration<C, I exten
private final Function<B, I> indicatorFactory; 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 * Creates a {@code AbstractCompositeHealthContributorConfiguration} that will use the
* given {@code indicatorFactory} to create health indicator instances. * given {@code indicatorFactory} to create health indicator instances.
@ -60,4 +75,34 @@ public abstract class AbstractCompositeHealthContributorConfiguration<C, I exten
return this.indicatorFactory.apply(bean); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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> public abstract class CompositeHealthContributorConfiguration<I extends HealthIndicator, B>
extends AbstractCompositeHealthContributorConfiguration<HealthContributor, I, 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 * Creates a {@code CompositeHealthContributorConfiguration} that will use the given
* {@code indicatorFactory} to create {@link HealthIndicator} instances. * {@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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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> public abstract class CompositeReactiveHealthContributorConfiguration<I extends ReactiveHealthIndicator, B>
extends AbstractCompositeHealthContributorConfiguration<ReactiveHealthContributor, I, 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 * Creates a {@code CompositeReactiveHealthContributorConfiguration} that will use the
* given {@code indicatorFactory} to create {@link ReactiveHealthIndicator} instances. * given {@code indicatorFactory} to create {@link ReactiveHealthIndicator} instances.

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

@ -81,8 +81,7 @@ class HealthEndpointWebExtensionConfiguration {
return webEndpoints.stream() return webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID)) .filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID))
.findFirst() .findFirst()
.orElseThrow( .get();
() -> new IllegalStateException("No endpoint with id '%s' found".formatted(HealthEndpoint.ID)));
} }
@ConditionalOnBean(DispatcherServlet.class) @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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * @author Eddú Meléndez
* @since 2.0.0 * @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) @AutoConfiguration(after = InfluxDbAutoConfiguration.class)
@ConditionalOnClass(InfluxDB.class) @ConditionalOnClass(InfluxDB.class)
@ConditionalOnBean(InfluxDB.class) @ConditionalOnBean(InfluxDB.class)
@ConditionalOnEnabledHealthIndicator("influxdb") @ConditionalOnEnabledHealthIndicator("influxdb")
@Deprecated(since = "3.2.0", forRemoval = true)
public class InfluxDbHealthContributorAutoConfiguration public class InfluxDbHealthContributorAutoConfiguration
extends CompositeHealthContributorConfiguration<InfluxDbHealthIndicator, InfluxDB> { 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -79,8 +79,6 @@ public class MetricsProperties {
return this.enable; 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() { public Map<String, String> getTags() {
return this.tags; return this.tags;
} }
@ -117,6 +115,8 @@ public class MetricsProperties {
public static class Client { public static class Client {
private final ClientRequest request = new ClientRequest();
/** /**
* Maximum number of unique URI tag values allowed. After the max number of * 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 * tag values is reached, metrics with additional tag values are denied by
@ -124,6 +124,10 @@ public class MetricsProperties {
*/ */
private int maxUriTags = 100; private int maxUriTags = 100;
public ClientRequest getRequest() {
return this.request;
}
public int getMaxUriTags() { public int getMaxUriTags() {
return this.maxUriTags; return this.maxUriTags;
} }
@ -132,10 +136,32 @@ public class MetricsProperties {
this.maxUriTags = maxUriTags; 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 { public static class Server {
private final ServerRequest request = new ServerRequest();
/** /**
* Maximum number of unique URI tag values allowed. After the max number of * 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 * tag values is reached, metrics with additional tag values are denied by
@ -143,6 +169,10 @@ public class MetricsProperties {
*/ */
private int maxUriTags = 100; private int maxUriTags = 100;
public ServerRequest getRequest() {
return this.request;
}
public int getMaxUriTags() { public int getMaxUriTags() {
return this.maxUriTags; return this.maxUriTags;
} }
@ -151,6 +181,27 @@ public class MetricsProperties {
this.maxUriTags = maxUriTags; 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; private final MeterFilter mapFilter;
@SuppressWarnings("removal")
public PropertiesMeterFilter(MetricsProperties properties) { public PropertiesMeterFilter(MetricsProperties properties) {
Assert.notNull(properties, "Properties must not be null"); Assert.notNull(properties, "Properties must not be null");
this.properties = properties; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; 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() { public Map<String, String> getDefaultDimensions() {
return this.defaultDimensions; return this.defaultDimensions;
} }
@ -178,14 +172,6 @@ public class DynatraceProperties extends StepRegistryProperties {
this.useDynatraceSummaryInstruments = useDynatraceSummaryInstruments; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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); 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) { private <V> Function<DynatraceProperties, V> v1(Function<V1, V> getter) {
return (properties) -> getter.apply(properties.getV1()); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport; 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.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to OTLP. * {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to OTLP.
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Moritz Halbritter
* @since 3.0.0 * @since 3.0.0
*/ */
@AutoConfiguration( @AutoConfiguration(
@ -47,27 +44,19 @@ import org.springframework.core.env.Environment;
@ConditionalOnBean(Clock.class) @ConditionalOnBean(Clock.class)
@ConditionalOnClass(OtlpMeterRegistry.class) @ConditionalOnClass(OtlpMeterRegistry.class)
@ConditionalOnEnabledMetricsExport("otlp") @ConditionalOnEnabledMetricsExport("otlp")
@EnableConfigurationProperties({ OtlpProperties.class, OpenTelemetryProperties.class }) @EnableConfigurationProperties(OtlpProperties.class)
public class OtlpMetricsExportAutoConfiguration { public class OtlpMetricsExportAutoConfiguration {
private final OtlpProperties properties; private final OtlpProperties properties;
OtlpMetricsExportAutoConfiguration(OtlpProperties properties) { public OtlpMetricsExportAutoConfiguration(OtlpProperties properties) {
this.properties = properties; this.properties = properties;
} }
@Bean
@ConditionalOnMissingBean(OtlpMetricsConnectionDetails.class)
OtlpMetricsConnectionDetails otlpMetricsConnectionDetails() {
return new PropertiesOtlpMetricsConnectionDetails(this.properties);
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
OtlpConfig otlpConfig(OpenTelemetryProperties openTelemetryProperties, public OtlpConfig otlpConfig() {
OtlpMetricsConnectionDetails connectionDetails, Environment environment) { return new OtlpPropertiesConfigAdapter(this.properties);
return new OtlpPropertiesConfigAdapter(this.properties, openTelemetryProperties, connectionDetails,
environment);
} }
@Bean @Bean
@ -76,22 +65,4 @@ public class OtlpMetricsExportAutoConfiguration {
return new OtlpMeterRegistry(otlpConfig, clock); 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; package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.micrometer.registry.otlp.AggregationTemporality; import io.micrometer.registry.otlp.AggregationTemporality;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties; import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
/** /**
* {@link ConfigurationProperties @ConfigurationProperties} for configuring OTLP metrics * {@link ConfigurationProperties @ConfigurationProperties} for configuring OTLP metrics
@ -57,11 +55,6 @@ public class OtlpProperties extends StepRegistryProperties {
*/ */
private Map<String, String> headers; private Map<String, String> headers;
/**
* Time unit for exported metrics.
*/
private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS;
public String getUrl() { public String getUrl() {
return this.url; return this.url;
} }
@ -78,13 +71,10 @@ public class OtlpProperties extends StepRegistryProperties {
this.aggregationTemporality = aggregationTemporality; 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() { public Map<String, String> getResourceAttributes() {
return this.resourceAttributes; return this.resourceAttributes;
} }
@Deprecated(since = "3.2.0", forRemoval = true)
public void setResourceAttributes(Map<String, String> resourceAttributes) { public void setResourceAttributes(Map<String, String> resourceAttributes) {
this.resourceAttributes = resourceAttributes; this.resourceAttributes = resourceAttributes;
} }
@ -97,12 +87,4 @@ public class OtlpProperties extends StepRegistryProperties {
this.headers = headers; 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; package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.micrometer.registry.otlp.AggregationTemporality; import io.micrometer.registry.otlp.AggregationTemporality;
import io.micrometer.registry.otlp.OtlpConfig; import io.micrometer.registry.otlp.OtlpConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter; 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}. * Adapter to convert {@link OtlpProperties} to an {@link OtlpConfig}.
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Jonatan Ivanov * @author Jonatan Ivanov
* @author Moritz Halbritter
*/ */
class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<OtlpProperties> implements OtlpConfig { class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<OtlpProperties> implements OtlpConfig {
/** OtlpPropertiesConfigAdapter(OtlpProperties properties) {
* 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) {
super(properties); super(properties);
this.connectionDetails = connectionDetails;
this.openTelemetryProperties = openTelemetryProperties;
this.environment = environment;
} }
@Override @Override
@ -64,7 +42,7 @@ class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<Ot
@Override @Override
public String url() { public String url() {
return get((properties) -> this.connectionDetails.getUrl(), OtlpConfig.super::url); return get(OtlpProperties::getUrl, OtlpConfig.super::url);
} }
@Override @Override
@ -73,17 +51,8 @@ class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<Ot
} }
@Override @Override
@SuppressWarnings("removal")
public Map<String, String> resourceAttributes() { public Map<String, String> resourceAttributes() {
Map<String, String> resourceAttributes = this.openTelemetryProperties.getResourceAttributes(); return get(OtlpProperties::getResourceAttributes, OtlpConfig.super::resourceAttributes);
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);
} }
@Override @Override
@ -91,9 +60,4 @@ class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<Ot
return get(OtlpProperties::getHeaders, OtlpConfig.super::headers); 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; 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 io.micrometer.wavefront.WavefrontConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PushRegistryPropertiesConfigAdapter; import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PushRegistryPropertiesConfigAdapter;
@ -85,9 +84,4 @@ public class WavefrontPropertiesConfigAdapter
return get(Export::isReportDayDistribution, WavefrontConfig.super::reportDayDistribution); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; 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.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; 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.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -57,12 +57,13 @@ import org.springframework.core.annotation.Order;
@ConditionalOnClass({ ResourceConfig.class, MetricsApplicationEventListener.class }) @ConditionalOnClass({ ResourceConfig.class, MetricsApplicationEventListener.class })
@ConditionalOnBean({ MeterRegistry.class, ResourceConfig.class }) @ConditionalOnBean({ MeterRegistry.class, ResourceConfig.class })
@EnableConfigurationProperties(MetricsProperties.class) @EnableConfigurationProperties(MetricsProperties.class)
@SuppressWarnings("removal")
public class JerseyServerMetricsAutoConfiguration { public class JerseyServerMetricsAutoConfiguration {
private final ObservationProperties observationProperties; private final MetricsProperties properties;
public JerseyServerMetricsAutoConfiguration(ObservationProperties observationProperties) { public JerseyServerMetricsAutoConfiguration(MetricsProperties properties) {
this.observationProperties = observationProperties; this.properties = properties;
} }
@Bean @Bean
@ -74,19 +75,19 @@ public class JerseyServerMetricsAutoConfiguration {
@Bean @Bean
public ResourceConfigCustomizer jerseyServerMetricsResourceConfigCustomizer(MeterRegistry meterRegistry, public ResourceConfigCustomizer jerseyServerMetricsResourceConfigCustomizer(MeterRegistry meterRegistry,
JerseyTagsProvider tagsProvider) { JerseyTagsProvider tagsProvider) {
String metricName = this.observationProperties.getHttp().getServer().getRequests().getName(); Server server = this.properties.getWeb().getServer();
return (config) -> config.register(new MetricsApplicationEventListener(meterRegistry, tagsProvider, metricName, return (config) -> config.register(new MetricsApplicationEventListener(meterRegistry, tagsProvider,
true, new AnnotationUtilsAnnotationFinder())); server.getRequest().getMetricName(), true, new AnnotationUtilsAnnotationFinder()));
} }
@Bean @Bean
@Order(0) @Order(0)
public MeterFilter jerseyMetricsUriTagFilter(MetricsProperties metricsProperties) { public MeterFilter jerseyMetricsUriTagFilter() {
String metricName = this.observationProperties.getHttp().getServer().getRequests().getName(); String metricName = this.properties.getWeb().getServer().getRequest().getMetricName();
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter( MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> String.format("Reached the maximum number of URI tags for '%s'.", metricName)); () -> String.format("Reached the maximum number of URI tags for '%s'.", metricName));
return MeterFilter.maximumAllowableTags(metricName, "uri", return MeterFilter.maximumAllowableTags(metricName, "uri", this.properties.getWeb().getServer().getMaxUriTags(),
metricsProperties.getWeb().getServer().getMaxUriTags(), filter); filter);
} }
/** /**

@ -27,11 +27,9 @@ import io.micrometer.observation.ObservationFilter;
import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationPredicate; import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import io.micrometer.tracing.Tracer; import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler; import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.handler.TracingObservationHandler; import io.micrometer.tracing.handler.TracingObservationHandler;
import org.aspectj.weaver.Advice;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; 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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API. * {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
@ -53,7 +50,6 @@ import org.springframework.core.annotation.Order;
* @author Moritz Halbritter * @author Moritz Halbritter
* @author Brian Clozel * @author Brian Clozel
* @author Jonatan Ivanov * @author Jonatan Ivanov
* @author Vedran Pavic
* @since 3.0.0 * @since 3.0.0
*/ */
@AutoConfiguration(after = { CompositeMeterRegistryAutoConfiguration.class, MicrometerTracingAutoConfiguration.class }) @AutoConfiguration(after = { CompositeMeterRegistryAutoConfiguration.class, MicrometerTracingAutoConfiguration.class })
@ -79,12 +75,6 @@ public class ObservationAutoConfiguration {
return ObservationRegistry.create(); return ObservationRegistry.create();
} }
@Bean
@Order(0)
PropertiesObservationFilterPredicate propertiesObservationFilter(ObservationProperties properties) {
return new PropertiesObservationFilterPredicate(properties);
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class) @ConditionalOnClass(MeterRegistry.class)
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer") @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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; package org.springframework.boot.actuate.autoconfigure.observation;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationHandler;
@ -31,7 +30,6 @@ import org.springframework.util.MultiValueMap;
* Groups {@link ObservationHandler ObservationHandlers} by type. * Groups {@link ObservationHandler ObservationHandlers} by type.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Moritz Halbritter
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
class ObservationHandlerGrouping { class ObservationHandlerGrouping {
@ -48,14 +46,13 @@ class ObservationHandlerGrouping {
void apply(List<ObservationHandler<?>> handlers, ObservationConfig config) { void apply(List<ObservationHandler<?>> handlers, ObservationConfig config) {
MultiValueMap<Class<? extends ObservationHandler>, ObservationHandler<?>> groupings = new LinkedMultiValueMap<>(); MultiValueMap<Class<? extends ObservationHandler>, ObservationHandler<?>> groupings = new LinkedMultiValueMap<>();
List<ObservationHandler<?>> handlersWithoutCategory = new ArrayList<>();
for (ObservationHandler<?> handler : handlers) { for (ObservationHandler<?> handler : handlers) {
Class<? extends ObservationHandler> category = findCategory(handler); Class<? extends ObservationHandler> category = findCategory(handler);
if (category != null) { if (category != null) {
groupings.add(category, handler); groupings.add(category, handler);
} }
else { else {
handlersWithoutCategory.add(handler); config.observationHandler(handler);
} }
} }
for (Class<? extends ObservationHandler> category : this.categories) { for (Class<? extends ObservationHandler> category : this.categories) {
@ -64,9 +61,6 @@ class ObservationHandlerGrouping {
config.observationHandler(new FirstMatchingCompositeObservationHandler(handlerGroup)); config.observationHandler(new FirstMatchingCompositeObservationHandler(handlerGroup));
} }
} }
for (ObservationHandler<?> observationHandler : handlersWithoutCategory) {
config.observationHandler(observationHandler);
}
} }
private Class<? extends ObservationHandler> findCategory(ObservationHandler<?> handler) { 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; package org.springframework.boot.actuate.autoconfigure.observation;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
@ -26,7 +23,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* observations. * observations.
* *
* @author Brian Clozel * @author Brian Clozel
* @author Moritz Halbritter
* @since 3.0.0 * @since 3.0.0
*/ */
@ConfigurationProperties("management.observations") @ConfigurationProperties("management.observations")
@ -34,37 +30,10 @@ public class ObservationProperties {
private final Http http = new Http(); 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() { public Http getHttp() {
return this.http; 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 { public static class Http {
private final Client client = new Client(); private final Client client = new Client();
@ -90,9 +59,10 @@ public class ObservationProperties {
public static class ClientRequests { 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() { public String getName() {
return this.name; return this.name;
@ -117,9 +87,10 @@ public class ObservationProperties {
public static class ServerRequests { 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() { public String getName() {
return this.name; 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) @AutoConfiguration(after = ObservationAutoConfiguration.class)
@ConditionalOnBean(ObservationRegistry.class) @ConditionalOnBean(ObservationRegistry.class)
@ConditionalOnClass({ GraphQL.class, GraphQlSource.class, Observation.class }) @ConditionalOnClass({ GraphQL.class, GraphQlSource.class, Observation.class })
@SuppressWarnings("removal")
public class GraphQlObservationAutoConfiguration { public class GraphQlObservationAutoConfiguration {
@Bean @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 @Bean
@Order(0) @Order(0)
@SuppressWarnings("removal")
MeterFilter metricsHttpClientUriTagFilter(ObservationProperties observationProperties, MeterFilter metricsHttpClientUriTagFilter(ObservationProperties observationProperties,
MetricsProperties metricsProperties) { MetricsProperties metricsProperties) {
Client clientProperties = metricsProperties.getWeb().getClient(); 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( MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter(
() -> "Reached the maximum number of URI tags for '%s'. Are you using 'uriVariables'?" () -> "Reached the maximum number of URI tags for '%s'. Are you using 'uriVariables'?"
.formatted(name)); .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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties; 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.ObservationRestTemplateCustomizer;
import org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTagsProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
@ -39,16 +40,39 @@ import org.springframework.web.client.RestTemplate;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class) @ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(RestTemplateBuilder.class) @ConditionalOnBean(RestTemplateBuilder.class)
@SuppressWarnings("removal")
class RestTemplateObservationConfiguration { class RestTemplateObservationConfiguration {
@Bean @Bean
ObservationRestTemplateCustomizer observationRestTemplateCustomizer(ObservationRegistry observationRegistry, ObservationRestTemplateCustomizer observationRestTemplateCustomizer(ObservationRegistry observationRegistry,
ObjectProvider<ClientRequestObservationConvention> customConvention, ObjectProvider<ClientRequestObservationConvention> customConvention,
ObservationProperties observationProperties, MetricsProperties metricsProperties) { ObservationProperties observationProperties, MetricsProperties metricsProperties,
String name = observationProperties.getHttp().getClient().getRequests().getName(); ObjectProvider<RestTemplateExchangeTagsProvider> optionalTagsProvider) {
ClientRequestObservationConvention observationConvention = customConvention String name = observationName(observationProperties, metricsProperties);
.getIfAvailable(() -> new DefaultClientRequestObservationConvention(name)); ClientRequestObservationConvention observationConvention = createConvention(customConvention.getIfAvailable(),
name, optionalTagsProvider.getIfAvailable());
return new ObservationRestTemplateCustomizer(observationRegistry, observationConvention); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties; 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.ObservationWebClientCustomizer;
import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -36,16 +37,39 @@ import org.springframework.web.reactive.function.client.WebClient;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebClient.class) @ConditionalOnClass(WebClient.class)
@SuppressWarnings("removal")
class WebClientObservationConfiguration { class WebClientObservationConfiguration {
@Bean @Bean
ObservationWebClientCustomizer observationWebClientCustomizer(ObservationRegistry observationRegistry, ObservationWebClientCustomizer observationWebClientCustomizer(ObservationRegistry observationRegistry,
ObjectProvider<ClientRequestObservationConvention> customConvention, ObjectProvider<ClientRequestObservationConvention> customConvention,
ObservationProperties observationProperties, MetricsProperties metricsProperties) { ObservationProperties observationProperties, ObjectProvider<WebClientExchangeTagsProvider> tagsProvider,
String name = observationProperties.getHttp().getClient().getRequests().getName(); MetricsProperties metricsProperties) {
ClientRequestObservationConvention observationConvention = customConvention String name = observationName(observationProperties, metricsProperties);
.getIfAvailable(() -> new DefaultClientRequestObservationConvention(name)); ClientRequestObservationConvention observationConvention = createConvention(customConvention.getIfAvailable(),
tagsProvider.getIfAvailable(), name);
return new ObservationWebClientCustomizer(observationRegistry, observationConvention); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; package org.springframework.boot.actuate.autoconfigure.observation.web.reactive;
import java.util.List;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter; import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.observation.Observation; import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry; 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.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; 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.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties; 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.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 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.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; 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.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 * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Spring
@ -44,22 +57,75 @@ import org.springframework.core.annotation.Order;
* @author Dmytro Nosan * @author Dmytro Nosan
* @since 3.0.0 * @since 3.0.0
*/ */
@AutoConfiguration(after = { SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class }) @AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
@ConditionalOnClass({ Observation.class, MeterRegistry.class }) SimpleMetricsExportAutoConfiguration.class, ObservationAutoConfiguration.class })
@ConditionalOnBean({ ObservationRegistry.class, MeterRegistry.class }) @ConditionalOnClass(Observation.class)
@ConditionalOnBean(ObservationRegistry.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class }) @EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class })
@SuppressWarnings("removal")
public class WebFluxObservationAutoConfiguration { public class WebFluxObservationAutoConfiguration {
private final MetricsProperties metricsProperties;
private final ObservationProperties observationProperties;
public WebFluxObservationAutoConfiguration(MetricsProperties metricsProperties,
ObservationProperties observationProperties) {
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 @Bean
@Order(0) @Order(0)
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties, MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties,
ObservationProperties observationProperties) { ObservationProperties observationProperties) {
String name = observationProperties.getHttp().getServer().getRequests().getName(); String observationName = observationProperties.getHttp().getServer().getRequests().getName();
String name = (observationName != null) ? observationName
: metricsProperties.getWeb().getServer().getRequest().getMetricName();
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter( MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> "Reached the maximum number of URI tags for '%s'.".formatted(name)); () -> "Reached the maximum number of URI tags for '%s'.".formatted(name));
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(), return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(),
filter); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; package org.springframework.boot.actuate.autoconfigure.observation.web.servlet;
import java.util.List;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter; import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.observation.Observation; 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.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties; 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.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -62,16 +66,28 @@ import org.springframework.web.servlet.DispatcherServlet;
@ConditionalOnClass({ DispatcherServlet.class, Observation.class }) @ConditionalOnClass({ DispatcherServlet.class, Observation.class })
@ConditionalOnBean(ObservationRegistry.class) @ConditionalOnBean(ObservationRegistry.class)
@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class }) @EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class })
@SuppressWarnings("removal")
public class WebMvcObservationAutoConfiguration { public class WebMvcObservationAutoConfiguration {
private final MetricsProperties metricsProperties;
private final ObservationProperties observationProperties;
public WebMvcObservationAutoConfiguration(ObservationProperties observationProperties,
MetricsProperties metricsProperties) {
this.observationProperties = observationProperties;
this.metricsProperties = metricsProperties;
}
@Bean @Bean
@ConditionalOnMissingFilterBean @ConditionalOnMissingFilterBean
public FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(ObservationRegistry registry, public FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(ObservationRegistry registry,
ObjectProvider<ServerRequestObservationConvention> customConvention, ObjectProvider<ServerRequestObservationConvention> customConvention,
ObservationProperties observationProperties) { ObjectProvider<WebMvcTagsProvider> customTagsProvider,
String name = observationProperties.getHttp().getServer().getRequests().getName(); ObjectProvider<WebMvcTagsContributor> contributorsProvider) {
ServerRequestObservationConvention convention = customConvention String name = httpRequestsMetricName(this.observationProperties, this.metricsProperties);
.getIfAvailable(() -> new DefaultServerRequestObservationConvention(name)); ServerRequestObservationConvention convention = createConvention(customConvention.getIfAvailable(), name,
customTagsProvider.getIfAvailable(), contributorsProvider.orderedStream().toList());
ServerHttpObservationFilter filter = new ServerHttpObservationFilter(registry, convention); ServerHttpObservationFilter filter = new ServerHttpObservationFilter(registry, convention);
FilterRegistrationBean<ServerHttpObservationFilter> registration = new FilterRegistrationBean<>(filter); FilterRegistrationBean<ServerHttpObservationFilter> registration = new FilterRegistrationBean<>(filter);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
@ -79,6 +95,27 @@ public class WebMvcObservationAutoConfiguration {
return registration; 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) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class) @ConditionalOnClass(MeterRegistry.class)
@ConditionalOnBean(MeterRegistry.class) @ConditionalOnBean(MeterRegistry.class)
@ -86,9 +123,9 @@ public class WebMvcObservationAutoConfiguration {
@Bean @Bean
@Order(0) @Order(0)
MeterFilter metricsHttpServerUriTagFilter(ObservationProperties observationProperties, MeterFilter metricsHttpServerUriTagFilter(MetricsProperties metricsProperties,
MetricsProperties metricsProperties) { ObservationProperties observationProperties) {
String name = observationProperties.getHttp().getServer().getRequests().getName(); String name = httpRequestsMetricName(observationProperties, metricsProperties);
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter( MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> String.format("Reached the maximum number of URI tags for '%s'.", name)); () -> String.format("Reached the maximum number of URI tags for '%s'.", name));
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getServer().getMaxUriTags(), 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) @AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@ConditionalOnClass({ Tracer.class, BraveTracer.class }) @ConditionalOnClass({ Tracer.class, BraveTracer.class })
@EnableConfigurationProperties(TracingProperties.class) @EnableConfigurationProperties(TracingProperties.class)
@ConditionalOnEnabledTracing
public class BraveAutoConfiguration { public class BraveAutoConfiguration {
private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager(); private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager();

@ -138,7 +138,7 @@ class CompositePropagationFactory extends Propagation.Factory {
* @return the B3 propagation factory * @return the B3 propagation factory
*/ */
private Propagation.Factory b3Single() { 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; package org.springframework.boot.actuate.autoconfigure.tracing;
import io.micrometer.tracing.Tracer; 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.DefaultTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler;
import io.micrometer.tracing.propagation.Propagator; 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.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; 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. * {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Tracing API.
* *
* @author Moritz Halbritter * @author Moritz Halbritter
* @author Jonatan Ivanov
* @since 3.0.0 * @since 3.0.0
*/ */
@AutoConfiguration @AutoConfiguration
@ConditionalOnClass(Tracer.class) @ConditionalOnClass(Tracer.class)
@ConditionalOnBean(Tracer.class) @ConditionalOnEnabledTracing
public class MicrometerTracingAutoConfiguration { public class MicrometerTracingAutoConfiguration {
/** /**
@ -71,6 +61,7 @@ public class MicrometerTracingAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ConditionalOnBean(Tracer.class)
@Order(DEFAULT_TRACING_OBSERVATION_HANDLER_ORDER) @Order(DEFAULT_TRACING_OBSERVATION_HANDLER_ORDER)
public DefaultTracingObservationHandler defaultTracingObservationHandler(Tracer tracer) { public DefaultTracingObservationHandler defaultTracingObservationHandler(Tracer tracer) {
return new DefaultTracingObservationHandler(tracer); return new DefaultTracingObservationHandler(tracer);
@ -78,7 +69,7 @@ public class MicrometerTracingAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ConditionalOnBean(Propagator.class) @ConditionalOnBean({ Tracer.class, Propagator.class })
@Order(SENDER_TRACING_OBSERVATION_HANDLER_ORDER) @Order(SENDER_TRACING_OBSERVATION_HANDLER_ORDER)
public PropagatingSenderTracingObservationHandler<?> propagatingSenderTracingObservationHandler(Tracer tracer, public PropagatingSenderTracingObservationHandler<?> propagatingSenderTracingObservationHandler(Tracer tracer,
Propagator propagator) { Propagator propagator) {
@ -87,39 +78,11 @@ public class MicrometerTracingAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ConditionalOnBean(Propagator.class) @ConditionalOnBean({ Tracer.class, Propagator.class })
@Order(RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER) @Order(RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER)
public PropagatingReceiverTracingObservationHandler<?> propagatingReceiverTracingObservationHandler(Tracer tracer, public PropagatingReceiverTracingObservationHandler<?> propagatingReceiverTracingObservationHandler(Tracer tracer,
Propagator propagator) { Propagator propagator) {
return new PropagatingReceiverTracingObservationHandler<>(tracer, 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.bridge.Slf4JEventListener;
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator; import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
import io.opentelemetry.api.OpenTelemetry; 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.api.trace.Tracer;
import io.opentelemetry.context.ContextStorage; import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; 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.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.SpringBootVersion; 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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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 Moritz Halbritter
* @author Marcin Grzejszczak * @author Marcin Grzejszczak
* @author Yanming Zhou * @author Yanming Zhou
* @since 3.0.0 * @since 3.0.0
*/ */
@AutoConfiguration(value = "openTelemetryTracingAutoConfiguration", before = MicrometerTracingAutoConfiguration.class) @AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@ConditionalOnEnabledTracing
@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class }) @ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class })
@EnableConfigurationProperties(TracingProperties.class) @EnableConfigurationProperties(TracingProperties.class)
public class OpenTelemetryAutoConfiguration { 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; private final TracingProperties tracingProperties;
OpenTelemetryAutoConfiguration(TracingProperties tracingProperties) { OpenTelemetryAutoConfiguration(TracingProperties tracingProperties) {
@ -82,10 +90,23 @@ public class OpenTelemetryAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
SdkTracerProvider otelSdkTracerProvider(Resource resource, SpanProcessors spanProcessors, Sampler sampler, OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) {
ObjectProvider<SdkTracerProviderBuilderCustomizer> customizers) { return OpenTelemetrySdk.builder()
SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler).setResource(resource); .setTracerProvider(sdkTracerProvider)
spanProcessors.forEach(builder::addSpanProcessor); .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)); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build(); return builder.build();
} }
@ -104,26 +125,14 @@ public class OpenTelemetryAutoConfiguration {
} }
@Bean @Bean
@ConditionalOnMissingBean SpanProcessor otelSpanProcessor(ObjectProvider<SpanExporter> spanExporters,
SpanProcessors spanProcessors(ObjectProvider<SpanProcessor> spanProcessors) {
return SpanProcessors.of(spanProcessors.orderedStream().toList());
}
@Bean
BatchSpanProcessor otelSpanProcessor(SpanExporters spanExporters,
ObjectProvider<SpanExportingPredicate> spanExportingPredicates, ObjectProvider<SpanReporter> spanReporters, ObjectProvider<SpanExportingPredicate> spanExportingPredicates, ObjectProvider<SpanReporter> spanReporters,
ObjectProvider<SpanFilter> spanFilters, ObjectProvider<MeterProvider> meterProvider) { ObjectProvider<SpanFilter> spanFilters) {
BatchSpanProcessorBuilder builder = BatchSpanProcessor return BatchSpanProcessor
.builder(new CompositeSpanExporter(spanExporters.list(), spanExportingPredicates.orderedStream().toList(), .builder(new CompositeSpanExporter(spanExporters.orderedStream().toList(),
spanReporters.orderedStream().toList(), spanFilters.orderedStream().toList())); spanExportingPredicates.orderedStream().toList(), spanReporters.orderedStream().toList(),
meterProvider.ifAvailable(builder::setMeterProvider); spanFilters.orderedStream().toList()))
return builder.build(); .build();
}
@Bean
@ConditionalOnMissingBean
SpanExporters spanExporters(ObjectProvider<SpanExporter> spanExporters) {
return SpanExporters.of(spanExporters.orderedStream().toList());
} }
@Bean @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; package org.springframework.boot.actuate.autoconfigure.tracing.otlp;
import java.util.Map.Entry;
import io.micrometer.tracing.otel.bridge.OtelTracer; import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; 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.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.trace.SdkTracerProvider; 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.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 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.EnableConfigurationProperties;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Bean;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for OTLP. Brave does not support * {@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. * define an {@link OtlpGrpcSpanExporter} and this auto-configuration will back off.
* *
* @author Jonatan Ivanov * @author Jonatan Ivanov
* @author Moritz Halbritter
* @author Eddú Meléndez
* @since 3.1.0 * @since 3.1.0
*/ */
@AutoConfiguration @AutoConfiguration
@ConditionalOnEnabledTracing
@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class, OtlpHttpSpanExporter.class }) @ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class, OtlpHttpSpanExporter.class })
@EnableConfigurationProperties(OtlpProperties.class) @EnableConfigurationProperties(OtlpProperties.class)
@Import({ OtlpTracingConfigurations.ConnectionDetails.class, OtlpTracingConfigurations.Exporters.class })
public class OtlpAutoConfiguration { 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. * 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 * 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration; 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.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -42,6 +43,7 @@ import org.springframework.util.function.SingletonSupplier;
after = MicrometerTracingAutoConfiguration.class) after = MicrometerTracingAutoConfiguration.class)
@ConditionalOnBean(Tracer.class) @ConditionalOnBean(Tracer.class)
@ConditionalOnClass({ Tracer.class, SpanContextSupplier.class }) @ConditionalOnClass({ Tracer.class, SpanContextSupplier.class })
@ConditionalOnEnabledTracing
public class PrometheusExemplarsAutoConfiguration { public class PrometheusExemplarsAutoConfiguration {
@Bean @Bean

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

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

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

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

@ -25,8 +25,6 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; 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.actuate.autoconfigure.metrics.export.properties.PushRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
@ -59,11 +57,6 @@ public class WavefrontProperties {
*/ */
private String apiToken; private String apiToken;
/**
* Type of the API token.
*/
private TokenType apiTokenType;
/** /**
* Application configuration. * Application configuration.
*/ */
@ -139,7 +132,7 @@ public class WavefrontProperties {
* @return the API token * @return the API token
*/ */
public String getApiTokenOrThrow() { 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, throw new InvalidConfigurationPropertyValueException("management.wavefront.api-token", null,
"This property is mandatory whenever publishing directly to the Wavefront API"); "This property is mandatory whenever publishing directly to the Wavefront API");
} }
@ -174,31 +167,6 @@ public class WavefrontProperties {
this.traceDerivedCustomTagKeys = traceDerivedCustomTagKeys; 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 { 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.WavefrontSender;
import com.wavefront.sdk.common.clients.WavefrontClient.Builder; 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.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.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.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize; import org.springframework.util.unit.DataSize;
@ -50,10 +46,8 @@ public class WavefrontSenderConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@Conditional(WavefrontTracingOrMetricsCondition.class)
public WavefrontSender wavefrontSender(WavefrontProperties properties) { public WavefrontSender wavefrontSender(WavefrontProperties properties) {
Builder builder = new Builder(properties.getEffectiveUri().toString(), properties.getWavefrontApiTokenType(), Builder builder = new Builder(properties.getEffectiveUri().toString(), properties.getApiTokenOrThrow());
properties.getApiTokenOrThrow());
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
WavefrontProperties.Sender sender = properties.getSender(); WavefrontProperties.Sender sender = properties.getSender();
map.from(sender.getMaxQueueSize()).to(builder::maxQueueSize); map.from(sender.getMaxQueueSize()).to(builder::maxQueueSize);
@ -63,22 +57,4 @@ public class WavefrontSenderConfiguration {
return builder.build(); 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(); Environment parentEnvironment = parentContext.getEnvironment();
ConfigurableEnvironment childEnvironment = ApplicationContextFactory.DEFAULT ConfigurableEnvironment childEnvironment = ApplicationContextFactory.DEFAULT
.createEnvironment(this.webApplicationType); .createEnvironment(this.webApplicationType);
if (parentEnvironment instanceof ConfigurableEnvironment configurableEnvironment) { if (parentEnvironment instanceof ConfigurableEnvironment) {
childEnvironment.setConversionService((configurableEnvironment).getConversionService()); childEnvironment.setConversionService(((ConfigurableEnvironment) parentEnvironment).getConversionService());
} }
ConfigurableApplicationContext managementContext = ApplicationContextFactory.DEFAULT ConfigurableApplicationContext managementContext = ApplicationContextFactory.DEFAULT
.create(this.webApplicationType); .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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.actuate.autoconfigure.web.server.ManagementWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; 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.JettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.NettyWebServerFactoryCustomizer; 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.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer;
@ -78,10 +76,8 @@ public class ReactiveManagementChildContextConfiguration {
ReactiveManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) { ReactiveManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
super(beanFactory, ReactiveWebServerFactoryCustomizer.class, TomcatWebServerFactoryCustomizer.class, super(beanFactory, ReactiveWebServerFactoryCustomizer.class, TomcatWebServerFactoryCustomizer.class,
TomcatReactiveWebServerFactoryCustomizer.class, TomcatReactiveWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class,
TomcatVirtualThreadsWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class, NettyWebServerFactoryCustomizer.class);
JettyVirtualThreadsWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class,
NettyWebServerFactoryCustomizer.class);
} }
} }

@ -217,8 +217,8 @@ class ChildManagementContextInitializer
} }
static void addIfPossible(ApplicationContext parentContext, ConfigurableApplicationContext childContext) { static void addIfPossible(ApplicationContext parentContext, ConfigurableApplicationContext childContext) {
if (parentContext instanceof ConfigurableApplicationContext configurableApplicationContext) { if (parentContext instanceof ConfigurableApplicationContext) {
add(configurableApplicationContext, childContext); 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.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.ServerProperties; 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.JettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer;
@ -124,8 +122,7 @@ class ServletManagementChildContextConfiguration {
ServletManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) { ServletManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
super(beanFactory, ServletWebServerFactoryCustomizer.class, TomcatServletWebServerFactoryCustomizer.class, super(beanFactory, ServletWebServerFactoryCustomizer.class, TomcatServletWebServerFactoryCustomizer.class,
TomcatWebServerFactoryCustomizer.class, TomcatVirtualThreadsWebServerFactoryCustomizer.class, TomcatWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class,
JettyWebServerFactoryCustomizer.class, JettyVirtualThreadsWebServerFactoryCustomizer.class,
UndertowServletWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class); UndertowServletWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class);
} }

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

@ -1,7 +1,3 @@
# Failure Analyzers # Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.actuate.autoconfigure.metrics.ValidationFailureAnalyzer 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.KafkaMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.Log4J2MetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.Log4J2MetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.LogbackMetricsAutoConfiguration 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.MetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.SystemMetricsAutoConfiguration 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.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.batch.BatchObservationAutoConfiguration org.springframework.boot.actuate.autoconfigure.observation.batch.BatchObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.graphql.GraphQlObservationAutoConfiguration 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.integration.IntegrationMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.jersey.JerseyServerMetricsAutoConfiguration 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.neo4j.Neo4jHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration 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.quartz.QuartzEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.r2dbc.ConnectionFactoryHealthContributorAutoConfiguration 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.RedisHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.data.redis.RedisReactiveHealthContributorAutoConfiguration org.springframework.boot.actuate.autoconfigure.data.redis.RedisReactiveHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksEndpointAutoConfiguration 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.reactive.ReactiveManagementWebSecurityAutoConfiguration
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration

@ -35,13 +35,10 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; 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.WebFluxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -55,14 +52,15 @@ class CloudFoundryReactiveHealthEndpointWebExtensionTests {
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner() private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withPropertyValues("VCAP_APPLICATION={}") .withPropertyValues("VCAP_APPLICATION={}")
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class, .withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
WebFluxAutoConfiguration.class, JacksonAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class, WebFluxAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
ReactiveCloudFoundryActuatorAutoConfigurationTests.WebClientCustomizerConfig.class, ReactiveCloudFoundryActuatorAutoConfigurationTests.WebClientCustomizerConfig.class,
WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class, WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class, HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
ReactiveCloudFoundryActuatorAutoConfiguration.class)) ReactiveCloudFoundryActuatorAutoConfiguration.class))
.withUserConfiguration(TestHealthIndicator.class, UserDetailsServiceConfiguration.class); .withUserConfiguration(TestHealthIndicator.class);
@Test @Test
void healthComponentsAlwaysPresent() { 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.info.ProjectInfoAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; 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.WebFluxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; 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.http.HttpMethod;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange; 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.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainProxy; import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
@ -85,16 +84,15 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
private static final String V3_JSON = ApiVersion.V3.getProducedMimeType().toString(); private static final String V3_JSON = ApiVersion.V3.getProducedMimeType().toString();
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner() private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration( .withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class, WebFluxAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class, WebFluxAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebClientCustomizerConfig.class, PropertyPlaceholderAutoConfiguration.class, WebClientCustomizerConfig.class,
WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class, WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class, HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class, InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class)) ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class));
.withUserConfiguration(UserDetailsServiceConfiguration.class);
private static final String BASE_PATH = "/cloudfoundryapplication"; 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 MockWebServer server;
private WebClient.Builder builder;
@BeforeEach @BeforeEach
void setup() { void setup() {
this.server = new MockWebServer(); this.server = new MockWebServer();
WebClient.Builder builder = WebClient.builder().baseUrl(this.server.url("/").toString()); this.builder = WebClient.builder().baseUrl(this.server.url("/").toString());
this.securityService = new ReactiveCloudFoundrySecurityService(builder, CLOUD_CONTROLLER, false); this.securityService = new ReactiveCloudFoundrySecurityService(this.builder, CLOUD_CONTROLLER, false);
} }
@AfterEach @AfterEach
@ -181,7 +183,7 @@ class ReactiveCloudFoundrySecurityServiceTests {
response.setHeader("Content-Type", "application/json"); response.setHeader("Content-Type", "application/json");
}); });
StepVerifier.create(this.securityService.fetchTokenKeys()) StepVerifier.create(this.securityService.fetchTokenKeys())
.consumeNextWith((tokenKeys) -> assertThat(tokenKeys).isEmpty()) .consumeNextWith((tokenKeys) -> assertThat(tokenKeys).hasSize(0))
.expectComplete() .expectComplete()
.verify(); .verify();
expectRequest((request) -> assertThat(request.getPath()).isEqualTo("/my-cloud-controller.com/info")); 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 * @author Eddú Meléndez
*/ */
@SuppressWarnings("removal")
@Deprecated(since = "3.2.0", forRemoval = true)
class InfluxDbHealthContributorAutoConfigurationTests { class InfluxDbHealthContributorAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() 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.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootConfiguration; 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.HealthEndpointWebExtension;
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 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.context.annotation.UserConfigurations;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -60,7 +57,6 @@ class WebEndpointsAutoConfigurationIntegrationTests {
} }
@Test @Test
@ClassPathExclusions({ "spring-security-oauth2-client-*.jar", "spring-security-oauth2-resource-server-*.jar" })
void healthEndpointReactiveWebExtensionIsAutoConfigured() { void healthEndpointReactiveWebExtensionIsAutoConfigured() {
reactiveWebRunner() reactiveWebRunner()
.run((context) -> assertThat(context).hasSingleBean(ReactiveHealthEndpointWebExtension.class)); .run((context) -> assertThat(context).hasSingleBean(ReactiveHealthEndpointWebExtension.class));
@ -84,8 +80,7 @@ class WebEndpointsAutoConfigurationIntegrationTests {
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class, MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class, HazelcastAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class, HazelcastAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class, RedisAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class, RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class, BraveAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class })
OpenTelemetryAutoConfiguration.class })
@SpringBootConfiguration @SpringBootConfiguration
static class WebEndpointTestApplication { static class WebEndpointTestApplication {

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

Loading…
Cancel
Save