Remove APIs deprecated for removal in 3.5

Closes gh-43788
This commit is contained in:
Andy Wilkinson 2025-01-14 12:34:43 +00:00
parent f301b2a123
commit 2f29a49a1d
61 changed files with 29 additions and 3522 deletions

View File

@ -49,7 +49,6 @@ dependencies {
optional("io.micrometer:micrometer-registry-new-relic")
optional("io.micrometer:micrometer-registry-otlp")
optional("io.micrometer:micrometer-registry-prometheus")
optional("io.micrometer:micrometer-registry-prometheus-simpleclient")
optional("io.micrometer:micrometer-registry-stackdriver") {
exclude group: "commons-logging", module: "commons-logging"
exclude group: "javax.annotation", module: "javax.annotation-api"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -78,10 +78,8 @@ public class PrometheusMetricsExportAutoConfiguration {
@ConditionalOnAvailableEndpoint(PrometheusScrapeEndpoint.class)
static class PrometheusScrapeEndpointConfiguration {
@SuppressWarnings("removal")
@Bean
@ConditionalOnMissingBean({ PrometheusScrapeEndpoint.class,
org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint.class })
@ConditionalOnMissingBean
PrometheusScrapeEndpoint prometheusEndpoint(PrometheusRegistry prometheusRegistry,
PrometheusConfig prometheusConfig) {
return new PrometheusScrapeEndpoint(prometheusRegistry, prometheusConfig.prometheusProperties());

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,7 +22,6 @@ import java.util.Map;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for configuring metrics export
@ -52,13 +51,6 @@ public class PrometheusProperties {
*/
private final Pushgateway pushgateway = new Pushgateway();
/**
* Histogram type for backing DistributionSummary and Timer.
* @deprecated since 3.3.0 for removal in 3.5.0
*/
@Deprecated(since = "3.3.0", forRemoval = true)
private HistogramFlavor histogramFlavor = HistogramFlavor.Prometheus;
/**
* Additional properties to pass to the Prometheus client.
*/
@ -77,17 +69,6 @@ public class PrometheusProperties {
this.descriptions = descriptions;
}
@Deprecated(since = "3.3.0", forRemoval = true)
@DeprecatedConfigurationProperty(since = "3.3.0",
reason = "No longer supported. Works only when using the Prometheus simpleclient.")
public HistogramFlavor getHistogramFlavor() {
return this.histogramFlavor;
}
public void setHistogramFlavor(HistogramFlavor histogramFlavor) {
this.histogramFlavor = histogramFlavor;
}
public Duration getStep() {
return this.step;
}
@ -223,16 +204,4 @@ public class PrometheusProperties {
}
/**
* Prometheus Histogram flavor.
*
* @deprecated since 3.3.0 for removal in 3.5.0
*/
@Deprecated(since = "3.3.0", forRemoval = true)
public enum HistogramFlavor {
Prometheus, VictoriaMetrics
}
}

View File

@ -1,162 +0,0 @@
/*
* Copyright 2012-2025 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.prometheus;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Map;
import io.micrometer.core.instrument.Clock;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exemplars.DefaultExemplarSampler;
import io.prometheus.client.exemplars.ExemplarSampler;
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
import io.prometheus.client.exporter.PushGateway;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint;
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.ConditionalOnBooleanProperty;
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.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Prometheus
* with the Prometheus simpleclient.
*
* @author Jon Schneider
* @author David J. M. Karlsen
* @author Jonatan Ivanov
* @since 2.0.0
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of
* {@link PrometheusMetricsExportAutoConfiguration}
*/
@Deprecated(since = "3.3.0", forRemoval = true)
@AutoConfiguration(
before = { CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class },
after = { MetricsAutoConfiguration.class, PrometheusMetricsExportAutoConfiguration.class })
@ConditionalOnBean(Clock.class)
@ConditionalOnClass(io.micrometer.prometheus.PrometheusMeterRegistry.class)
@ConditionalOnEnabledMetricsExport("prometheus")
@EnableConfigurationProperties(PrometheusProperties.class)
public class PrometheusSimpleclientMetricsExportAutoConfiguration {
@Bean
@ConditionalOnMissingBean
io.micrometer.prometheus.PrometheusConfig simpleclientPrometheusConfig(PrometheusProperties prometheusProperties) {
return new PrometheusSimpleclientPropertiesConfigAdapter(prometheusProperties);
}
@Bean
@ConditionalOnMissingBean
io.micrometer.prometheus.PrometheusMeterRegistry simpleclientPrometheusMeterRegistry(
io.micrometer.prometheus.PrometheusConfig prometheusConfig, CollectorRegistry collectorRegistry,
Clock clock, ObjectProvider<ExemplarSampler> exemplarSamplerProvider) {
return new io.micrometer.prometheus.PrometheusMeterRegistry(prometheusConfig, collectorRegistry, clock,
exemplarSamplerProvider.getIfAvailable());
}
@Bean
@ConditionalOnMissingBean
CollectorRegistry collectorRegistry() {
return new CollectorRegistry(true);
}
@Bean
@ConditionalOnMissingBean(ExemplarSampler.class)
@ConditionalOnBean(SpanContextSupplier.class)
DefaultExemplarSampler exemplarSampler(SpanContextSupplier spanContextSupplier) {
return new DefaultExemplarSampler(spanContextSupplier);
}
@SuppressWarnings("removal")
@Configuration(proxyBeanMethods = false)
@ConditionalOnAvailableEndpoint(PrometheusSimpleclientScrapeEndpoint.class)
static class PrometheusScrapeEndpointConfiguration {
@Bean
@ConditionalOnMissingBean({ PrometheusSimpleclientScrapeEndpoint.class, PrometheusScrapeEndpoint.class })
PrometheusSimpleclientScrapeEndpoint prometheusEndpoint(CollectorRegistry collectorRegistry) {
return new PrometheusSimpleclientScrapeEndpoint(collectorRegistry);
}
}
/**
* Configuration for <a href="https://github.com/prometheus/pushgateway">Prometheus
* Pushgateway</a>.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PushGateway.class)
@ConditionalOnBooleanProperty("management.prometheus.metrics.export.pushgateway.enabled")
static class PrometheusPushGatewayConfiguration {
/**
* The fallback job name. We use 'spring' since there's a history of Prometheus
* spring integration defaulting to that name from when Prometheus integration
* didn't exist in Spring itself.
*/
private static final String FALLBACK_JOB = "spring";
@Bean
@ConditionalOnMissingBean
PrometheusPushGatewayManager prometheusPushGatewayManager(CollectorRegistry collectorRegistry,
PrometheusProperties prometheusProperties, Environment environment) throws MalformedURLException {
PrometheusProperties.Pushgateway properties = prometheusProperties.getPushgateway();
Duration pushRate = properties.getPushRate();
String job = getJob(properties, environment);
Map<String, String> groupingKey = properties.getGroupingKey();
ShutdownOperation shutdownOperation = properties.getShutdownOperation();
PushGateway pushGateway = initializePushGateway(properties.getBaseUrl());
if (StringUtils.hasText(properties.getUsername())) {
pushGateway.setConnectionFactory(
new BasicAuthHttpConnectionFactory(properties.getUsername(), properties.getPassword()));
}
return new PrometheusPushGatewayManager(pushGateway, collectorRegistry, pushRate, job, groupingKey,
shutdownOperation);
}
private PushGateway initializePushGateway(String url) throws MalformedURLException {
return new PushGateway(new URL(url));
}
private String getJob(PrometheusProperties.Pushgateway properties, Environment environment) {
String job = properties.getJob();
job = (job != null) ? job : environment.getProperty("spring.application.name");
return (job != null) ? job : FALLBACK_JOB;
}
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright 2012-2024 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.prometheus;
import java.time.Duration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PropertiesConfigAdapter;
/**
* Adapter to convert {@link PrometheusProperties} to a
* {@link io.micrometer.prometheus.PrometheusConfig}.
*
* @author Jon Schneider
* @author Phillip Webb
*/
@SuppressWarnings({ "deprecation", "removal" })
class PrometheusSimpleclientPropertiesConfigAdapter extends PropertiesConfigAdapter<PrometheusProperties>
implements io.micrometer.prometheus.PrometheusConfig {
PrometheusSimpleclientPropertiesConfigAdapter(PrometheusProperties properties) {
super(properties);
}
@Override
public String prefix() {
return "management.prometheus.metrics.export";
}
@Override
public String get(String key) {
return null;
}
@Override
public boolean descriptions() {
return get(PrometheusProperties::isDescriptions, io.micrometer.prometheus.PrometheusConfig.super::descriptions);
}
@Override
public io.micrometer.prometheus.HistogramFlavor histogramFlavor() {
return get(PrometheusSimpleclientPropertiesConfigAdapter::mapToMicrometerHistogramFlavor,
io.micrometer.prometheus.PrometheusConfig.super::histogramFlavor);
}
static io.micrometer.prometheus.HistogramFlavor mapToMicrometerHistogramFlavor(PrometheusProperties properties) {
return switch (properties.getHistogramFlavor()) {
case Prometheus -> io.micrometer.prometheus.HistogramFlavor.Prometheus;
case VictoriaMetrics -> io.micrometer.prometheus.HistogramFlavor.VictoriaMetrics;
};
}
@Override
public Duration step() {
return get(PrometheusProperties::getStep, io.micrometer.prometheus.PrometheusConfig.super::step);
}
}

View File

@ -1,97 +0,0 @@
/*
* Copyright 2012-2024 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.prometheus;
import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusSimpleclientMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.util.function.SingletonSupplier;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Prometheus Exemplars with
* Micrometer Tracing.
*
* @author Jonatan Ivanov
* @since 3.0.0
* @deprecated since 3.3.0 for removal in 3.5.0
*/
@SuppressWarnings("removal")
@Deprecated(forRemoval = true, since = "3.3.0")
@AutoConfiguration(before = PrometheusSimpleclientMetricsExportAutoConfiguration.class,
after = MicrometerTracingAutoConfiguration.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnClass({ Tracer.class, SpanContextSupplier.class })
public class PrometheusSimpleclientExemplarsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
SpanContextSupplier spanContextSupplier(ObjectProvider<Tracer> tracerProvider) {
return new LazyTracingSpanContextSupplier(tracerProvider);
}
/**
* Since the MeterRegistry can depend on the {@link Tracer} (Exemplars) and the
* {@link Tracer} can depend on the MeterRegistry (recording metrics), this
* {@link SpanContextSupplier} breaks the cycle by lazily loading the {@link Tracer}.
*/
static class LazyTracingSpanContextSupplier implements SpanContextSupplier {
private final SingletonSupplier<Tracer> tracer;
LazyTracingSpanContextSupplier(ObjectProvider<Tracer> tracerProvider) {
this.tracer = SingletonSupplier.of(tracerProvider::getObject);
}
@Override
public String getTraceId() {
Span currentSpan = currentSpan();
return (currentSpan != null) ? currentSpan.context().traceId() : null;
}
@Override
public String getSpanId() {
Span currentSpan = currentSpan();
return (currentSpan != null) ? currentSpan.context().spanId() : null;
}
@Override
public boolean isSampled() {
Span currentSpan = currentSpan();
if (currentSpan == null) {
return false;
}
Boolean sampled = currentSpan.context().sampled();
return sampled != null && sampled;
}
private Span currentSpan() {
return this.tracer.obtain().currentSpan();
}
}
}

View File

@ -40,12 +40,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Configurations for Zipkin. Those are imported by {@link ZipkinAutoConfiguration}.
@ -57,8 +54,7 @@ import org.springframework.web.reactive.function.client.WebClient;
class ZipkinConfigurations {
@Configuration(proxyBeanMethods = false)
@Import({ HttpClientSenderConfiguration.class, WebClientSenderConfiguration.class,
RestTemplateSenderConfiguration.class, UrlConnectionSenderConfiguration.class })
@Import({ HttpClientSenderConfiguration.class, UrlConnectionSenderConfiguration.class })
static class SenderConfiguration {
}
@ -86,68 +82,6 @@ class ZipkinConfigurations {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebClient.class)
@EnableConfigurationProperties(ZipkinProperties.class)
static class WebClientSenderConfiguration {
@Bean
@ConditionalOnMissingBean(BytesMessageSender.class)
@SuppressWarnings({ "deprecation", "removal" })
ZipkinWebClientSender webClientSender(ZipkinProperties properties, Encoding encoding,
ObjectProvider<ZipkinWebClientBuilderCustomizer> customizers,
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) {
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider
.getIfAvailable(HttpEndpointSuppliers::constantFactory);
WebClient.Builder builder = WebClient.builder();
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return new ZipkinWebClientSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(),
builder.build(), properties.getConnectTimeout().plus(properties.getReadTimeout()));
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@EnableConfigurationProperties(ZipkinProperties.class)
static class RestTemplateSenderConfiguration {
@Bean
@ConditionalOnMissingBean(BytesMessageSender.class)
@SuppressWarnings({ "deprecation", "removal" })
ZipkinRestTemplateSender restTemplateSender(ZipkinProperties properties, Encoding encoding,
ObjectProvider<ZipkinRestTemplateBuilderCustomizer> customizers,
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) {
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider
.getIfAvailable(HttpEndpointSuppliers::constantFactory);
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder()
.setConnectTimeout(properties.getConnectTimeout())
.setReadTimeout(properties.getReadTimeout());
restTemplateBuilder = applyCustomizers(restTemplateBuilder, customizers);
return new ZipkinRestTemplateSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(),
restTemplateBuilder.build());
}
@SuppressWarnings({ "deprecation", "removal" })
private RestTemplateBuilder applyCustomizers(RestTemplateBuilder restTemplateBuilder,
ObjectProvider<ZipkinRestTemplateBuilderCustomizer> customizers) {
Iterable<ZipkinRestTemplateBuilderCustomizer> orderedCustomizers = () -> customizers.orderedStream()
.iterator();
RestTemplateBuilder currentBuilder = restTemplateBuilder;
for (ZipkinRestTemplateBuilderCustomizer customizer : orderedCustomizers) {
currentBuilder = customizer.customize(currentBuilder);
}
return currentBuilder;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(URLConnectionSender.class)
@EnableConfigurationProperties(ZipkinProperties.class)

View File

@ -1,41 +0,0 @@
/*
* Copyright 2012-2024 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.zipkin;
import org.springframework.boot.web.client.RestTemplateBuilder;
/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link RestTemplateBuilder} used to send spans to Zipkin.
*
* @author Marcin Grzejszczak
* @since 3.0.0
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of
* {@link ZipkinHttpClientBuilderCustomizer}
*/
@FunctionalInterface
@Deprecated(since = "3.3.0", forRemoval = true)
public interface ZipkinRestTemplateBuilderCustomizer {
/**
* Customize the rest template builder.
* @param restTemplateBuilder the {@code RestTemplateBuilder} to customize
* @return the customized {@code RestTemplateBuilder}
*/
RestTemplateBuilder customize(RestTemplateBuilder restTemplateBuilder);
}

View File

@ -1,53 +0,0 @@
/*
* Copyright 2012-2024 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.zipkin;
import java.net.URI;
import zipkin2.reporter.Encoding;
import zipkin2.reporter.HttpEndpointSupplier.Factory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
/**
* An {@link HttpSender} which uses {@link RestTemplate} for HTTP communication.
*
* @author Moritz Halbritter
* @author Stefan Bratanov
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of {@link ZipkinHttpClientSender}
*/
@Deprecated(since = "3.3.0", forRemoval = true)
class ZipkinRestTemplateSender extends HttpSender {
private final RestTemplate restTemplate;
ZipkinRestTemplateSender(Encoding encoding, Factory endpointSupplierFactory, String endpoint,
RestTemplate restTemplate) {
super(encoding, endpointSupplierFactory, endpoint);
this.restTemplate = restTemplate;
}
@Override
void postSpans(URI endpoint, MultiValueMap<String, String> headers, byte[] body) {
HttpEntity<byte[]> request = new HttpEntity<>(body, headers);
this.restTemplate.exchange(endpoint, HttpMethod.POST, request, Void.class);
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2012-2024 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.zipkin;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.Builder;
/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link Builder} used to send spans to Zipkin.
*
* @author Marcin Grzejszczak
* @since 3.0.0
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of
* {@link ZipkinHttpClientBuilderCustomizer}
*/
@FunctionalInterface
@Deprecated(since = "3.3.0", forRemoval = true)
public interface ZipkinWebClientBuilderCustomizer {
/**
* Customize the web client builder.
* @param webClientBuilder the {@code WebClient.Builder} to customize
*/
void customize(WebClient.Builder webClientBuilder);
}

View File

@ -1,61 +0,0 @@
/*
* Copyright 2012-2024 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.zipkin;
import java.net.URI;
import java.time.Duration;
import zipkin2.reporter.Encoding;
import zipkin2.reporter.HttpEndpointSupplier.Factory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
/**
* An {@link HttpSender} which uses {@link WebClient} for HTTP communication.
*
* @author Stefan Bratanov
* @author Moritz Halbritter
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of {@link ZipkinHttpClientSender}
*/
@Deprecated(since = "3.3.0", forRemoval = true)
class ZipkinWebClientSender extends HttpSender {
private final WebClient webClient;
private final Duration timeout;
ZipkinWebClientSender(Encoding encoding, Factory endpointSupplierFactory, String endpoint, WebClient webClient,
Duration timeout) {
super(encoding, endpointSupplierFactory, endpoint);
this.webClient = webClient;
this.timeout = timeout;
}
@Override
void postSpans(URI endpoint, MultiValueMap<String, String> headers, byte[] body) {
this.webClient.post()
.uri(endpoint)
.headers((h) -> h.addAll(headers))
.bodyValue(body)
.retrieve()
.toBodilessEntity()
.timeout(this.timeout)
.block();
}
}

View File

@ -65,7 +65,6 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.kairos.KairosMetri
org.springframework.boot.actuate.autoconfigure.metrics.export.newrelic.NewRelicMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusSimpleclientMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.export.signalfx.SignalFxMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver.StackdriverMetricsExportAutoConfiguration
@ -112,7 +111,6 @@ org.springframework.boot.actuate.autoconfigure.tracing.NoopTracerAutoConfigurati
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusExemplarsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusSimpleclientExemplarsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.wavefront.WavefrontTracingAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration
org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontAutoConfiguration

View File

@ -1,90 +0,0 @@
/*
* Copyright 2012-2024 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.endpoint.web.documentation;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.common.TextFormat;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
/**
* Tests for generating documentation describing the
* {@link org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint}.
*
* @author Andy Wilkinson
* @author Johnny Lim
*/
class PrometheusSimpleclientScrapeEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
@Test
void prometheus() {
assertThat(this.mvc.get().uri("/actuator/prometheus")).hasStatusOk()
.apply(document("prometheus-simpleclient/all"));
}
@Test
void prometheusOpenmetrics() {
assertThat(this.mvc.get().uri("/actuator/prometheus").accept(TextFormat.CONTENT_TYPE_OPENMETRICS_100))
.satisfies((result) -> {
assertThat(result).hasStatusOk()
.headers()
.hasValue("Content-Type", "application/openmetrics-text;version=1.0.0;charset=utf-8");
assertThat(result).apply(document("prometheus-simpleclient/openmetrics"));
});
}
@Test
void filteredPrometheus() {
assertThat(this.mvc.get()
.uri("/actuator/prometheus")
.param("includedNames", "jvm_memory_used_bytes,jvm_memory_committed_bytes"))
.hasStatusOk()
.apply(document("prometheus-simpleclient/names",
queryParameters(parameterWithName("includedNames")
.description("Restricts the samples to those that match the names. Optional.")
.optional())));
}
@Configuration(proxyBeanMethods = false)
@Import(BaseDocumentationConfiguration.class)
static class TestConfiguration {
@Bean
@SuppressWarnings({ "removal", "deprecation" })
org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint endpoint() {
CollectorRegistry collectorRegistry = new CollectorRegistry(true);
io.micrometer.prometheus.PrometheusMeterRegistry meterRegistry = new io.micrometer.prometheus.PrometheusMeterRegistry(
(key) -> null, collectorRegistry, Clock.SYSTEM);
new JvmMemoryMetrics().bindTo(meterRegistry);
return new org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint(
collectorRegistry);
}
}
}

View File

@ -1,411 +0,0 @@
/*
* Copyright 2012-2024 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.prometheus;
import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheusmetrics.PrometheusConfig;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exemplars.ExemplarSampler;
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
import io.prometheus.client.exporter.DefaultHttpConnectionFactory;
import io.prometheus.client.exporter.HttpConnectionFactory;
import io.prometheus.client.exporter.PushGateway;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
import org.assertj.core.api.ThrowingConsumer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.DualPrometheusMetricsExportAutoConfigurationTests.CustomSecondEndpointConfiguration.SecondPrometheusScrapeEndpoint;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link PrometheusSimpleclientMetricsExportAutoConfiguration} and
* {@link PrometheusMetricsExportAutoConfiguration} with both Prometheus clients on the
* classpath.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Jonatan Ivanov
*/
@SuppressWarnings({ "removal", "deprecation" })
@ExtendWith(OutputCaptureExtension.class)
class DualPrometheusMetricsExportAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PrometheusSimpleclientMetricsExportAutoConfiguration.class,
PrometheusMetricsExportAutoConfiguration.class));
@Test
void backsOffWithoutAClock() {
this.contextRunner.run((context) -> assertThat(context)
.doesNotHaveBean(io.micrometer.prometheusmetrics.PrometheusMeterRegistry.class)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusMeterRegistry.class));
}
@Test
void autoConfiguresItsConfigPrometheusRegistryAndMeterRegistry() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class)
.hasSingleBean(io.micrometer.prometheusmetrics.PrometheusMeterRegistry.class)
.hasSingleBean(PrometheusRegistry.class)
.hasSingleBean(io.micrometer.prometheusmetrics.PrometheusConfig.class));
}
@Test
void autoConfigurationCanBeDisabledWithDefaultsEnabledProperty() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.defaults.metrics.export.enabled=false")
.run((context) -> assertThat(context)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.doesNotHaveBean(CollectorRegistry.class)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusConfig.class)
.doesNotHaveBean(io.micrometer.prometheusmetrics.PrometheusMeterRegistry.class)
.doesNotHaveBean(PrometheusRegistry.class)
.doesNotHaveBean(io.micrometer.prometheusmetrics.PrometheusConfig.class));
}
@Test
void autoConfigurationCanBeDisabledWithSpecificEnabledProperty() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.prometheus.metrics.export.enabled=false")
.run((context) -> assertThat(context)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.doesNotHaveBean(CollectorRegistry.class)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusConfig.class)
.doesNotHaveBean(io.micrometer.prometheusmetrics.PrometheusMeterRegistry.class)
.doesNotHaveBean(PrometheusRegistry.class)
.doesNotHaveBean(io.micrometer.prometheusmetrics.PrometheusConfig.class));
}
@Test
void allowsCustomConfigToBeUsed() {
this.contextRunner.withUserConfiguration(CustomConfigConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class)
.hasBean("customConfig")
.hasSingleBean(PrometheusRegistry.class)
.hasSingleBean(io.micrometer.prometheusmetrics.PrometheusConfig.class)
.hasBean("otherCustomConfig"));
}
@Test
void allowsCustomRegistryToBeUsed() {
this.contextRunner.withUserConfiguration(CustomRegistryConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasBean("customRegistry")
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class)
.hasSingleBean(PrometheusRegistry.class)
.hasSingleBean(io.micrometer.prometheusmetrics.PrometheusConfig.class)
.hasBean("otherCustomRegistry"));
}
@Test
void allowsCustomCollectorRegistryToBeUsed() {
this.contextRunner.withUserConfiguration(CustomCollectorRegistryConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasBean("customCollectorRegistry")
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class)
.hasBean("customPrometheusRegistry")
.hasSingleBean(PrometheusRegistry.class)
.hasSingleBean(io.micrometer.prometheusmetrics.PrometheusConfig.class));
}
@Test
void autoConfiguresExemplarSamplerIfSpanContextSupplierIsPresent() {
this.contextRunner.withUserConfiguration(ExemplarsConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(SpanContextSupplier.class)
.hasSingleBean(ExemplarSampler.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class));
}
@Test
void allowsCustomExemplarSamplerToBeUsed() {
this.contextRunner.withUserConfiguration(ExemplarsConfiguration.class)
.withBean("customExemplarSampler", ExemplarSampler.class, () -> mock(ExemplarSampler.class))
.run((context) -> assertThat(context).hasSingleBean(ExemplarSampler.class)
.getBean(ExemplarSampler.class)
.isSameAs(context.getBean("customExemplarSampler")));
}
@Test
void exemplarSamplerIsNotAutoConfiguredIfSpanContextSupplierIsMissing() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(SpanContextSupplier.class)
.doesNotHaveBean(ExemplarSampler.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class));
}
@Test
void addsScrapeEndpointToManagementContext() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.endpoints.web.exposure.include=prometheus")
.run((context) -> assertThat(context).hasSingleBean(PrometheusScrapeEndpoint.class)
.doesNotHaveBean(PrometheusSimpleclientScrapeEndpoint.class));
}
@Test
void scrapeEndpointNotAddedToManagementContextWhenNotExposed() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(PrometheusSimpleclientScrapeEndpoint.class)
.doesNotHaveBean(PrometheusScrapeEndpoint.class));
}
@Test
void scrapeEndpointCanBeDisabled() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.endpoints.web.exposure.include=prometheus",
"management.endpoint.prometheus.enabled=false")
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(PrometheusSimpleclientScrapeEndpoint.class)
.doesNotHaveBean(PrometheusScrapeEndpoint.class));
}
@Test
void allowsCustomScrapeEndpointToBeUsed() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withUserConfiguration(CustomEndpointConfiguration.class)
.run((context) -> assertThat(context).hasBean("customEndpoint")
.hasSingleBean(PrometheusSimpleclientScrapeEndpoint.class));
}
@Test
void allowsCustomSecondScrapeEndpointToBeUsed() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withUserConfiguration(CustomSecondEndpointConfiguration.class)
.run((context) -> assertThat(context).hasBean("customSecondEndpoint")
.hasSingleBean(PrometheusSimpleclientScrapeEndpoint.class)
.hasSingleBean(SecondPrometheusScrapeEndpoint.class)
.hasSingleBean(PrometheusScrapeEndpoint.class));
}
@Test
void pushGatewayIsNotConfiguredWhenEnabledFlagIsNotSet() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(PrometheusPushGatewayManager.class));
}
@Test
void withPushGatewayEnabled(CapturedOutput output) {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true")
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> {
assertThat(output).doesNotContain("Invalid PushGateway base url");
hasGatewayURL(context, "http://localhost:9091/metrics/");
});
}
@Test
void withPushGatewayNoBasicAuth() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true")
.withUserConfiguration(BaseConfiguration.class)
.run(hasHttpConnectionFactory((httpConnectionFactory) -> assertThat(httpConnectionFactory)
.isInstanceOf(DefaultHttpConnectionFactory.class)));
}
@Test
void withCustomPushGatewayURL() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true",
"management.prometheus.metrics.export.pushgateway.base-url=https://example.com:8080")
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> hasGatewayURL(context, "https://example.com:8080/metrics/"));
}
@Test
void withPushGatewayBasicAuth() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true",
"management.prometheus.metrics.export.pushgateway.username=admin",
"management.prometheus.metrics.export.pushgateway.password=secret")
.withUserConfiguration(BaseConfiguration.class)
.run(hasHttpConnectionFactory((httpConnectionFactory) -> assertThat(httpConnectionFactory)
.isInstanceOf(BasicAuthHttpConnectionFactory.class)));
}
private void hasGatewayURL(AssertableApplicationContext context, String url) {
assertThat(getPushGateway(context)).hasFieldOrPropertyWithValue("gatewayBaseURL", url);
}
private ContextConsumer<AssertableApplicationContext> hasHttpConnectionFactory(
ThrowingConsumer<HttpConnectionFactory> httpConnectionFactory) {
return (context) -> {
PushGateway pushGateway = getPushGateway(context);
httpConnectionFactory
.accept((HttpConnectionFactory) ReflectionTestUtils.getField(pushGateway, "connectionFactory"));
};
}
private PushGateway getPushGateway(AssertableApplicationContext context) {
assertThat(context).hasSingleBean(PrometheusPushGatewayManager.class);
PrometheusPushGatewayManager gatewayManager = context.getBean(PrometheusPushGatewayManager.class);
return (PushGateway) ReflectionTestUtils.getField(gatewayManager, "pushGateway");
}
@Configuration(proxyBeanMethods = false)
static class BaseConfiguration {
@Bean
Clock clock() {
return Clock.SYSTEM;
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomConfigConfiguration {
@Bean
io.micrometer.prometheus.PrometheusConfig customConfig() {
return (key) -> null;
}
@Bean
io.micrometer.prometheusmetrics.PrometheusConfig otherCustomConfig() {
return (key) -> null;
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomRegistryConfiguration {
@Bean
io.micrometer.prometheus.PrometheusMeterRegistry customRegistry(
io.micrometer.prometheus.PrometheusConfig config, CollectorRegistry collectorRegistry, Clock clock) {
return new io.micrometer.prometheus.PrometheusMeterRegistry(config, collectorRegistry, clock);
}
@Bean
io.micrometer.prometheusmetrics.PrometheusMeterRegistry otherCustomRegistry(
io.micrometer.prometheusmetrics.PrometheusConfig config, PrometheusRegistry prometheusRegistry,
Clock clock) {
return new io.micrometer.prometheusmetrics.PrometheusMeterRegistry(config, prometheusRegistry, clock);
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomCollectorRegistryConfiguration {
@Bean
CollectorRegistry customCollectorRegistry() {
return new CollectorRegistry();
}
@Bean
PrometheusRegistry customPrometheusRegistry() {
return new PrometheusRegistry();
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomEndpointConfiguration {
@Bean
PrometheusSimpleclientScrapeEndpoint customEndpoint(CollectorRegistry collectorRegistry) {
return new PrometheusSimpleclientScrapeEndpoint(collectorRegistry);
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomSecondEndpointConfiguration {
@Bean
PrometheusScrapeEndpoint prometheusScrapeEndpoint(PrometheusRegistry prometheusRegistry,
PrometheusConfig prometheusConfig) {
return new PrometheusScrapeEndpoint(prometheusRegistry, prometheusConfig.prometheusProperties());
}
@Bean
SecondPrometheusScrapeEndpoint customSecondEndpoint(CollectorRegistry collectorRegistry) {
return new SecondPrometheusScrapeEndpoint(collectorRegistry);
}
@WebEndpoint(id = "prometheussc")
static class SecondPrometheusScrapeEndpoint extends PrometheusSimpleclientScrapeEndpoint {
SecondPrometheusScrapeEndpoint(CollectorRegistry collectorRegistry) {
super(collectorRegistry);
}
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class ExemplarsConfiguration {
@Bean
SpanContextSupplier spanContextSupplier() {
return new SpanContextSupplier() {
@Override
public String getTraceId() {
return null;
}
@Override
public String getSpanId() {
return null;
}
@Override
public boolean isSampled() {
return false;
}
};
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -35,15 +35,4 @@ class PrometheusPropertiesTests {
assertThat(properties.getStep()).isEqualTo(config.step());
}
@SuppressWarnings("deprecation")
@Test
void defaultValuesAreConsistentWithSimpleclient() {
PrometheusProperties properties = new PrometheusProperties();
io.micrometer.prometheus.PrometheusConfig config = io.micrometer.prometheus.PrometheusConfig.DEFAULT;
assertThat(properties.isDescriptions()).isEqualTo(config.descriptions());
assertThat(PrometheusSimpleclientPropertiesConfigAdapter.mapToMicrometerHistogramFlavor(properties))
.isEqualTo(config.histogramFlavor());
assertThat(properties.getStep()).isEqualTo(config.step());
}
}

View File

@ -1,330 +0,0 @@
/*
* Copyright 2012-2024 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.prometheus;
import io.micrometer.core.instrument.Clock;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exemplars.ExemplarSampler;
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
import io.prometheus.client.exporter.DefaultHttpConnectionFactory;
import io.prometheus.client.exporter.HttpConnectionFactory;
import io.prometheus.client.exporter.PushGateway;
import org.assertj.core.api.ThrowingConsumer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link PrometheusSimpleclientMetricsExportAutoConfiguration}.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Jonatan Ivanov
*/
@SuppressWarnings({ "removal", "deprecation" })
@ExtendWith(OutputCaptureExtension.class)
class PrometheusSimpleclientMetricsExportAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withClassLoader(new FilteredClassLoader("io.micrometer.prometheusmetrics.", "io.prometheus.metrics"))
.withConfiguration(AutoConfigurations.of(PrometheusSimpleclientMetricsExportAutoConfiguration.class));
@Test
void backsOffWithoutAClock() {
this.contextRunner.run((context) -> assertThat(context)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusMeterRegistry.class));
}
@Test
void autoConfiguresItsConfigCollectorRegistryAndMeterRegistry() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class));
}
@Test
void autoConfigurationCanBeDisabledWithDefaultsEnabledProperty() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.defaults.metrics.export.enabled=false")
.run((context) -> assertThat(context)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.doesNotHaveBean(CollectorRegistry.class)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusConfig.class));
}
@Test
void autoConfigurationCanBeDisabledWithSpecificEnabledProperty() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.prometheus.metrics.export.enabled=false")
.run((context) -> assertThat(context)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.doesNotHaveBean(CollectorRegistry.class)
.doesNotHaveBean(io.micrometer.prometheus.PrometheusConfig.class));
}
@Test
void allowsCustomConfigToBeUsed() {
this.contextRunner.withUserConfiguration(CustomConfigConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class)
.hasBean("customConfig"));
}
@Test
void allowsCustomRegistryToBeUsed() {
this.contextRunner.withUserConfiguration(CustomRegistryConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasBean("customRegistry")
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class));
}
@Test
void allowsCustomCollectorRegistryToBeUsed() {
this.contextRunner.withUserConfiguration(CustomCollectorRegistryConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class)
.hasBean("customCollectorRegistry")
.hasSingleBean(CollectorRegistry.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusConfig.class));
}
@Test
void autoConfiguresExemplarSamplerIfSpanContextSupplierIsPresent() {
this.contextRunner.withUserConfiguration(ExemplarsConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(SpanContextSupplier.class)
.hasSingleBean(ExemplarSampler.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class));
}
@Test
void allowsCustomExemplarSamplerToBeUsed() {
this.contextRunner.withUserConfiguration(ExemplarsConfiguration.class)
.withBean("customExemplarSampler", ExemplarSampler.class, () -> mock(ExemplarSampler.class))
.run((context) -> assertThat(context).hasSingleBean(ExemplarSampler.class)
.getBean(ExemplarSampler.class)
.isSameAs(context.getBean("customExemplarSampler")));
}
@Test
void exemplarSamplerIsNotAutoConfiguredIfSpanContextSupplierIsMissing() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(SpanContextSupplier.class)
.doesNotHaveBean(ExemplarSampler.class)
.hasSingleBean(io.micrometer.prometheus.PrometheusMeterRegistry.class));
}
@Test
void addsScrapeEndpointToManagementContext() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.endpoints.web.exposure.include=prometheus")
.run((context) -> assertThat(context).hasSingleBean(PrometheusSimpleclientScrapeEndpoint.class));
}
@Test
void scrapeEndpointNotAddedToManagementContextWhenNotExposed() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(PrometheusSimpleclientScrapeEndpoint.class));
}
@Test
void scrapeEndpointCanBeDisabled() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.endpoints.web.exposure.include=prometheus",
"management.endpoint.prometheus.enabled=false")
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(PrometheusSimpleclientScrapeEndpoint.class));
}
@Test
void allowsCustomScrapeEndpointToBeUsed() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withUserConfiguration(CustomEndpointConfiguration.class)
.run((context) -> assertThat(context).hasBean("customEndpoint")
.hasSingleBean(PrometheusSimpleclientScrapeEndpoint.class));
}
@Test
void pushGatewayIsNotConfiguredWhenEnabledFlagIsNotSet() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(PrometheusPushGatewayManager.class));
}
@Test
void withPushGatewayEnabled(CapturedOutput output) {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true")
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> {
assertThat(output).doesNotContain("Invalid PushGateway base url");
hasGatewayURL(context, "http://localhost:9091/metrics/");
});
}
@Test
void withPushGatewayNoBasicAuth() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true")
.withUserConfiguration(BaseConfiguration.class)
.run(hasHttpConnectionFactory((httpConnectionFactory) -> assertThat(httpConnectionFactory)
.isInstanceOf(DefaultHttpConnectionFactory.class)));
}
@Test
void withCustomPushGatewayURL() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true",
"management.prometheus.metrics.export.pushgateway.base-url=https://example.com:8080")
.withUserConfiguration(BaseConfiguration.class)
.run((context) -> hasGatewayURL(context, "https://example.com:8080/metrics/"));
}
@Test
void withPushGatewayBasicAuth() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class))
.withPropertyValues("management.prometheus.metrics.export.pushgateway.enabled=true",
"management.prometheus.metrics.export.pushgateway.username=admin",
"management.prometheus.metrics.export.pushgateway.password=secret")
.withUserConfiguration(BaseConfiguration.class)
.run(hasHttpConnectionFactory((httpConnectionFactory) -> assertThat(httpConnectionFactory)
.isInstanceOf(BasicAuthHttpConnectionFactory.class)));
}
private void hasGatewayURL(AssertableApplicationContext context, String url) {
assertThat(getPushGateway(context)).hasFieldOrPropertyWithValue("gatewayBaseURL", url);
}
private ContextConsumer<AssertableApplicationContext> hasHttpConnectionFactory(
ThrowingConsumer<HttpConnectionFactory> httpConnectionFactory) {
return (context) -> {
PushGateway pushGateway = getPushGateway(context);
httpConnectionFactory
.accept((HttpConnectionFactory) ReflectionTestUtils.getField(pushGateway, "connectionFactory"));
};
}
private PushGateway getPushGateway(AssertableApplicationContext context) {
assertThat(context).hasSingleBean(PrometheusPushGatewayManager.class);
PrometheusPushGatewayManager gatewayManager = context.getBean(PrometheusPushGatewayManager.class);
return (PushGateway) ReflectionTestUtils.getField(gatewayManager, "pushGateway");
}
@Configuration(proxyBeanMethods = false)
static class BaseConfiguration {
@Bean
Clock clock() {
return Clock.SYSTEM;
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomConfigConfiguration {
@Bean
io.micrometer.prometheus.PrometheusConfig customConfig() {
return (key) -> null;
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomRegistryConfiguration {
@Bean
io.micrometer.prometheus.PrometheusMeterRegistry customRegistry(
io.micrometer.prometheus.PrometheusConfig config, CollectorRegistry collectorRegistry, Clock clock) {
return new io.micrometer.prometheus.PrometheusMeterRegistry(config, collectorRegistry, clock);
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomCollectorRegistryConfiguration {
@Bean
CollectorRegistry customCollectorRegistry() {
return new CollectorRegistry();
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomEndpointConfiguration {
@Bean
PrometheusSimpleclientScrapeEndpoint customEndpoint(CollectorRegistry collectorRegistry) {
return new PrometheusSimpleclientScrapeEndpoint(collectorRegistry);
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class ExemplarsConfiguration {
@Bean
SpanContextSupplier spanContextSupplier() {
return new SpanContextSupplier() {
@Override
public String getTraceId() {
return null;
}
@Override
public String getSpanId() {
return null;
}
@Override
public boolean isSampled() {
return false;
}
};
}
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright 2012-2024 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.prometheus;
import java.time.Duration;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties.HistogramFlavor;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.AbstractPropertiesConfigAdapterTests;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PrometheusSimpleclientPropertiesConfigAdapter}.
*
* @author Mirko Sobeck
*/
@SuppressWarnings({ "deprecation", "removal" })
class PrometheusSimpleclientPropertiesConfigAdapterTests extends
AbstractPropertiesConfigAdapterTests<PrometheusProperties, PrometheusSimpleclientPropertiesConfigAdapter> {
PrometheusSimpleclientPropertiesConfigAdapterTests() {
super(PrometheusSimpleclientPropertiesConfigAdapter.class);
}
@Test
void whenPropertiesDescriptionsIsSetAdapterDescriptionsReturnsIt() {
PrometheusProperties properties = new PrometheusProperties();
properties.setDescriptions(false);
assertThat(new PrometheusSimpleclientPropertiesConfigAdapter(properties).descriptions()).isFalse();
}
@Test
void whenPropertiesHistogramFlavorIsSetAdapterHistogramFlavorReturnsIt() {
PrometheusProperties properties = new PrometheusProperties();
properties.setHistogramFlavor(HistogramFlavor.VictoriaMetrics);
assertThat(new PrometheusSimpleclientPropertiesConfigAdapter(properties).histogramFlavor())
.isEqualTo(io.micrometer.prometheus.HistogramFlavor.VictoriaMetrics);
}
@Test
void whenPropertiesStepIsSetAdapterStepReturnsIt() {
PrometheusProperties properties = new PrometheusProperties();
properties.setStep(Duration.ofSeconds(30));
assertThat(new PrometheusSimpleclientPropertiesConfigAdapter(properties).step())
.isEqualTo(Duration.ofSeconds(30));
}
}

View File

@ -1,152 +0,0 @@
/*
* Copyright 2012-2024 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.prometheus;
import io.micrometer.tracing.Span;
import io.micrometer.tracing.TraceContext;
import io.micrometer.tracing.Tracer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for
* {@link org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusSimpleclientExemplarsAutoConfiguration.LazyTracingSpanContextSupplier}.
*
* @author Andy Wilkinson
*/
@SuppressWarnings({ "deprecation", "removal" })
class LazyTracingSpanContextSupplierTests {
private final Tracer tracer = mock(Tracer.class);
private final ObjectProvider<Tracer> objectProvider = new ObjectProvider<>() {
@Override
public Tracer getObject() throws BeansException {
return LazyTracingSpanContextSupplierTests.this.tracer;
}
@Override
public Tracer getObject(Object... args) throws BeansException {
return LazyTracingSpanContextSupplierTests.this.tracer;
}
@Override
public Tracer getIfAvailable() throws BeansException {
return LazyTracingSpanContextSupplierTests.this.tracer;
}
@Override
public Tracer getIfUnique() throws BeansException {
return LazyTracingSpanContextSupplierTests.this.tracer;
}
};
private final org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusSimpleclientExemplarsAutoConfiguration.LazyTracingSpanContextSupplier spanContextSupplier = new org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusSimpleclientExemplarsAutoConfiguration.LazyTracingSpanContextSupplier(
this.objectProvider);
@Test
void whenCurrentSpanIsNullThenSpanIdIsNull() {
assertThat(this.spanContextSupplier.getSpanId()).isNull();
}
@Test
void whenCurrentSpanIsNullThenTraceIdIsNull() {
assertThat(this.spanContextSupplier.getTraceId()).isNull();
}
@Test
void whenCurrentSpanIsNullThenSampledIsFalse() {
assertThat(this.spanContextSupplier.isSampled()).isFalse();
}
@Test
void whenCurrentSpanHasSpanIdThenSpanIdIsFromSpan() {
Span span = mock(Span.class);
given(this.tracer.currentSpan()).willReturn(span);
TraceContext traceContext = mock(TraceContext.class);
given(traceContext.spanId()).willReturn("span-id");
given(span.context()).willReturn(traceContext);
assertThat(this.spanContextSupplier.getSpanId()).isEqualTo("span-id");
}
@Test
void whenCurrentSpanHasTraceIdThenTraceIdIsFromSpan() {
Span span = mock(Span.class);
given(this.tracer.currentSpan()).willReturn(span);
TraceContext traceContext = mock(TraceContext.class);
given(traceContext.traceId()).willReturn("trace-id");
given(span.context()).willReturn(traceContext);
assertThat(this.spanContextSupplier.getTraceId()).isEqualTo("trace-id");
}
@Test
void whenCurrentSpanHasNoSpanIdThenSpanIdIsNull() {
Span span = mock(Span.class);
given(this.tracer.currentSpan()).willReturn(span);
TraceContext traceContext = mock(TraceContext.class);
given(span.context()).willReturn(traceContext);
assertThat(this.spanContextSupplier.getSpanId()).isNull();
}
@Test
void whenCurrentSpanHasNoTraceIdThenTraceIdIsNull() {
Span span = mock(Span.class);
given(this.tracer.currentSpan()).willReturn(span);
TraceContext traceContext = mock(TraceContext.class);
given(span.context()).willReturn(traceContext);
assertThat(this.spanContextSupplier.getTraceId()).isNull();
}
@Test
void whenCurrentSpanIsSampledThenSampledIsTrue() {
Span span = mock(Span.class);
given(this.tracer.currentSpan()).willReturn(span);
TraceContext traceContext = mock(TraceContext.class);
given(traceContext.sampled()).willReturn(true);
given(span.context()).willReturn(traceContext);
assertThat(this.spanContextSupplier.isSampled()).isTrue();
}
@Test
void whenCurrentSpanIsNotSampledThenSampledIsFalse() {
Span span = mock(Span.class);
given(this.tracer.currentSpan()).willReturn(span);
TraceContext traceContext = mock(TraceContext.class);
given(traceContext.sampled()).willReturn(false);
given(span.context()).willReturn(traceContext);
assertThat(this.spanContextSupplier.isSampled()).isFalse();
}
@Test
void whenCurrentSpanHasDeferredSamplingThenSampledIsFalse() {
Span span = mock(Span.class);
given(this.tracer.currentSpan()).willReturn(span);
TraceContext traceContext = mock(TraceContext.class);
given(traceContext.sampled()).willReturn(null);
given(span.context()).willReturn(traceContext);
assertThat(this.spanContextSupplier.isSampled()).isFalse();
}
}

View File

@ -1,135 +0,0 @@
/*
* Copyright 2012-2024 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.prometheus;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
import io.prometheus.client.exporter.common.TextFormat;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusSimpleclientMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link PrometheusSimpleclientExemplarsAutoConfiguration}.
*
* @author Jonatan Ivanov
*/
@SuppressWarnings("removal")
class PrometheusSimpleclientExemplarsAutoConfigurationTests {
private static final Pattern BUCKET_TRACE_INFO_PATTERN = Pattern.compile(
"^test_observation_seconds_bucket\\{error=\"none\",le=\".+\"} 1.0 # \\{span_id=\"(\\p{XDigit}+)\",trace_id=\"(\\p{XDigit}+)\"} .+$");
private static final Pattern COUNTER_TRACE_INFO_PATTERN = Pattern.compile(
"^test_observation_seconds_count\\{error=\"none\"} 1.0 # \\{span_id=\"(\\p{XDigit}+)\",trace_id=\"(\\p{XDigit}+)\"} .+$");
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withPropertyValues("management.tracing.sampling.probability=1.0",
"management.metrics.distribution.percentiles-histogram.all=true")
.with(MetricsRun.limitedTo())
.withConfiguration(AutoConfigurations.of(PrometheusSimpleclientMetricsExportAutoConfiguration.class,
PrometheusSimpleclientExemplarsAutoConfiguration.class, ObservationAutoConfiguration.class,
BraveAutoConfiguration.class, MicrometerTracingAutoConfiguration.class));
@Test
void shouldNotSupplyBeansIfPrometheusSupportIsMissing() {
this.contextRunner.withClassLoader(new FilteredClassLoader("io.prometheus.client.exemplars"))
.run((context) -> assertThat(context).doesNotHaveBean(SpanContextSupplier.class));
}
@Test
void shouldNotSupplyBeansIfMicrometerTracingIsMissing() {
this.contextRunner.withClassLoader(new FilteredClassLoader("io.micrometer.tracing"))
.run((context) -> assertThat(context).doesNotHaveBean(SpanContextSupplier.class));
}
@Test
void shouldSupplyCustomBeans() {
this.contextRunner.withUserConfiguration(CustomConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(SpanContextSupplier.class)
.getBean(SpanContextSupplier.class)
.isSameAs(CustomConfiguration.SUPPLIER));
}
@Test
@SuppressWarnings("deprecation")
void prometheusOpenMetricsOutputShouldContainExemplars() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(SpanContextSupplier.class);
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("test.observation", observationRegistry).stop();
io.micrometer.prometheus.PrometheusMeterRegistry prometheusMeterRegistry = context
.getBean(io.micrometer.prometheus.PrometheusMeterRegistry.class);
String openMetricsOutput = prometheusMeterRegistry.scrape(TextFormat.CONTENT_TYPE_OPENMETRICS_100);
assertThat(openMetricsOutput).contains("test_observation_seconds_bucket");
assertThat(openMetricsOutput).containsOnlyOnce("test_observation_seconds_count");
assertThat(StringUtils.countOccurrencesOf(openMetricsOutput, "span_id")).isEqualTo(2);
assertThat(StringUtils.countOccurrencesOf(openMetricsOutput, "trace_id")).isEqualTo(2);
Optional<TraceInfo> bucketTraceInfo = openMetricsOutput.lines()
.filter((line) -> line.contains("test_observation_seconds_bucket") && line.contains("span_id"))
.map(BUCKET_TRACE_INFO_PATTERN::matcher)
.flatMap(Matcher::results)
.map((matchResult) -> new TraceInfo(matchResult.group(2), matchResult.group(1)))
.findFirst();
Optional<TraceInfo> counterTraceInfo = openMetricsOutput.lines()
.filter((line) -> line.contains("test_observation_seconds_count") && line.contains("span_id"))
.map(COUNTER_TRACE_INFO_PATTERN::matcher)
.flatMap(Matcher::results)
.map((matchResult) -> new TraceInfo(matchResult.group(2), matchResult.group(1)))
.findFirst();
assertThat(bucketTraceInfo).isNotEmpty().contains(counterTraceInfo.orElse(null));
});
}
@Configuration(proxyBeanMethods = false)
private static final class CustomConfiguration {
static final SpanContextSupplier SUPPLIER = mock(SpanContextSupplier.class);
@Bean
SpanContextSupplier customSpanContextSupplier() {
return SUPPLIER;
}
}
private record TraceInfo(String traceId, String spanId) {
}
}

View File

@ -16,17 +16,9 @@
package org.springframework.boot.actuate.autoconfigure.tracing.zipkin;
import java.io.IOException;
import java.net.http.HttpClient;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import zipkin2.reporter.BytesMessageSender;
import zipkin2.reporter.HttpEndpointSupplier;
import zipkin2.reporter.urlconnection.URLConnectionSender;
@ -36,16 +28,10 @@ import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfi
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
/**
@ -60,19 +46,11 @@ class ZipkinConfigurationsSenderConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DefaultEncodingConfiguration.class, SenderConfiguration.class));
private final ReactiveWebApplicationContextRunner reactiveContextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DefaultEncodingConfiguration.class, SenderConfiguration.class));
private final WebApplicationContextRunner servletContextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DefaultEncodingConfiguration.class, SenderConfiguration.class));
@Test
void shouldSupplyDefaultHttpClientSenderBeans() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinHttpClientSender.class);
assertThat(context).doesNotHaveBean(ZipkinRestTemplateSender.class);
assertThat(context).doesNotHaveBean(ZipkinWebClientSenderTests.class);
assertThat(context).doesNotHaveBean(URLConnectionSender.class);
});
}
@ -80,7 +58,7 @@ class ZipkinConfigurationsSenderConfigurationTests {
@Test
void shouldUseUrlSenderIfHttpSenderIsNotAvailable() {
this.contextRunner.withUserConfiguration(UrlConnectionSenderConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class, RestTemplate.class))
.withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
@ -88,83 +66,6 @@ class ZipkinConfigurationsSenderConfigurationTests {
});
}
@Test
void shouldPreferWebClientSenderIfWebApplicationIsReactiveAndHttpClientSenderIsNotAvailable() {
this.reactiveContextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinWebClientSender.class);
then(context.getBean(ZipkinWebClientBuilderCustomizer.class)).should()
.customize(ArgumentMatchers.any());
});
}
@Test
void shouldPreferWebClientSenderIfWebApplicationIsServletAndHttpClientSenderIsNotAvailable() {
this.servletContextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinWebClientSender.class);
});
}
@Test
void shouldPreferWebClientInNonWebApplicationAndHttpClientSenderIsNotAvailable() {
this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinWebClientSender.class);
});
}
@Test
void willUseRestTemplateInNonWebApplicationIfSenderAndWebClientAreNotAvailable() {
this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(HttpClient.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
});
}
@Test
void willUseRestTemplateInServletWebApplicationIfHttpClientSenderAndWebClientNotAvailable() {
this.servletContextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
});
}
@Test
void willUseRestTemplateInReactiveWebApplicationIfHttpClientSenderAndWebClientAreNotAvailable() {
this.reactiveContextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
});
}
@Test
void shouldNotUseWebClientSenderIfNoBuilderIsAvailable() {
this.reactiveContextRunner.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinWebClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinHttpClientSender.class);
});
}
@Test
void shouldBackOffOnCustomBeans() {
this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> {
@ -173,74 +74,15 @@ class ZipkinConfigurationsSenderConfigurationTests {
});
}
@Test
void shouldApplyZipkinRestTemplateBuilderCustomizers() throws IOException {
try (MockWebServer mockWebServer = new MockWebServer()) {
mockWebServer.enqueue(new MockResponse().setResponseCode(204));
this.reactiveContextRunner
.withPropertyValues("management.zipkin.tracing.endpoint=" + mockWebServer.url("/"))
.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> {
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
ZipkinRestTemplateSender sender = context.getBean(ZipkinRestTemplateSender.class);
sender.send(List.of("spans".getBytes(StandardCharsets.UTF_8)));
RecordedRequest recordedRequest = mockWebServer.takeRequest(1, TimeUnit.SECONDS);
assertThat(recordedRequest).isNotNull();
assertThat(recordedRequest.getHeaders().get("x-dummy")).isEqualTo("dummy");
});
}
}
@Test
void shouldUseCustomHttpEndpointSupplierFactory() {
this.contextRunner.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class, RestTemplate.class))
.withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> assertThat(context.getBean(URLConnectionSender.class))
.extracting("delegate.endpointSupplier")
.isInstanceOf(CustomHttpEndpointSupplier.class));
}
@Test
@SuppressWarnings("resource")
void shouldUseCustomHttpEndpointSupplierFactoryWhenReactive() {
this.reactiveContextRunner.withUserConfiguration(WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class))
.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class)
.run((context) -> assertThat(context.getBean(ZipkinWebClientSender.class)).extracting("endpointSupplier")
.isInstanceOf(CustomHttpEndpointSupplier.class));
}
@Test
@SuppressWarnings("resource")
void shouldUseCustomHttpEndpointSupplierFactoryWhenRestTemplate() {
this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class)
.run((context) -> assertThat(context.getBean(ZipkinRestTemplateSender.class)).extracting("endpointSupplier")
.isInstanceOf(CustomHttpEndpointSupplier.class));
}
@Configuration(proxyBeanMethods = false)
private static final class RestTemplateConfiguration {
@Bean
ZipkinRestTemplateBuilderCustomizer zipkinRestTemplateBuilderCustomizer() {
return new DummyZipkinRestTemplateBuilderCustomizer();
}
}
@Configuration(proxyBeanMethods = false)
private static final class WebClientConfiguration {
@Bean
ZipkinWebClientBuilderCustomizer webClientBuilder() {
return mock(ZipkinWebClientBuilderCustomizer.class);
}
}
@Configuration(proxyBeanMethods = false)
private static final class HttpClientConfiguration {
@ -261,15 +103,6 @@ class ZipkinConfigurationsSenderConfigurationTests {
}
private static final class DummyZipkinRestTemplateBuilderCustomizer implements ZipkinRestTemplateBuilderCustomizer {
@Override
public RestTemplateBuilder customize(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.defaultHeader("x-dummy", "dummy");
}
}
@Configuration(proxyBeanMethods = false)
private static final class CustomHttpEndpointSupplierFactoryConfiguration {

View File

@ -1,139 +0,0 @@
/*
* Copyright 2012-2024 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.zipkin;
import java.io.IOException;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import zipkin2.reporter.BytesMessageSender;
import zipkin2.reporter.Encoding;
import zipkin2.reporter.HttpEndpointSupplier;
import zipkin2.reporter.HttpEndpointSuppliers;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThatException;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
/**
* Tests for {@link ZipkinRestTemplateSender}.
*
* @author Moritz Halbritter
* @author Stefan Bratanov
*/
@SuppressWarnings({ "deprecation", "removal" })
class ZipkinRestTemplateSenderTests extends ZipkinHttpSenderTests {
private static final String ZIPKIN_URL = "http://localhost:9411/api/v2/spans";
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
@Override
BytesMessageSender createSender() {
this.restTemplate = new RestTemplate();
this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
return createSender(Encoding.JSON);
}
BytesMessageSender createSender(Encoding encoding) {
return createSender(HttpEndpointSuppliers.constantFactory(), encoding);
}
BytesMessageSender createSender(HttpEndpointSupplier.Factory endpointSupplierFactory, Encoding encoding) {
return new ZipkinRestTemplateSender(encoding, endpointSupplierFactory, ZIPKIN_URL, this.restTemplate);
}
@AfterEach
@Override
void afterEach() throws IOException {
super.afterEach();
this.mockServer.verify();
}
@Test
void sendShouldSendSpansToZipkin() throws IOException {
this.mockServer.expect(requestTo(ZIPKIN_URL))
.andExpect(method(HttpMethod.POST))
.andExpect(content().contentType("application/json"))
.andExpect(content().string("[span1,span2]"))
.andRespond(withStatus(HttpStatus.ACCEPTED));
this.sender.send(List.of(toByteArray("span1"), toByteArray("span2")));
}
@Test
void sendShouldSendSpansToZipkinInProto3() throws IOException {
this.mockServer.expect(requestTo(ZIPKIN_URL))
.andExpect(method(HttpMethod.POST))
.andExpect(content().contentType("application/x-protobuf"))
.andExpect(content().string("span1span2"))
.andRespond(withStatus(HttpStatus.ACCEPTED));
try (BytesMessageSender sender = createSender(Encoding.PROTO3)) {
sender.send(List.of(toByteArray("span1"), toByteArray("span2")));
}
}
@Test
void sendUsesDynamicEndpoint() throws Exception {
this.mockServer.expect(requestTo(ZIPKIN_URL + "/1")).andRespond(withStatus(HttpStatus.ACCEPTED));
this.mockServer.expect(requestTo(ZIPKIN_URL + "/2")).andRespond(withStatus(HttpStatus.ACCEPTED));
try (HttpEndpointSupplier httpEndpointSupplier = new TestHttpEndpointSupplier(ZIPKIN_URL)) {
try (BytesMessageSender sender = createSender((endpoint) -> httpEndpointSupplier, Encoding.JSON)) {
sender.send(Collections.emptyList());
sender.send(Collections.emptyList());
}
}
}
@Test
void sendShouldHandleHttpFailures() {
this.mockServer.expect(requestTo(ZIPKIN_URL))
.andExpect(method(HttpMethod.POST))
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
assertThatException().isThrownBy(() -> this.sender.send(Collections.emptyList()))
.withMessageContaining("500 Internal Server Error");
}
@Test
void sendShouldCompressData() throws IOException {
String uncompressed = "a".repeat(10000);
// This is gzip compressed 10000 times 'a'
byte[] compressed = Base64.getDecoder()
.decode("H4sIAAAAAAAA/+3BMQ0AAAwDIKFLj/k3UR8NcA8AAAAAAAAAAAADUsAZfeASJwAA");
this.mockServer.expect(requestTo(ZIPKIN_URL))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Content-Encoding", "gzip"))
.andExpect(content().contentType("application/json"))
.andExpect(content().bytes(compressed))
.andRespond(withStatus(HttpStatus.ACCEPTED));
this.sender.send(List.of(toByteArray(uncompressed)));
}
}

View File

@ -1,204 +0,0 @@
/*
* Copyright 2012-2024 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.zipkin;
import java.io.IOException;
import java.time.Duration;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.QueueDispatcher;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import zipkin2.reporter.BytesMessageSender;
import zipkin2.reporter.Encoding;
import zipkin2.reporter.HttpEndpointSupplier;
import zipkin2.reporter.HttpEndpointSuppliers;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatException;
/**
* Tests for {@link ZipkinWebClientSender}.
*
* @author Stefan Bratanov
*/
@SuppressWarnings({ "deprecation", "removal" })
class ZipkinWebClientSenderTests extends ZipkinHttpSenderTests {
private static final Duration TIMEOUT = Duration.ofSeconds(30);
private static ClearableDispatcher dispatcher;
private static MockWebServer mockBackEnd;
private static String ZIPKIN_URL;
@BeforeAll
static void beforeAll() throws IOException {
dispatcher = new ClearableDispatcher();
mockBackEnd = new MockWebServer();
mockBackEnd.setDispatcher(dispatcher);
mockBackEnd.start();
ZIPKIN_URL = mockBackEnd.url("/api/v2/spans").toString();
}
@AfterAll
static void afterAll() throws IOException {
mockBackEnd.shutdown();
}
@Override
@BeforeEach
void beforeEach() {
super.beforeEach();
clearResponses();
clearRequests();
}
@Override
BytesMessageSender createSender() {
return createSender(Encoding.JSON, TIMEOUT);
}
ZipkinWebClientSender createSender(Encoding encoding, Duration timeout) {
return createSender(HttpEndpointSuppliers.constantFactory(), encoding, timeout);
}
ZipkinWebClientSender createSender(HttpEndpointSupplier.Factory endpointSupplierFactory, Encoding encoding,
Duration timeout) {
WebClient webClient = WebClient.builder().build();
return new ZipkinWebClientSender(encoding, endpointSupplierFactory, ZIPKIN_URL, webClient, timeout);
}
@Test
void sendShouldSendSpansToZipkin() throws IOException, InterruptedException {
mockBackEnd.enqueue(new MockResponse());
List<byte[]> encodedSpans = List.of(toByteArray("span1"), toByteArray("span2"));
this.sender.send(encodedSpans);
requestAssertions((request) -> {
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeader("Content-Type")).isEqualTo("application/json");
assertThat(request.getBody().readUtf8()).isEqualTo("[span1,span2]");
});
}
@Test
void sendShouldSendSpansToZipkinInProto3() throws IOException, InterruptedException {
mockBackEnd.enqueue(new MockResponse());
List<byte[]> encodedSpans = List.of(toByteArray("span1"), toByteArray("span2"));
try (BytesMessageSender sender = createSender(Encoding.PROTO3, TIMEOUT)) {
sender.send(encodedSpans);
}
requestAssertions((request) -> {
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeader("Content-Type")).isEqualTo("application/x-protobuf");
assertThat(request.getBody().readUtf8()).isEqualTo("span1span2");
});
}
@Test
void sendUsesDynamicEndpoint() throws Exception {
mockBackEnd.enqueue(new MockResponse());
mockBackEnd.enqueue(new MockResponse());
try (HttpEndpointSupplier httpEndpointSupplier = new TestHttpEndpointSupplier(ZIPKIN_URL)) {
try (BytesMessageSender sender = createSender((endpoint) -> httpEndpointSupplier, Encoding.JSON, TIMEOUT)) {
sender.send(Collections.emptyList());
sender.send(Collections.emptyList());
}
assertThat(mockBackEnd.takeRequest().getPath()).endsWith("/1");
assertThat(mockBackEnd.takeRequest().getPath()).endsWith("/2");
}
}
@Test
void sendShouldHandleHttpFailures() throws InterruptedException {
mockBackEnd.enqueue(new MockResponse().setResponseCode(500));
assertThatException().isThrownBy(() -> this.sender.send(Collections.emptyList()))
.withMessageContaining("500 Internal Server Error");
requestAssertions((request) -> assertThat(request.getMethod()).isEqualTo("POST"));
}
@Test
void sendShouldCompressData() throws IOException, InterruptedException {
String uncompressed = "a".repeat(10000);
// This is gzip compressed 10000 times 'a'
byte[] compressed = Base64.getDecoder()
.decode("H4sIAAAAAAAA/+3BMQ0AAAwDIKFLj/k3UR8NcA8AAAAAAAAAAAADUsAZfeASJwAA");
mockBackEnd.enqueue(new MockResponse());
this.sender.send(List.of(toByteArray(uncompressed)));
requestAssertions((request) -> {
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeader("Content-Type")).isEqualTo("application/json");
assertThat(request.getHeader("Content-Encoding")).isEqualTo("gzip");
assertThat(request.getBody().readByteArray()).isEqualTo(compressed);
});
}
@Test
void shouldTimeout() throws IOException {
try (BytesMessageSender sender = createSender(Encoding.JSON, Duration.ofMillis(1))) {
MockResponse response = new MockResponse().setResponseCode(200).setHeadersDelay(100, TimeUnit.MILLISECONDS);
mockBackEnd.enqueue(response);
assertThatException().isThrownBy(() -> sender.send(Collections.emptyList()))
.withCauseInstanceOf(TimeoutException.class);
}
}
private void requestAssertions(Consumer<RecordedRequest> assertions) throws InterruptedException {
RecordedRequest request = mockBackEnd.takeRequest();
assertThat(request).satisfies(assertions);
}
private static void clearRequests() {
RecordedRequest request;
do {
try {
request = mockBackEnd.takeRequest(0, TimeUnit.SECONDS);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new RuntimeException(ex);
}
}
while (request != null);
}
private static void clearResponses() {
dispatcher.clear();
}
private static final class ClearableDispatcher extends QueueDispatcher {
void clear() {
getResponseQueue().clear();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -53,17 +53,6 @@ public class PrometheusScrapeEndpoint {
private volatile int nextMetricsScrapeSize = 16;
/**
* Creates a new {@link PrometheusScrapeEndpoint}.
* @param prometheusRegistry the Prometheus registry to use
* @deprecated since 3.3.1 for removal in 3.5.0 in favor of
* {@link #PrometheusScrapeEndpoint(PrometheusRegistry, Properties)}
*/
@Deprecated(since = "3.3.1", forRemoval = true)
public PrometheusScrapeEndpoint(PrometheusRegistry prometheusRegistry) {
this(prometheusRegistry, null);
}
/**
* Creates a new {@link PrometheusScrapeEndpoint}.
* @param prometheusRegistry the Prometheus registry to use

View File

@ -1,77 +0,0 @@
/*
* Copyright 2012-2024 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.metrics.export.prometheus;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Set;
import io.prometheus.client.Collector.MetricFamilySamples;
import io.prometheus.client.CollectorRegistry;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.lang.Nullable;
/**
* {@link Endpoint @Endpoint} that uses the Prometheus simpleclient to output metrics in a
* format that can be scraped by the Prometheus server.
*
* @author Jon Schneider
* @author Johnny Lim
* @since 2.0.0
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of
* {@link PrometheusScrapeEndpoint}
*/
@Deprecated(since = "3.3.0", forRemoval = true)
@WebEndpoint(id = "prometheus")
public class PrometheusSimpleclientScrapeEndpoint {
private static final int METRICS_SCRAPE_CHARS_EXTRA = 1024;
private final CollectorRegistry collectorRegistry;
private volatile int nextMetricsScrapeSize = 16;
public PrometheusSimpleclientScrapeEndpoint(CollectorRegistry collectorRegistry) {
this.collectorRegistry = collectorRegistry;
}
@SuppressWarnings("removal")
@ReadOperation(producesFrom = TextOutputFormat.class)
public WebEndpointResponse<String> scrape(TextOutputFormat format, @Nullable Set<String> includedNames) {
try {
Writer writer = new StringWriter(this.nextMetricsScrapeSize);
Enumeration<MetricFamilySamples> samples = (includedNames != null)
? this.collectorRegistry.filteredMetricFamilySamples(includedNames)
: this.collectorRegistry.metricFamilySamples();
format.write(writer, samples);
String scrapePage = writer.toString();
this.nextMetricsScrapeSize = scrapePage.length() + METRICS_SCRAPE_CHARS_EXTRA;
return new WebEndpointResponse<>(scrapePage, format);
}
catch (IOException ex) {
// This actually never happens since StringWriter doesn't throw an IOException
throw new IllegalStateException("Writing metrics failed", ex);
}
}
}

View File

@ -1,82 +0,0 @@
/*
* Copyright 2012-2024 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.metrics.export.prometheus;
import java.io.IOException;
import java.io.Writer;
import java.util.Enumeration;
import io.prometheus.client.Collector.MetricFamilySamples;
import io.prometheus.client.exporter.common.TextFormat;
import org.springframework.boot.actuate.endpoint.Producible;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
/**
* A {@link Producible} enum for supported Prometheus {@link TextFormat}.
*
* @author Andy Wilkinson
* @since 2.5.0
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of {@link PrometheusOutputFormat}
*/
@Deprecated(since = "3.3.0", forRemoval = true)
public enum TextOutputFormat implements Producible<TextOutputFormat> {
/**
* Prometheus text version 0.0.4.
*/
CONTENT_TYPE_004(TextFormat.CONTENT_TYPE_004) {
@Override
void write(Writer writer, Enumeration<MetricFamilySamples> samples) throws IOException {
TextFormat.write004(writer, samples);
}
@Override
public boolean isDefault() {
return true;
}
},
/**
* OpenMetrics text version 1.0.0.
*/
CONTENT_TYPE_OPENMETRICS_100(TextFormat.CONTENT_TYPE_OPENMETRICS_100) {
@Override
void write(Writer writer, Enumeration<MetricFamilySamples> samples) throws IOException {
TextFormat.writeOpenMetrics100(writer, samples);
}
};
private final MimeType mimeType;
TextOutputFormat(String mimeType) {
this.mimeType = MimeTypeUtils.parseMimeType(mimeType);
}
@Override
public MimeType getProducedMimeType() {
return this.mimeType;
}
abstract void write(Writer writer, Enumeration<MetricFamilySamples> samples) throws IOException;
}

View File

@ -42,17 +42,6 @@ public class SessionsEndpoint {
private final FindByIndexNameSessionRepository<? extends Session> indexedSessionRepository;
/**
* Create a new {@link SessionsEndpoint} instance.
* @param sessionRepository the session repository
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of
* {@link #SessionsEndpoint(SessionRepository, FindByIndexNameSessionRepository)}
*/
@Deprecated(since = "3.3.0", forRemoval = true)
public SessionsEndpoint(FindByIndexNameSessionRepository<? extends Session> sessionRepository) {
this(sessionRepository, sessionRepository);
}
/**
* Create a new {@link SessionsEndpoint} instance.
* @param sessionRepository the session repository

View File

@ -1,150 +0,0 @@
/*
* Copyright 2012-2024 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.metrics.export.prometheus;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.common.TextFormat;
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PrometheusSimpleclientScrapeEndpoint}.
*
* @author Jon Schneider
* @author Johnny Lim
*/
@SuppressWarnings({ "deprecation", "removal" })
class PrometheusSimpleclientScrapeEndpointIntegrationTests {
@WebEndpointTest
void scrapeHasContentTypeText004ByDefault(WebTestClient client) {
String expectedContentType = TextFormat.CONTENT_TYPE_004;
assertThat(TextFormat.chooseContentType(null)).isEqualTo(expectedContentType);
client.get()
.uri("/actuator/prometheus")
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(MediaType.parseMediaType(expectedContentType))
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.contains("counter3_total"));
}
@WebEndpointTest
void scrapeHasContentTypeText004ByDefaultWhenClientAcceptsWildcardWithParameter(WebTestClient client) {
String expectedContentType = TextFormat.CONTENT_TYPE_004;
String accept = "*/*;q=0.8";
assertThat(TextFormat.chooseContentType(accept)).isEqualTo(expectedContentType);
client.get()
.uri("/actuator/prometheus")
.accept(MediaType.parseMediaType(accept))
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(MediaType.parseMediaType(expectedContentType))
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.contains("counter3_total"));
}
@WebEndpointTest
void scrapeCanProduceOpenMetrics100(WebTestClient client) {
MediaType openMetrics = MediaType.parseMediaType(TextFormat.CONTENT_TYPE_OPENMETRICS_100);
client.get()
.uri("/actuator/prometheus")
.accept(openMetrics)
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(openMetrics)
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.contains("counter3_total"));
}
@WebEndpointTest
void scrapePrefersToProduceOpenMetrics100(WebTestClient client) {
MediaType openMetrics = MediaType.parseMediaType(TextFormat.CONTENT_TYPE_OPENMETRICS_100);
MediaType textPlain = MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004);
client.get()
.uri("/actuator/prometheus")
.accept(openMetrics, textPlain)
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(openMetrics);
}
@WebEndpointTest
void scrapeWithIncludedNames(WebTestClient client) {
client.get()
.uri("/actuator/prometheus?includedNames=counter1_total,counter2_total")
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004))
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.doesNotContain("counter3_total"));
}
@Configuration(proxyBeanMethods = false)
static class TestConfiguration {
@Bean
PrometheusSimpleclientScrapeEndpoint prometheusScrapeEndpoint(CollectorRegistry collectorRegistry) {
return new PrometheusSimpleclientScrapeEndpoint(collectorRegistry);
}
@Bean
CollectorRegistry collectorRegistry() {
return new CollectorRegistry(true);
}
@Bean
@SuppressWarnings("deprecation")
MeterRegistry registry(CollectorRegistry registry) {
io.micrometer.prometheus.PrometheusMeterRegistry meterRegistry = new io.micrometer.prometheus.PrometheusMeterRegistry(
(k) -> null, registry, Clock.SYSTEM);
Counter.builder("counter1").register(meterRegistry);
Counter.builder("counter2").register(meterRegistry);
Counter.builder("counter3").register(meterRegistry);
return meterRegistry;
}
}
}

View File

@ -1,210 +0,0 @@
/*
* Copyright 2012-2024 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.metrics.export.prometheus;
import java.util.Properties;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter;
import io.prometheus.metrics.expositionformats.PrometheusTextFormatWriter;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for exposing a {@link PrometheusScrapeEndpoint} and
* {@link PrometheusSimpleclientScrapeEndpoint} with different IDs.
*
* @author Jon Schneider
* @author Johnny Lim
*/
class SecondCustomPrometheusScrapeEndpointIntegrationTests {
@WebEndpointTest
void scrapeHasContentTypeText004ByDefault(WebTestClient client) {
scrapeHasContentTypeText004ByDefault(client, "/actuator/prometheus");
scrapeHasContentTypeText004ByDefault(client, "/actuator/prometheussc");
}
private void scrapeHasContentTypeText004ByDefault(WebTestClient client, String uri) {
String expectedContentType = PrometheusTextFormatWriter.CONTENT_TYPE;
client.get()
.uri(uri)
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(MediaType.parseMediaType(expectedContentType))
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.contains("counter3_total"));
}
@WebEndpointTest
void scrapeHasContentTypeText004ByDefaultWhenClientAcceptsWildcardWithParameter(WebTestClient client) {
scrapeHasContentTypeText004ByDefaultWhenClientAcceptsWildcardWithParameter(client, "/actuator/prometheus");
scrapeHasContentTypeText004ByDefaultWhenClientAcceptsWildcardWithParameter(client, "/actuator/prometheussc");
}
private void scrapeHasContentTypeText004ByDefaultWhenClientAcceptsWildcardWithParameter(WebTestClient client,
String uri) {
String expectedContentType = PrometheusTextFormatWriter.CONTENT_TYPE;
String accept = "*/*;q=0.8";
client.get()
.uri(uri)
.accept(MediaType.parseMediaType(accept))
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(MediaType.parseMediaType(expectedContentType))
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.contains("counter3_total"));
}
@WebEndpointTest
void scrapeCanProduceOpenMetrics100(WebTestClient client) {
scrapeCanProduceOpenMetrics100(client, "/actuator/prometheus");
scrapeCanProduceOpenMetrics100(client, "/actuator/prometheussc");
}
private void scrapeCanProduceOpenMetrics100(WebTestClient client, String uri) {
MediaType openMetrics = MediaType.parseMediaType(OpenMetricsTextFormatWriter.CONTENT_TYPE);
client.get()
.uri(uri)
.accept(openMetrics)
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(openMetrics)
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.contains("counter3_total"));
}
@WebEndpointTest
void scrapePrefersToProduceOpenMetrics100(WebTestClient client) {
scrapePrefersToProduceOpenMetrics100(client, "/actuator/prometheus");
scrapePrefersToProduceOpenMetrics100(client, "/actuator/prometheussc");
}
private void scrapePrefersToProduceOpenMetrics100(WebTestClient client, String uri) {
MediaType openMetrics = MediaType.parseMediaType(OpenMetricsTextFormatWriter.CONTENT_TYPE);
MediaType textPlain = MediaType.parseMediaType(PrometheusTextFormatWriter.CONTENT_TYPE);
client.get()
.uri(uri)
.accept(openMetrics, textPlain)
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(openMetrics);
}
@WebEndpointTest
void scrapeWithIncludedNames(WebTestClient client) {
scrapeWithIncludedNames(client, "/actuator/prometheus?includedNames=counter1,counter2");
scrapeWithIncludedNames(client, "/actuator/prometheussc?includedNames=counter1_total,counter2_total");
}
private void scrapeWithIncludedNames(WebTestClient client, String uri) {
client.get()
.uri(uri)
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentType(MediaType.parseMediaType(PrometheusTextFormatWriter.CONTENT_TYPE))
.expectBody(String.class)
.value((body) -> assertThat(body).contains("counter1_total")
.contains("counter2_total")
.doesNotContain("counter3_total"));
}
@SuppressWarnings({ "deprecation", "removal" })
@Configuration(proxyBeanMethods = false)
static class TestConfiguration {
@Bean
PrometheusScrapeEndpoint prometheusScrapeEndpoint(PrometheusRegistry prometheusRegistry) {
return new PrometheusScrapeEndpoint(prometheusRegistry, new Properties());
}
@Bean
CustomPrometheusScrapeEndpoint customPrometheusScrapeEndpoint(CollectorRegistry collectorRegistry) {
return new CustomPrometheusScrapeEndpoint(collectorRegistry);
}
@Bean
PrometheusRegistry prometheusRegistry() {
return new PrometheusRegistry();
}
@Bean
CollectorRegistry collectorRegistry() {
return new CollectorRegistry(true);
}
@Bean
PrometheusMeterRegistry registry(PrometheusRegistry prometheusRegistry) {
return new PrometheusMeterRegistry((k) -> null, prometheusRegistry, Clock.SYSTEM);
}
@Bean
io.micrometer.prometheus.PrometheusMeterRegistry oldRegistry(CollectorRegistry collectorRegistry) {
return new io.micrometer.prometheus.PrometheusMeterRegistry((k) -> null, collectorRegistry, Clock.SYSTEM);
}
@Bean
CompositeMeterRegistry compositeMeterRegistry(PrometheusMeterRegistry prometheusMeterRegistry,
io.micrometer.prometheus.PrometheusMeterRegistry prometheusSCMeterRegistry) {
CompositeMeterRegistry composite = new CompositeMeterRegistry();
composite.add(prometheusMeterRegistry).add(prometheusSCMeterRegistry);
Counter.builder("counter1").register(composite);
Counter.builder("counter2").register(composite);
Counter.builder("counter3").register(composite);
return composite;
}
@WebEndpoint(id = "prometheussc")
static class CustomPrometheusScrapeEndpoint extends PrometheusSimpleclientScrapeEndpoint {
CustomPrometheusScrapeEndpoint(CollectorRegistry collectorRegistry) {
super(collectorRegistry);
}
}
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2012-2024 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.autoconfigure.jooq;
import java.sql.SQLException;
import org.apache.commons.logging.LogFactory;
import org.jooq.ExecuteContext;
import org.jooq.ExecuteListener;
import org.springframework.dao.DataAccessException;
/**
* Transforms {@link SQLException} into a Spring-specific {@link DataAccessException}.
*
* @author Lukas Eder
* @author Andreas Ahlenstorf
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.5.10
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of
* {@link ExceptionTranslatorExecuteListener#DEFAULT} or
* {@link ExceptionTranslatorExecuteListener#of}
*/
@Deprecated(since = "3.3.0", forRemoval = true)
public class JooqExceptionTranslator implements ExecuteListener {
private final DefaultExceptionTranslatorExecuteListener delegate = new DefaultExceptionTranslatorExecuteListener(
LogFactory.getLog(JooqExceptionTranslator.class));
@Override
public void exception(ExecuteContext context) {
this.delegate.exception(context);
}
}

View File

@ -1,91 +0,0 @@
/*
* Copyright 2012-2024 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.autoconfigure.jooq;
import java.sql.SQLException;
import org.jooq.Configuration;
import org.jooq.ExecuteContext;
import org.jooq.SQLDialect;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.jdbc.BadSqlGrammarException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.assertArg;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
/**
* Tests for {@link JooqExceptionTranslator}
*
* @author Andy Wilkinson
* @deprecated since 3.3.0 for removal in 3.5.0
*/
@Deprecated(since = "3.3.0")
@SuppressWarnings("removal")
class JooqExceptionTranslatorTests {
private final JooqExceptionTranslator exceptionTranslator = new JooqExceptionTranslator();
@ParameterizedTest(name = "{0}")
@MethodSource
void exceptionTranslation(SQLDialect dialect, SQLException sqlException) {
ExecuteContext context = mock(ExecuteContext.class);
Configuration configuration = mock(Configuration.class);
given(context.configuration()).willReturn(configuration);
given(configuration.dialect()).willReturn(dialect);
given(context.sqlException()).willReturn(sqlException);
this.exceptionTranslator.exception(context);
then(context).should().exception(assertArg((ex) -> assertThat(ex).isInstanceOf(BadSqlGrammarException.class)));
}
@Test
void whenExceptionCannotBeTranslatedThenExecuteContextExceptionIsNotCalled() {
ExecuteContext context = mock(ExecuteContext.class);
Configuration configuration = mock(Configuration.class);
given(context.configuration()).willReturn(configuration);
given(configuration.dialect()).willReturn(SQLDialect.POSTGRES);
given(context.sqlException()).willReturn(new SQLException(null, null, 123456789));
this.exceptionTranslator.exception(context);
then(context).should(never()).exception(any());
}
static Object[] exceptionTranslation() {
return new Object[] { new Object[] { SQLDialect.DERBY, sqlException("42802") },
new Object[] { SQLDialect.H2, sqlException(42000) },
new Object[] { SQLDialect.HSQLDB, sqlException(-22) },
new Object[] { SQLDialect.MARIADB, sqlException(1054) },
new Object[] { SQLDialect.MYSQL, sqlException(1054) },
new Object[] { SQLDialect.POSTGRES, sqlException("03000") },
new Object[] { SQLDialect.SQLITE, sqlException("21000") } };
}
private static SQLException sqlException(String sqlState) {
return new SQLException(null, sqlState);
}
private static SQLException sqlException(int vendorCode) {
return new SQLException(null, null, vendorCode);
}
}

View File

@ -91,7 +91,7 @@ dependencies {
testImplementation("com.h2database:h2")
testImplementation("com.unboundid:unboundid-ldapsdk")
testImplementation("io.lettuce:lettuce-core")
testImplementation("io.micrometer:micrometer-registry-prometheus-simpleclient")
testImplementation("io.micrometer:micrometer-registry-prometheus")
testImplementation("io.projectreactor.netty:reactor-netty-http")
testImplementation("io.projectreactor:reactor-core")
testImplementation("io.projectreactor:reactor-test")

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,14 +36,14 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Moritz Halbritter
*/
@SpringBootTest
@SuppressWarnings("deprecation")
class AutoConfigureObservabilityMissingIntegrationTests {
@Test
void customizerRunsAndOnlyEnablesSimpleMeterRegistryWhenNoAnnotationPresent(
@Autowired ApplicationContext applicationContext) {
assertThat(applicationContext.getBean(MeterRegistry.class)).isInstanceOf(SimpleMeterRegistry.class);
assertThat(applicationContext.getBeansOfType(io.micrometer.prometheus.PrometheusMeterRegistry.class)).isEmpty();
assertThat(applicationContext.getBeansOfType(io.micrometer.prometheusmetrics.PrometheusMeterRegistry.class))
.isEmpty();
}
@Test

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -35,13 +35,12 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@SpringBootTest
@AutoConfigureObservability
@SuppressWarnings("deprecation")
class AutoConfigureObservabilityPresentIntegrationTests {
@Test
void customizerDoesNotDisableAvailableMeterRegistriesWhenAnnotationPresent(
@Autowired ApplicationContext applicationContext) {
assertThat(applicationContext.getBeansOfType(io.micrometer.prometheus.PrometheusMeterRegistry.class))
assertThat(applicationContext.getBeansOfType(io.micrometer.prometheusmetrics.PrometheusMeterRegistry.class))
.hasSize(1);
}

View File

@ -38,7 +38,6 @@ import org.springframework.util.Assert;
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of
* {@link org.springframework.test.context.bean.override.mockito.MockReset}
*/
@Deprecated(since = "3.4.0", forRemoval = true)
public enum MockReset {

View File

@ -17,11 +17,8 @@
package org.springframework.boot.buildpack.platform.docker;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -293,34 +290,6 @@ public class DockerApi {
}
}
/**
* Export the layers of an image as paths to layer tar files.
* @param reference the reference to export
* @param exports a consumer to receive the layer tar file paths (file can only be
* accessed during the callback)
* @throws IOException on IO error
* @since 2.7.10
* @deprecated since 3.2.6 for removal in 3.5.0 in favor of
* {@link #exportLayers(ImageReference, IOBiConsumer)}
*/
@Deprecated(since = "3.2.6", forRemoval = true)
public void exportLayerFiles(ImageReference reference, IOBiConsumer<String, Path> exports) throws IOException {
Assert.notNull(reference, "'reference' must not be null");
Assert.notNull(exports, "'exports' must not be null");
exportLayers(reference, (name, archive) -> {
Path path = Files.createTempFile("docker-export-layer-files-", null);
try {
try (OutputStream out = Files.newOutputStream(path)) {
archive.writeTo(out);
exports.accept(name, path);
}
}
finally {
Files.delete(path);
}
});
}
/**
* Export the layers of an image as {@link TarArchive TarArchives}.
* @param reference the reference to export

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -78,28 +78,6 @@ public abstract class ProgressUpdateEvent extends UpdateEvent {
this.total = total;
}
/**
* Return the current progress value.
* @return the current progress
* @deprecated since 3.3.7 for removal in 3.5.0 in favor of
* {@link #asPercentage()}
*/
@Deprecated(since = "3.3.7", forRemoval = true)
public int getCurrent() {
return (int) Long.min(this.current, Integer.MAX_VALUE);
}
/**
* Return the total progress possible value.
* @return the total progress possible
* @deprecated since 3.3.7 for removal in 3.5.0 in favor of
* {@link #asPercentage()}
*/
@Deprecated(since = "3.3.7", forRemoval = true)
public int getTotal() {
return (int) Long.min(this.total, Integer.MAX_VALUE);
}
/**
* Return the progress as a percentage.
* @return the progress percentage
@ -110,14 +88,7 @@ public abstract class ProgressUpdateEvent extends UpdateEvent {
return (percentage < 0) ? 0 : Math.min(percentage, 100);
}
/**
* Return if the progress detail is considered empty.
* @param progressDetail the progress detail to check
* @return if the progress detail is empty
* @deprecated since 3.3.7 for removal in 3.5.0
*/
@Deprecated(since = "3.3.7", forRemoval = true)
public static boolean isEmpty(ProgressDetail progressDetail) {
private static boolean isEmpty(ProgressDetail progressDetail) {
return progressDetail == null || progressDetail.current == null || progressDetail.total == null;
}

View File

@ -23,10 +23,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
@ -392,21 +389,6 @@ class DockerApiTests {
assertThat(image.getLayers()).hasSize(46);
}
@Test
@SuppressWarnings("removal")
void exportLayersWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.exportLayerFiles(null, (name, archive) -> {
})).withMessage("'reference' must not be null");
}
@Test
@SuppressWarnings("removal")
void exportLayersWhenExportsIsNullThrowsException() {
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
assertThatIllegalArgumentException().isThrownBy(() -> this.api.exportLayerFiles(reference, null))
.withMessage("'exports' must not be null");
}
@Test
void exportLayersExportsLayerTars() throws Exception {
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
@ -463,29 +445,6 @@ class DockerApiTests {
.containsExactly("/cnb/stack.toml");
}
@Test
@SuppressWarnings("removal")
void exportLayerFilesDeletesTempFiles() throws Exception {
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
URI exportUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder:base/get");
given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export.tar"));
List<Path> layerFilePaths = new ArrayList<>();
this.api.exportLayerFiles(reference, (name, path) -> layerFilePaths.add(path));
layerFilePaths.forEach((path) -> assertThat(path.toFile()).doesNotExist());
}
@Test
@SuppressWarnings("removal")
void exportLayersWithNoManifestThrowsException() throws Exception {
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
URI exportUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder:base/get");
given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export-no-manifest.tar"));
String expectedMessage = "Exported image '%s' does not contain 'index.json' or 'manifest.json'"
.formatted(reference);
assertThatIllegalStateException().isThrownBy(() -> this.api.exportLayerFiles(reference, (name, archive) -> {
})).withMessageContaining(expectedMessage);
}
@Test
void tagWhenReferenceIsNullThrowsException() {
ImageReference tag = ImageReference.of("localhost:5000/ubuntu");

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -39,20 +39,14 @@ abstract class ProgressUpdateEventTests<E extends ProgressUpdateEvent> {
}
@Test
@SuppressWarnings("removal")
void getProgressDetailsReturnsProgressDetails() {
ProgressUpdateEvent event = createEvent();
assertThat(event.getProgressDetail().getCurrent()).isOne();
assertThat(event.getProgressDetail().getTotal()).isEqualTo(2);
assertThat(event.getProgressDetail().asPercentage()).isEqualTo(50);
}
@Test
@SuppressWarnings("removal")
void getProgressDetailsReturnsProgressDetailsForLongNumbers() {
ProgressUpdateEvent event = createEvent("status", new ProgressDetail(4000000000L, 8000000000L), "progress");
assertThat(event.getProgressDetail().getCurrent()).isEqualTo(Integer.MAX_VALUE);
assertThat(event.getProgressDetail().getTotal()).isEqualTo(Integer.MAX_VALUE);
assertThat(event.getProgressDetail().asPercentage()).isEqualTo(50);
}
@ -62,27 +56,6 @@ abstract class ProgressUpdateEventTests<E extends ProgressUpdateEvent> {
assertThat(event.getProgress()).isEqualTo("progress");
}
@Test
@SuppressWarnings("removal")
void progressDetailIsEmptyWhenCurrentIsNullReturnsTrue() {
ProgressDetail detail = new ProgressDetail(null, 2L);
assertThat(ProgressDetail.isEmpty(detail)).isTrue();
}
@Test
@SuppressWarnings("removal")
void progressDetailIsEmptyWhenTotalIsNullReturnsTrue() {
ProgressDetail detail = new ProgressDetail(1L, null);
assertThat(ProgressDetail.isEmpty(detail)).isTrue();
}
@Test
@SuppressWarnings("removal")
void progressDetailIsEmptyWhenTotalAndCurrentAreNotNullReturnsFalse() {
ProgressDetail detail = new ProgressDetail(1L, 2L);
assertThat(ProgressDetail.isEmpty(detail)).isFalse();
}
protected E createEvent() {
return createEvent("status", new ProgressDetail(1L, 2L), "progress");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,8 +36,6 @@ class PullUpdateEventTests extends AbstractJsonTests {
PullImageUpdateEvent.class);
assertThat(event.getId()).isEqualTo("4f4fb700ef54");
assertThat(event.getStatus()).isEqualTo("Extracting");
assertThat(event.getProgressDetail().getCurrent()).isEqualTo(16);
assertThat(event.getProgressDetail().getTotal()).isEqualTo(32);
assertThat(event.getProgressDetail().asPercentage()).isEqualTo(50);
assertThat(event.getProgress()).isEqualTo("[==================================================>] 32B/32B");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -154,10 +154,8 @@ public abstract class BootJar extends Jar implements BootArchive {
jarmodeToolsLocation);
}
@SuppressWarnings("removal")
private boolean isIncludeJarmodeTools() {
return Boolean.TRUE.equals(this.getIncludeTools().get())
&& Boolean.TRUE.equals(this.layered.getIncludeLayerTools().get());
return Boolean.TRUE.equals(this.getIncludeTools().get());
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -128,10 +128,8 @@ public abstract class BootWar extends War implements BootArchive {
layerResolver, jarmodeToolsLocation);
}
@SuppressWarnings("removal")
private boolean isIncludeJarmodeTools() {
return Boolean.TRUE.equals(this.getIncludeTools().get())
&& Boolean.TRUE.equals(this.layered.getIncludeLayerTools().get());
return Boolean.TRUE.equals(this.getIncludeTools().get());
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -64,20 +64,8 @@ public abstract class LayeredSpec {
this.application = objects.newInstance(ApplicationSpec.class);
this.dependencies = objects.newInstance(DependenciesSpec.class);
getEnabled().convention(true);
getIncludeLayerTools().convention(true);
}
/**
* Returns whether the layer tools should be included as a dependency in the layered
* archive.
* @return whether the layer tools should be included
* @since 3.0.0
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of {@code includeTools}.
*/
@Input
@Deprecated(since = "3.3.0", forRemoval = true)
public abstract Property<Boolean> getIncludeLayerTools();
/**
* Returns whether the layers.idx should be included in the archive.
* @return whether the layers.idx should be included

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -321,18 +321,6 @@ abstract class AbstractBootArchiveIntegrationTests {
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
}
@TestTemplate
void notUpToDateWhenBuiltWithLayerToolsAndThenWithoutLayerTools() {
assertThat(this.gradleBuild.scriptProperty("layerTools", "")
.build(this.taskName)
.task(":" + this.taskName)
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(this.gradleBuild.scriptProperty("layerTools", "includeLayerTools = false")
.build(this.taskName)
.task(":" + this.taskName)
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
}
@TestTemplate
void notUpToDateWhenBuiltWithToolsAndThenWithoutTools() {
assertThat(this.gradleBuild.scriptProperty("includeTools", "")

View File

@ -626,14 +626,6 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
assertThat(entryNames).isNotEmpty().contains(this.libPath + JarModeLibrary.TOOLS.getName());
}
@Test
@SuppressWarnings("removal")
void whenArchiveIsLayeredAndIncludeLayerToolsIsFalseThenLayerToolsAreNotAddedToTheJar() throws IOException {
List<String> entryNames = getEntryNames(
createLayeredJar((configuration) -> configuration.getIncludeLayerTools().set(false)));
assertThat(entryNames).isNotEmpty().doesNotContain(this.libPath + JarModeLibrary.TOOLS.getName());
}
@Test
void whenIncludeToolsIsFalseThenToolsAreNotAddedToTheJar() throws IOException {
this.task.getIncludeTools().set(false);

View File

@ -1,11 +0,0 @@
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}
bootJar {
mainClass = 'com.example.Application'
layered {
{layerTools}
}
}

View File

@ -1,12 +0,0 @@
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
id 'war'
}
bootWar {
mainClass = 'com.example.Application'
layered {
{layerTools}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -408,19 +408,6 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
});
}
@TestTemplate
void whenJarIsRepackagedWithTheLayersEnabledAndLayerToolsExcluded(MavenBuild mavenBuild) {
mavenBuild.project("jar-layered-no-layer-tools").execute((project) -> {
File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
.hasEntryWithNameStartingWith("BOOT-INF/lib/jar-release")
.hasEntryWithNameStartingWith("BOOT-INF/lib/jar-snapshot")
.hasEntryWithNameStartingWith("BOOT-INF/layers.idx")
.doesNotHaveEntryWithNameStartingWith(
"BOOT-INF/lib/" + JarModeLibrary.TOOLS.getCoordinates().getArtifactId());
});
}
@TestTemplate
void whenJarIsRepackagedWithToolsExclude(MavenBuild mavenBuild) {
mavenBuild.project("jar-no-tools").execute((project) -> {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -184,19 +184,6 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests {
});
}
@TestTemplate
void whenWarIsRepackagedWithTheLayersEnabledAndLayerToolsExcluded(MavenBuild mavenBuild) {
mavenBuild.project("war-layered-no-layer-tools").execute((project) -> {
File repackaged = new File(project, "war/target/war-layered-0.0.1.BUILD-SNAPSHOT.war");
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("WEB-INF/classes/")
.hasEntryWithNameStartingWith("WEB-INF/lib/jar-release")
.hasEntryWithNameStartingWith("WEB-INF/lib/jar-snapshot")
.hasEntryWithNameStartingWith("WEB-INF/layers.idx")
.doesNotHaveEntryWithNameStartingWith(
"WEB-INF/lib/" + JarModeLibrary.TOOLS.getCoordinates().getArtifactId());
});
}
@TestTemplate
void whenWarIsRepackagedWithToolsExclude(MavenBuild mavenBuild) {
mavenBuild.project("war-no-tools").execute((project) -> {

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-release</artifactId>
<version>0.0.1.RELEASE</version>
<packaging>jar</packaging>
<name>jar</name>
<description>Release Jar dependency</description>
</project>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-snapshot</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jar</name>
<description>Snapshot Jar dependency</description>
</project>

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-layered</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>@java.version@</maven.compiler.source>
<maven.compiler.target>@java.version@</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<layers>
<includeLayerTools>false</includeLayerTools>
</layers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-snapshot</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-release</artifactId>
<version>0.0.1.RELEASE</version>
</dependency>
</dependencies>
</project>

View File

@ -1,24 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.test;
public class SampleApplication {
public static void main(String[] args) {
}
}

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>aggregator</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>@java.version@</maven.compiler.source>
<maven.compiler.target>@java.version@</maven.compiler.target>
</properties>
<modules>
<module>jar-snapshot</module>
<module>jar-release</module>
<module>jar</module>
</modules>
</project>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-release</artifactId>
<version>0.0.1.RELEASE</version>
<packaging>jar</packaging>
<name>jar</name>
<description>Release Jar dependency</description>
</project>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-snapshot</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jar</name>
<description>Snapshot Jar dependency</description>
</project>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>aggregator</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>@java.version@</maven.compiler.source>
<maven.compiler.target>@java.version@</maven.compiler.target>
</properties>
<modules>
<module>jar-snapshot</module>
<module>jar-release</module>
<module>war</module>
</modules>
</project>

View File

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>aggregator</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
</parent>
<artifactId>war-layered</artifactId>
<packaging>war</packaging>
<name>war</name>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<layers>
<includeLayerTools>false</includeLayerTools>
</layers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>@maven-war-plugin.version@</version>
<configuration>
<archive>
<manifestEntries>
<Not-Used>Foo</Not-Used>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>@spring-framework.version@</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>@jakarta-servlet.version@</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-snapshot</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>jar-release</artifactId>
<version>0.0.1.RELEASE</version>
</dependency>
</dependencies>
</project>

View File

@ -1,24 +0,0 @@
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.test;
public class SampleApplication {
public static void main(String[] args) {
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -180,12 +180,8 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
return packager;
}
@SuppressWarnings("removal")
private boolean getIncludeRelevantJarModeJars() {
if (!this.includeTools) {
return false;
}
return this.layers.isIncludeLayerTools();
return this.includeTools;
}
private CustomLayers getCustomLayers(File configuration) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -28,9 +28,6 @@ public class Layers {
private boolean enabled = true;
@Deprecated(since = "3.3.0", forRemoval = true)
private boolean includeLayerTools = true;
private File configuration;
/**
@ -41,16 +38,6 @@ public class Layers {
return this.enabled;
}
/**
* Whether to include the layer tools jar.
* @return true if layer tools should be included
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of {@code includeTools}.
*/
@Deprecated(since = "3.3.0", forRemoval = true)
public boolean isIncludeLayerTools() {
return this.includeLayerTools;
}
/**
* The location of the layers configuration file. If no file is provided, a default
* configuration is used with four layers: {@code application}, {@code resources},