This commit is contained in:
Phillip Webb 2022-10-25 17:04:05 -07:00
parent 0f405c06bf
commit a59b6cb1f3
31 changed files with 208 additions and 153 deletions

View File

@ -47,21 +47,8 @@ public abstract class AbstractCompositeHealthContributorConfiguration<C, I exten
*/ */
@Deprecated(since = "3.0.0", forRemoval = true) @Deprecated(since = "3.0.0", forRemoval = true)
protected AbstractCompositeHealthContributorConfiguration() { protected AbstractCompositeHealthContributorConfiguration() {
ResolvableType type = ResolvableType.forClass(AbstractCompositeHealthContributorConfiguration.class, this.indicatorFactory = new ReflectionIndicatorFactory(
getClass()); ResolvableType.forClass(AbstractCompositeHealthContributorConfiguration.class, getClass()));
Class<?> indicatorType = type.resolveGeneric(1);
Class<?> beanType = type.resolveGeneric(2);
this.indicatorFactory = (bean) -> {
try {
@SuppressWarnings("unchecked")
Constructor<I> constructor = (Constructor<I>) indicatorType.getDeclaredConstructor(beanType);
return BeanUtils.instantiateClass(constructor, bean);
}
catch (Exception ex) {
throw new IllegalStateException(
"Unable to create health indicator " + indicatorType + " for bean type " + beanType, ex);
}
};
} }
/** /**
@ -88,4 +75,34 @@ public abstract class AbstractCompositeHealthContributorConfiguration<C, I exten
return this.indicatorFactory.apply(bean); return this.indicatorFactory.apply(bean);
} }
private class ReflectionIndicatorFactory implements Function<B, I> {
private final Class<?> indicatorType;
private final Class<?> beanType;
ReflectionIndicatorFactory(ResolvableType type) {
this.indicatorType = type.resolveGeneric(1);
this.beanType = type.resolveGeneric(2);
}
@Override
public I apply(B bean) {
try {
return BeanUtils.instantiateClass(getConstructor(), bean);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create health indicator %s for bean type %s"
.formatted(this.indicatorType, this.beanType), ex);
}
}
@SuppressWarnings("unchecked")
private Constructor<I> getConstructor() throws NoSuchMethodException {
return (Constructor<I>) this.indicatorType.getDeclaredConstructor(this.beanType);
}
}
} }

View File

@ -132,8 +132,8 @@ public class ObservationAutoConfiguration {
@Bean @Bean
TracingAwareMeterObservationHandler<Observation.Context> tracingAwareMeterObservationHandler( TracingAwareMeterObservationHandler<Observation.Context> tracingAwareMeterObservationHandler(
MeterRegistry meterRegistry, Tracer tracer) { MeterRegistry meterRegistry, Tracer tracer) {
return new TracingAwareMeterObservationHandler<>(new DefaultMeterObservationHandler(meterRegistry), DefaultMeterObservationHandler delegate = new DefaultMeterObservationHandler(meterRegistry);
tracer); return new TracingAwareMeterObservationHandler<>(delegate, tracer);
} }
} }

View File

@ -16,16 +16,15 @@
package org.springframework.boot.actuate.autoconfigure.observation; package org.springframework.boot.actuate.autoconfigure.observation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObservationHandler; import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObservationHandler;
import io.micrometer.observation.ObservationRegistry.ObservationConfig; import io.micrometer.observation.ObservationRegistry.ObservationConfig;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/** /**
* Groups {@link ObservationHandler ObservationHandlers} by type. * Groups {@link ObservationHandler ObservationHandlers} by type.
@ -46,11 +45,11 @@ class ObservationHandlerGrouping {
} }
void apply(List<ObservationHandler<?>> handlers, ObservationConfig config) { void apply(List<ObservationHandler<?>> handlers, ObservationConfig config) {
Map<Class<? extends ObservationHandler>, List<ObservationHandler<?>>> groupings = new HashMap<>(); MultiValueMap<Class<? extends ObservationHandler>, ObservationHandler<?>> groupings = new LinkedMultiValueMap<>();
for (ObservationHandler<?> handler : handlers) { for (ObservationHandler<?> handler : handlers) {
Class<? extends ObservationHandler> category = findCategory(handler); Class<? extends ObservationHandler> category = findCategory(handler);
if (category != null) { if (category != null) {
groupings.computeIfAbsent(category, (c) -> new ArrayList<>()).add(handler); groupings.add(category, handler);
} }
else { else {
config.observationHandler(handler); config.observationHandler(handler);

View File

@ -73,13 +73,11 @@ class ObservationRegistryConfigurer {
} }
private void registerObservationPredicates(ObservationRegistry registry) { private void registerObservationPredicates(ObservationRegistry registry) {
this.observationPredicates.orderedStream().forEach( this.observationPredicates.orderedStream().forEach(registry.observationConfig()::observationPredicate);
(observationPredicate) -> registry.observationConfig().observationPredicate(observationPredicate));
} }
private void registerGlobalObservationConventions(ObservationRegistry registry) { private void registerGlobalObservationConventions(ObservationRegistry registry) {
this.observationConventions.orderedStream() this.observationConventions.orderedStream().forEach(registry.observationConfig()::observationConvention);
.forEach((convention) -> registry.observationConfig().observationConvention(convention));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -44,9 +44,9 @@ class ClientHttpObservationConventionAdapter implements ClientRequestObservation
@Override @Override
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) { public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) {
KeyValues keyValues = KeyValues.empty();
Iterable<Tag> tags = this.tagsProvider.getTags(context.getUriTemplate(), context.getCarrier(), Iterable<Tag> tags = this.tagsProvider.getTags(context.getUriTemplate(), context.getCarrier(),
context.getResponse()); context.getResponse());
KeyValues keyValues = KeyValues.empty();
for (Tag tag : tags) { for (Tag tag : tags) {
keyValues = keyValues.and(tag.getKey(), tag.getValue()); keyValues = keyValues.and(tag.getKey(), tag.getValue());
} }

View File

@ -21,6 +21,7 @@ import io.micrometer.core.instrument.Tag;
import io.micrometer.observation.Observation; import io.micrometer.observation.Observation;
import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider; import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider;
import org.springframework.core.Conventions;
import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientRequestObservationContext; import org.springframework.web.reactive.function.client.ClientRequestObservationContext;
import org.springframework.web.reactive.function.client.ClientRequestObservationConvention; import org.springframework.web.reactive.function.client.ClientRequestObservationConvention;
@ -32,10 +33,11 @@ import org.springframework.web.reactive.function.client.WebClient;
* *
* @author Brian Clozel * @author Brian Clozel
*/ */
@SuppressWarnings({ "deprecation", "removal" }) @SuppressWarnings("removal")
class ClientObservationConventionAdapter implements ClientRequestObservationConvention { class ClientObservationConventionAdapter implements ClientRequestObservationConvention {
private static final String URI_TEMPLATE_ATTRIBUTE = WebClient.class.getName() + ".uriTemplate"; private static final String URI_TEMPLATE_ATTRIBUTE = Conventions.getQualifiedAttributeName(WebClient.class,
"uriTemplate");
private final String metricName; private final String metricName;
@ -53,20 +55,18 @@ class ClientObservationConventionAdapter implements ClientRequestObservationConv
@Override @Override
public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) { public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) {
KeyValues keyValues = KeyValues.empty();
mutateClientRequest(context); mutateClientRequest(context);
Iterable<Tag> tags = this.tagsProvider.tags(context.getCarrier(), context.getResponse(), context.getError()); Iterable<Tag> tags = this.tagsProvider.tags(context.getCarrier(), context.getResponse(), context.getError());
KeyValues keyValues = KeyValues.empty();
for (Tag tag : tags) { for (Tag tag : tags) {
keyValues = keyValues.and(tag.getKey(), tag.getValue()); keyValues = keyValues.and(tag.getKey(), tag.getValue());
} }
return keyValues; return keyValues;
} }
/* private void mutateClientRequest(ClientRequestObservationContext context) {
* {@link WebClientExchangeTagsProvider} relies on a request attribute to get the URI // WebClientExchangeTagsProvider relies on a request attribute to get the URI
* template, we need to adapt to that. // template, we need to adapt to that.
*/
private static void mutateClientRequest(ClientRequestObservationContext context) {
ClientRequest clientRequest = ClientRequest.from(context.getCarrier()) ClientRequest clientRequest = ClientRequest.from(context.getCarrier())
.attribute(URI_TEMPLATE_ATTRIBUTE, context.getUriTemplate()).build(); .attribute(URI_TEMPLATE_ATTRIBUTE, context.getUriTemplate()).build();
context.setCarrier(clientRequest); context.setCarrier(clientRequest);

View File

@ -23,6 +23,7 @@ import io.micrometer.observation.ObservationRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Client;
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties; import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
@ -67,13 +68,14 @@ public class HttpClientObservationsAutoConfiguration {
@SuppressWarnings("removal") @SuppressWarnings("removal")
MeterFilter metricsHttpClientUriTagFilter(ObservationProperties observationProperties, MeterFilter metricsHttpClientUriTagFilter(ObservationProperties observationProperties,
MetricsProperties metricsProperties) { MetricsProperties metricsProperties) {
String metricName = metricsProperties.getWeb().getClient().getRequest().getMetricName(); Client clientProperties = metricsProperties.getWeb().getClient();
String metricName = clientProperties.getRequest().getMetricName();
String observationName = observationProperties.getHttp().getClient().getRequests().getName(); String observationName = observationProperties.getHttp().getClient().getRequests().getName();
String name = (observationName != null) ? observationName : metricName; String name = (observationName != null) ? observationName : metricName;
MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter(() -> String MeterFilter denyFilter = new OnlyOnceLoggingDenyMeterFilter(
.format("Reached the maximum number of URI tags for '%s'. Are you using 'uriVariables'?", name)); () -> "Reached the maximum number of URI tags for '%s'. Are you using 'uriVariables'?"
return MeterFilter.maximumAllowableTags(name, "uri", metricsProperties.getWeb().getClient().getMaxUriTags(), .formatted(name));
denyFilter); return MeterFilter.maximumAllowableTags(name, "uri", clientProperties.getMaxUriTags(), denyFilter);
} }
} }

View File

@ -58,8 +58,8 @@ class ServerRequestObservationConventionAdapter implements ServerRequestObservat
@Override @Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) { public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
KeyValues keyValues = KeyValues.empty();
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(context.getServerWebExchange(), context.getError()); Iterable<Tag> tags = this.tagsProvider.httpRequestTags(context.getServerWebExchange(), context.getError());
KeyValues keyValues = KeyValues.empty();
for (Tag tag : tags) { for (Tag tag : tags) {
keyValues = keyValues.and(tag.getKey(), tag.getValue()); keyValues = keyValues.and(tag.getKey(), tag.getValue());
} }

View File

@ -27,6 +27,7 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server;
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
@ -80,22 +81,26 @@ public class WebFluxObservationAutoConfiguration {
public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry, public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry,
ObjectProvider<WebFluxTagsProvider> tagConfigurer, ObjectProvider<WebFluxTagsProvider> tagConfigurer,
ObjectProvider<WebFluxTagsContributor> contributorsProvider) { ObjectProvider<WebFluxTagsContributor> contributorsProvider) {
String observationName = this.observationProperties.getHttp().getServer().getRequests().getName(); String observationName = this.observationProperties.getHttp().getServer().getRequests().getName();
String metricName = this.metricsProperties.getWeb().getServer().getRequest().getMetricName(); String metricName = this.metricsProperties.getWeb().getServer().getRequest().getMetricName();
String name = (observationName != null) ? observationName : metricName; String name = (observationName != null) ? observationName : metricName;
WebFluxTagsProvider tagsProvider = tagConfigurer.getIfAvailable(); WebFluxTagsProvider tagsProvider = tagConfigurer.getIfAvailable();
List<WebFluxTagsContributor> tagsContributors = contributorsProvider.orderedStream().toList(); List<WebFluxTagsContributor> tagsContributors = contributorsProvider.orderedStream().toList();
ServerRequestObservationConvention convention = new DefaultServerRequestObservationConvention(name); ServerRequestObservationConvention convention = extracted(name, tagsProvider, tagsContributors);
if (tagsProvider != null) {
convention = new ServerRequestObservationConventionAdapter(name, tagsProvider);
}
else if (!tagsContributors.isEmpty()) {
convention = new ServerRequestObservationConventionAdapter(name, tagsContributors);
}
return new ServerHttpObservationFilter(registry, convention); return new ServerHttpObservationFilter(registry, convention);
} }
private ServerRequestObservationConvention extracted(String name, WebFluxTagsProvider tagsProvider,
List<WebFluxTagsContributor> tagsContributors) {
if (tagsProvider != null) {
return new ServerRequestObservationConventionAdapter(name, tagsProvider);
}
if (!tagsContributors.isEmpty()) {
return new ServerRequestObservationConventionAdapter(name, tagsContributors);
}
return new DefaultServerRequestObservationConvention(name);
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class) @ConditionalOnClass(MeterRegistry.class)
@ConditionalOnBean(MeterRegistry.class) @ConditionalOnBean(MeterRegistry.class)
@ -104,11 +109,11 @@ public class WebFluxObservationAutoConfiguration {
@Bean @Bean
@Order(0) @Order(0)
MeterFilter metricsHttpServerUriTagFilter(MetricsProperties properties) { MeterFilter metricsHttpServerUriTagFilter(MetricsProperties properties) {
String metricName = properties.getWeb().getServer().getRequest().getMetricName(); Server serverProperties = properties.getWeb().getServer();
String metricName = serverProperties.getRequest().getMetricName();
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter( MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
() -> String.format("Reached the maximum number of URI tags for '%s'.", metricName)); () -> "Reached the maximum number of URI tags for '%s'.".formatted(metricName));
return MeterFilter.maximumAllowableTags(metricName, "uri", properties.getWeb().getServer().getMaxUriTags(), return MeterFilter.maximumAllowableTags(metricName, "uri", serverProperties.getMaxUriTags(), filter);
filter);
} }
} }

View File

@ -64,9 +64,9 @@ class ServerRequestObservationConventionAdapter implements ServerRequestObservat
@Override @Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) { public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
KeyValues keyValues = KeyValues.empty();
Iterable<Tag> tags = this.tagsProvider.getTags(context.getCarrier(), context.getResponse(), getHandler(context), Iterable<Tag> tags = this.tagsProvider.getTags(context.getCarrier(), context.getResponse(), getHandler(context),
context.getError()); context.getError());
KeyValues keyValues = KeyValues.empty();
for (Tag tag : tags) { for (Tag tag : tags) {
keyValues = keyValues.and(tag.getKey(), tag.getValue()); keyValues = keyValues.and(tag.getKey(), tag.getValue());
} }

View File

@ -110,9 +110,7 @@ public class BraveAutoConfiguration {
Builder builder = Tracing.newBuilder().currentTraceContext(currentTraceContext).traceId128Bit(true) Builder builder = Tracing.newBuilder().currentTraceContext(currentTraceContext).traceId128Bit(true)
.supportsJoin(false).propagationFactory(propagationFactory).sampler(sampler) .supportsJoin(false).propagationFactory(propagationFactory).sampler(sampler)
.localServiceName(applicationName); .localServiceName(applicationName);
for (SpanHandler spanHandler : spanHandlers) { spanHandlers.forEach(builder::addSpanHandler);
builder.addSpanHandler(spanHandler);
}
for (TracingCustomizer tracingCustomizer : tracingCustomizers) { for (TracingCustomizer tracingCustomizer : tracingCustomizers) {
tracingCustomizer.customize(builder); tracingCustomizer.customize(builder);
} }
@ -130,9 +128,7 @@ public class BraveAutoConfiguration {
public CurrentTraceContext braveCurrentTraceContext(List<CurrentTraceContext.ScopeDecorator> scopeDecorators, public CurrentTraceContext braveCurrentTraceContext(List<CurrentTraceContext.ScopeDecorator> scopeDecorators,
List<CurrentTraceContextCustomizer> currentTraceContextCustomizers) { List<CurrentTraceContextCustomizer> currentTraceContextCustomizers) {
ThreadLocalCurrentTraceContext.Builder builder = ThreadLocalCurrentTraceContext.newBuilder(); ThreadLocalCurrentTraceContext.Builder builder = ThreadLocalCurrentTraceContext.newBuilder();
for (ScopeDecorator scopeDecorator : scopeDecorators) { scopeDecorators.forEach(builder::addScopeDecorator);
builder.addScopeDecorator(scopeDecorator);
}
for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) { for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) {
currentTraceContextCustomizer.customize(builder); currentTraceContextCustomizer.customize(builder);
} }

View File

@ -66,8 +66,11 @@ class MeterRegistrySpanMetrics implements SpanMetrics {
@Override @Override
public void registerQueueRemainingCapacity(BlockingQueue<?> queue) { public void registerQueueRemainingCapacity(BlockingQueue<?> queue) {
this.meterRegistry.gauge("wavefront.reporter.queue.remaining_capacity", queue, this.meterRegistry.gauge("wavefront.reporter.queue.remaining_capacity", queue, this::remainingCapacity);
(q) -> (double) q.remainingCapacity()); }
private double remainingCapacity(BlockingQueue<?> queue) {
return queue.remainingCapacity();
} }
} }

View File

@ -60,9 +60,11 @@ class ZipkinConfigurations {
@Bean @Bean
@ConditionalOnMissingBean(Sender.class) @ConditionalOnMissingBean(Sender.class)
URLConnectionSender urlConnectionSender(ZipkinProperties properties) { URLConnectionSender urlConnectionSender(ZipkinProperties properties) {
return URLConnectionSender.newBuilder().connectTimeout((int) properties.getConnectTimeout().toMillis()) URLConnectionSender.Builder builder = URLConnectionSender.newBuilder();
.readTimeout((int) properties.getReadTimeout().toMillis()).endpoint(properties.getEndpoint()) builder.connectTimeout((int) properties.getConnectTimeout().toMillis());
.build(); builder.readTimeout((int) properties.getReadTimeout().toMillis());
builder.endpoint(properties.getEndpoint());
return builder.build();
} }
} }
@ -78,7 +80,7 @@ class ZipkinConfigurations {
ObjectProvider<ZipkinRestTemplateBuilderCustomizer> customizers) { ObjectProvider<ZipkinRestTemplateBuilderCustomizer> customizers) {
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder() RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder()
.setConnectTimeout(properties.getConnectTimeout()).setReadTimeout(properties.getReadTimeout()); .setConnectTimeout(properties.getConnectTimeout()).setReadTimeout(properties.getReadTimeout());
customizers.orderedStream().forEach((c) -> c.customize(restTemplateBuilder)); customizers.orderedStream().forEach((customizer) -> customizer.customize(restTemplateBuilder));
return new ZipkinRestTemplateSender(properties.getEndpoint(), restTemplateBuilder.build()); return new ZipkinRestTemplateSender(properties.getEndpoint(), restTemplateBuilder.build());
} }
@ -94,7 +96,7 @@ class ZipkinConfigurations {
ZipkinWebClientSender webClientSender(ZipkinProperties properties, ZipkinWebClientSender webClientSender(ZipkinProperties properties,
ObjectProvider<ZipkinWebClientBuilderCustomizer> customizers) { ObjectProvider<ZipkinWebClientBuilderCustomizer> customizers) {
WebClient.Builder builder = WebClient.builder(); WebClient.Builder builder = WebClient.builder();
customizers.orderedStream().forEach((c) -> c.customize(builder)); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return new ZipkinWebClientSender(properties.getEndpoint(), builder.build()); return new ZipkinWebClientSender(properties.getEndpoint(), builder.build());
} }

View File

@ -20,6 +20,7 @@ import reactor.core.publisher.Mono;
import zipkin2.Call; import zipkin2.Call;
import zipkin2.Callback; import zipkin2.Callback;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
@ -73,8 +74,12 @@ class ZipkinWebClientSender extends HttpSender {
} }
private Mono<ResponseEntity<Void>> sendRequest() { private Mono<ResponseEntity<Void>> sendRequest() {
return this.webClient.post().uri(this.endpoint).headers((headers) -> headers.addAll(getDefaultHeaders())) return this.webClient.post().uri(this.endpoint).headers(this::addDefaultHeaders).bodyValue(getBody())
.bodyValue(getBody()).retrieve().toBodilessEntity(); .retrieve().toBodilessEntity();
}
private void addDefaultHeaders(HttpHeaders headers) {
headers.addAll(getDefaultHeaders());
} }
} }

View File

@ -48,12 +48,12 @@ public class WavefrontSenderConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
public WavefrontSender wavefrontSender(WavefrontProperties properties) { public WavefrontSender wavefrontSender(WavefrontProperties properties) {
Builder builder = new Builder(properties.getEffectiveUri().toString(), properties.getApiTokenOrThrow()); Builder builder = new Builder(properties.getEffectiveUri().toString(), properties.getApiTokenOrThrow());
PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
WavefrontProperties.Sender sender = properties.getSender(); WavefrontProperties.Sender sender = properties.getSender();
mapper.from(sender.getMaxQueueSize()).to(builder::maxQueueSize); map.from(sender.getMaxQueueSize()).to(builder::maxQueueSize);
mapper.from(sender.getFlushInterval()).asInt(Duration::getSeconds).to(builder::flushIntervalSeconds); map.from(sender.getFlushInterval()).asInt(Duration::getSeconds).to(builder::flushIntervalSeconds);
mapper.from(sender.getMessageSize()).asInt(DataSize::toBytes).to(builder::messageSizeBytes); map.from(sender.getMessageSize()).asInt(DataSize::toBytes).to(builder::messageSizeBytes);
mapper.from(sender.getBatchSize()).to(builder::batchSize); map.from(sender.getBatchSize()).to(builder::batchSize);
return builder.build(); return builder.build();
} }

View File

@ -43,6 +43,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
@ -208,10 +209,7 @@ public class HeapDumpWebEndpoint {
@Override @Override
public File dumpHeap(Boolean live) throws IOException, InterruptedException { public File dumpHeap(Boolean live) throws IOException, InterruptedException {
if (live != null) { Assert.isNull(live, "OpenJ9DiagnosticsMXBean does not support live parameter when dumping the heap");
throw new IllegalArgumentException(
"OpenJ9DiagnosticsMXBean does not support live parameter when dumping the heap");
}
return new File( return new File(
(String) ReflectionUtils.invokeMethod(this.dumpHeapMethod, this.diagnosticMXBean, "heap", null)); (String) ReflectionUtils.invokeMethod(this.dumpHeapMethod, this.diagnosticMXBean, "heap", null));
} }

View File

@ -99,11 +99,11 @@ class RabbitStreamConfiguration {
static EnvironmentBuilder configure(EnvironmentBuilder builder, RabbitProperties properties) { static EnvironmentBuilder configure(EnvironmentBuilder builder, RabbitProperties properties) {
builder.lazyInitialization(true); builder.lazyInitialization(true);
RabbitProperties.Stream stream = properties.getStream(); RabbitProperties.Stream stream = properties.getStream();
PropertyMapper mapper = PropertyMapper.get(); PropertyMapper map = PropertyMapper.get();
mapper.from(stream.getHost()).to(builder::host); map.from(stream.getHost()).to(builder::host);
mapper.from(stream.getPort()).to(builder::port); map.from(stream.getPort()).to(builder::port);
mapper.from(stream.getUsername()).as(withFallback(properties::getUsername)).whenNonNull().to(builder::username); map.from(stream.getUsername()).as(withFallback(properties::getUsername)).whenNonNull().to(builder::username);
mapper.from(stream.getPassword()).as(withFallback(properties::getPassword)).whenNonNull().to(builder::password); map.from(stream.getPassword()).as(withFallback(properties::getPassword)).whenNonNull().to(builder::password);
return builder; return builder;
} }

View File

@ -52,9 +52,9 @@ class MongoDataConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
MongoMappingContext mongoMappingContext(MongoProperties properties, MongoCustomConversions conversions, MongoMappingContext mongoMappingContext(MongoProperties properties, MongoCustomConversions conversions,
MongoManagedTypes managedTypes) { MongoManagedTypes managedTypes) {
PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
MongoMappingContext context = new MongoMappingContext(); MongoMappingContext context = new MongoMappingContext();
mapper.from(properties.isAutoIndexCreation()).to(context::setAutoIndexCreation); map.from(properties.isAutoIndexCreation()).to(context::setAutoIndexCreation);
context.setManagedTypes(managedTypes); context.setManagedTypes(managedTypes);
Class<?> strategyClass = properties.getFieldNamingStrategy(); Class<?> strategyClass = properties.getFieldNamingStrategy();
if (strategyClass != null) { if (strategyClass != null) {

View File

@ -24,6 +24,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import org.flywaydb.core.api.FlywayException; import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.api.Location; import org.flywaydb.core.api.Location;
@ -57,7 +58,7 @@ class NativeImageResourceProvider implements ResourceProvider {
private final boolean failOnMissingLocations; private final boolean failOnMissingLocations;
private final List<ResourceWithLocation> resources = new ArrayList<>(); private final List<LocatedResource> locatedResources = new ArrayList<>();
private final Lock lock = new ReentrantLock(); private final Lock lock = new ReentrantLock();
@ -93,14 +94,21 @@ class NativeImageResourceProvider implements ResourceProvider {
return this.scanner.getResources(prefix, suffixes); return this.scanner.getResources(prefix, suffixes);
} }
ensureInitialized(); ensureInitialized();
List<LoadableResource> result = new ArrayList<>(this.scanner.getResources(prefix, suffixes)); Predicate<LocatedResource> matchesPrefixAndSuffixes = (locatedResource) -> StringUtils
this.resources.stream().filter((r) -> StringUtils.startsAndEndsWith(r.resource.getFilename(), prefix, suffixes)) .startsAndEndsWith(locatedResource.resource.getFilename(), prefix, suffixes);
.map((r) -> (LoadableResource) new ClassPathResource(r.location(), List<LoadableResource> result = new ArrayList<>();
r.location().getPath() + "/" + r.resource().getFilename(), this.classLoader, this.encoding)) result.addAll(this.scanner.getResources(prefix, suffixes));
this.locatedResources.stream().filter(matchesPrefixAndSuffixes).map(this::asClassPathResource)
.forEach(result::add); .forEach(result::add);
return result; return result;
} }
private ClassPathResource asClassPathResource(LocatedResource locatedResource) {
Location location = locatedResource.location();
String fileNameWithAbsolutePath = location.getPath() + "/" + locatedResource.resource().getFilename();
return new ClassPathResource(location, fileNameWithAbsolutePath, this.classLoader, this.encoding);
}
private void ensureInitialized() { private void ensureInitialized() {
this.lock.lock(); this.lock.lock();
try { try {
@ -127,20 +135,24 @@ class NativeImageResourceProvider implements ResourceProvider {
} }
continue; continue;
} }
Resource[] resources; Resource[] resources = getResources(resolver, location, root);
try {
resources = resolver.getResources(root.getURI() + "/*");
}
catch (IOException ex) {
throw new UncheckedIOException("Failed to list resources for " + location.getDescriptor(), ex);
}
for (Resource resource : resources) { for (Resource resource : resources) {
this.resources.add(new ResourceWithLocation(resource, location)); this.locatedResources.add(new LocatedResource(resource, location));
} }
} }
} }
private record ResourceWithLocation(Resource resource, Location location) { private Resource[] getResources(PathMatchingResourcePatternResolver resolver, Location location, Resource root) {
try {
return resolver.getResources(root.getURI() + "/*");
}
catch (IOException ex) {
throw new UncheckedIOException("Failed to list resources for " + location.getDescriptor(), ex);
}
}
private record LocatedResource(Resource resource, Location location) {
} }
} }

View File

@ -52,8 +52,8 @@ class HazelcastSessionConfiguration {
SessionRepositoryCustomizer<HazelcastIndexedSessionRepository> springBootSessionRepositoryCustomizer( SessionRepositoryCustomizer<HazelcastIndexedSessionRepository> springBootSessionRepositoryCustomizer(
SessionProperties sessionProperties, HazelcastSessionProperties hazelcastSessionProperties, SessionProperties sessionProperties, HazelcastSessionProperties hazelcastSessionProperties,
ServerProperties serverProperties) { ServerProperties serverProperties) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
return (sessionRepository) -> { return (sessionRepository) -> {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(sessionProperties.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout())) map.from(sessionProperties.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout()))
.to(sessionRepository::setDefaultMaxInactiveInterval); .to(sessionRepository::setDefaultMaxInactiveInterval);
map.from(hazelcastSessionProperties::getMapName).to(sessionRepository::setSessionMapName); map.from(hazelcastSessionProperties::getMapName).to(sessionRepository::setSessionMapName);

View File

@ -67,8 +67,8 @@ class JdbcSessionConfiguration {
SessionRepositoryCustomizer<JdbcIndexedSessionRepository> springBootSessionRepositoryCustomizer( SessionRepositoryCustomizer<JdbcIndexedSessionRepository> springBootSessionRepositoryCustomizer(
SessionProperties sessionProperties, JdbcSessionProperties jdbcSessionProperties, SessionProperties sessionProperties, JdbcSessionProperties jdbcSessionProperties,
ServerProperties serverProperties) { ServerProperties serverProperties) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
return (sessionRepository) -> { return (sessionRepository) -> {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(sessionProperties.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout())) map.from(sessionProperties.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout()))
.to(sessionRepository::setDefaultMaxInactiveInterval); .to(sessionRepository::setDefaultMaxInactiveInterval);
map.from(jdbcSessionProperties::getTableName).to(sessionRepository::setTableName); map.from(jdbcSessionProperties::getTableName).to(sessionRepository::setTableName);

View File

@ -50,8 +50,8 @@ class MongoReactiveSessionConfiguration {
ReactiveSessionRepositoryCustomizer<ReactiveMongoSessionRepository> springBootSessionRepositoryCustomizer( ReactiveSessionRepositoryCustomizer<ReactiveMongoSessionRepository> springBootSessionRepositoryCustomizer(
SessionProperties sessionProperties, MongoSessionProperties mongoSessionProperties, SessionProperties sessionProperties, MongoSessionProperties mongoSessionProperties,
ServerProperties serverProperties) { ServerProperties serverProperties) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
return (sessionRepository) -> { return (sessionRepository) -> {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(sessionProperties.determineTimeout(() -> serverProperties.getReactive().getSession().getTimeout())) map.from(sessionProperties.determineTimeout(() -> serverProperties.getReactive().getSession().getTimeout()))
.to(sessionRepository::setDefaultMaxInactiveInterval); .to(sessionRepository::setDefaultMaxInactiveInterval);
map.from(mongoSessionProperties::getCollectionName).to(sessionRepository::setCollectionName); map.from(mongoSessionProperties::getCollectionName).to(sessionRepository::setCollectionName);

View File

@ -50,8 +50,8 @@ class RedisReactiveSessionConfiguration {
ReactiveSessionRepositoryCustomizer<ReactiveRedisSessionRepository> springBootSessionRepositoryCustomizer( ReactiveSessionRepositoryCustomizer<ReactiveRedisSessionRepository> springBootSessionRepositoryCustomizer(
SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties, SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties,
ServerProperties serverProperties) { ServerProperties serverProperties) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
return (sessionRepository) -> { return (sessionRepository) -> {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(sessionProperties.determineTimeout(() -> serverProperties.getReactive().getSession().getTimeout())) map.from(sessionProperties.determineTimeout(() -> serverProperties.getReactive().getSession().getTimeout()))
.to(sessionRepository::setDefaultMaxInactiveInterval); .to(sessionRepository::setDefaultMaxInactiveInterval);
map.from(redisSessionProperties::getNamespace).to(sessionRepository::setRedisKeyNamespace); map.from(redisSessionProperties::getNamespace).to(sessionRepository::setRedisKeyNamespace);

View File

@ -67,11 +67,11 @@ class RedisSessionConfiguration {
String cleanupCron = redisSessionProperties.getCleanupCron(); String cleanupCron = redisSessionProperties.getCleanupCron();
if (cleanupCron != null) { if (cleanupCron != null) {
throw new InvalidConfigurationPropertyValueException("spring.session.redis.cleanup-cron", cleanupCron, throw new InvalidConfigurationPropertyValueException("spring.session.redis.cleanup-cron", cleanupCron,
"Cron-based cleanup is only supported when spring.session.redis.repository-type is set to " "Cron-based cleanup is only supported when "
+ "indexed."); + "spring.session.redis.repository-type is set to indexed.");
} }
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
return (sessionRepository) -> { return (sessionRepository) -> {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(sessionProperties map.from(sessionProperties
.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout())) .determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout()))
.to(sessionRepository::setDefaultMaxInactiveInterval); .to(sessionRepository::setDefaultMaxInactiveInterval);
@ -101,8 +101,8 @@ class RedisSessionConfiguration {
SessionRepositoryCustomizer<RedisIndexedSessionRepository> springBootSessionRepositoryCustomizer( SessionRepositoryCustomizer<RedisIndexedSessionRepository> springBootSessionRepositoryCustomizer(
SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties, SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties,
ServerProperties serverProperties) { ServerProperties serverProperties) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
return (sessionRepository) -> { return (sessionRepository) -> {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(sessionProperties map.from(sessionProperties
.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout())) .determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout()))
.to(sessionRepository::setDefaultMaxInactiveInterval); .to(sessionRepository::setDefaultMaxInactiveInterval);

View File

@ -1,6 +1,5 @@
[[actuator.tracing]] [[actuator.tracing]]
== HTTP Tracing == HTTP Tracing
You can enable HTTP Tracing by providing a bean of type `HttpTraceRepository` in your application's configuration. You can enable HTTP Tracing by providing a bean of type `HttpTraceRepository` in your application's configuration.
For convenience, Spring Boot offers `InMemoryHttpTraceRepository`, which stores traces for the last 100 (the default) request-response exchanges. For convenience, Spring Boot offers `InMemoryHttpTraceRepository`, which stores traces for the last 100 (the default) request-response exchanges.
`InMemoryHttpTraceRepository` is limited compared to other tracing solutions, and we recommend using it only for development environments. `InMemoryHttpTraceRepository` is limited compared to other tracing solutions, and we recommend using it only for development environments.
@ -9,8 +8,9 @@ Alternatively, you can create your own `HttpTraceRepository`.
You can use the `httptrace` endpoint to obtain information about the request-response exchanges that are stored in the `HttpTraceRepository`. You can use the `httptrace` endpoint to obtain information about the request-response exchanges that are stored in the `HttpTraceRepository`.
[[actuator.tracing.custom]]
=== Custom HTTP tracing
[[actuator.tracing.custom]]
=== Custom HTTP Tracing
To customize the items that are included in each trace, use the configprop:management.trace.http.include[] configuration property. To customize the items that are included in each trace, use the configprop:management.trace.http.include[] configuration property.
For advanced customization, consider registering your own `HttpExchangeTracer` implementation. For advanced customization, consider registering your own `HttpExchangeTracer` implementation.

View File

@ -1,22 +1,23 @@
[[actuator.micrometer-tracing]] [[actuator.micrometer-tracing]]
== Tracing == Tracing
Spring Boot Actuator provides dependency management and auto-configuration for https://micrometer.io/docs/tracing[Micrometer Tracing], a facade for popular tracer libraries. Spring Boot Actuator provides dependency management and auto-configuration for https://micrometer.io/docs/tracing[Micrometer Tracing], a facade for popular tracer libraries.
Micrometer Tracing hooks into Micrometer's `ObservationHandler`, which means a https://micrometer.io/docs/tracing#_glossary[span] is reported for every completed observation. Micrometer Tracing hooks into Micrometer's `ObservationHandler`, which means a https://micrometer.io/docs/tracing#_glossary[span] is reported for every completed observation.
TIP: To learn more about Micrometer Tracing capabilities, see its https://micrometer.io/docs/tracing[reference documentation]. TIP: To learn more about Micrometer Tracing capabilities, see its https://micrometer.io/docs/tracing[reference documentation].
[[actuator.micrometer-tracing.tracers]]
=== Supported tracers
[[actuator.micrometer-tracing.tracers]]
=== Supported Tracers
Spring Boot ships auto-configuration for the following tracers: Spring Boot ships auto-configuration for the following tracers:
* https://opentelemetry.io/[OpenTelemetry] with https://zipkin.io/[Zipkin] or https://docs.wavefront.com/[Wavefront] * https://opentelemetry.io/[OpenTelemetry] with https://zipkin.io/[Zipkin] or https://docs.wavefront.com/[Wavefront]
* https://github.com/openzipkin/brave[OpenZipkin Brave] with https://zipkin.io/[Zipkin] or https://docs.wavefront.com/[Wavefront] * https://github.com/openzipkin/brave[OpenZipkin Brave] with https://zipkin.io/[Zipkin] or https://docs.wavefront.com/[Wavefront]
[[actuator.micrometer-tracing.getting-started]] [[actuator.micrometer-tracing.getting-started]]
=== Getting Started === Getting Started
We need an example application that we can use to getting started with tracing. We need an example application that we can use to getting started with tracing.
For our purposes, the simple "`Hello World!`" web application that's covered in the "`<<getting-started#getting-started.first-application>>`" section will suffice. For our purposes, the simple "`Hello World!`" web application that's covered in the "`<<getting-started#getting-started.first-application>>`" section will suffice.
We're going to use the OpenTelemetry tracer with Zipkin as trace backend. We're going to use the OpenTelemetry tracer with Zipkin as trace backend.
@ -64,42 +65,52 @@ Press the "Show" button to see the details of that trace.
TIP: You can include the current trace and span id in the logs by setting the `logging.pattern.level` property to `%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]` TIP: You can include the current trace and span id in the logs by setting the `logging.pattern.level` property to `%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]`
[[actuator.micrometer-tracing.tracer-implementations]]
=== Tracer implementations
[[actuator.micrometer-tracing.tracer-implementations]]
=== Tracer Implementations
As Micrometer Tracer supports multiple tracer implementations, there are multiple dependency combinations possible with Spring Boot. As Micrometer Tracer supports multiple tracer implementations, there are multiple dependency combinations possible with Spring Boot.
All tracer implementations need the `org.springframework.boot:spring-boot-starter-actuator` dependency. All tracer implementations need the `org.springframework.boot:spring-boot-starter-actuator` dependency.
[[actuator.micrometer-tracing.tracer-implementations.otel-zipkin]] [[actuator.micrometer-tracing.tracer-implementations.otel-zipkin]]
==== OpenTelemetry with Zipkin ==== OpenTelemetry With Zipkin
* `io.micrometer:micrometer-tracing-bridge-otel` - which is needed to bride the Micrometer Observation API to OpenTelemetry. * `io.micrometer:micrometer-tracing-bridge-otel` - which is needed to bride the Micrometer Observation API to OpenTelemetry.
* `io.opentelemetry:opentelemetry-exporter-zipkin` - which is needed to report traces to Zipkin. * `io.opentelemetry:opentelemetry-exporter-zipkin` - which is needed to report traces to Zipkin.
[[actuator.micrometer-tracing.tracer-implementations.otel-wavefront]] [[actuator.micrometer-tracing.tracer-implementations.otel-wavefront]]
==== OpenTelemetry with Wavefront ==== OpenTelemetry With Wavefront
* `io.micrometer:micrometer-tracing-bridge-otel` - which is needed to bride the Micrometer Observation API to OpenTelemetry. * `io.micrometer:micrometer-tracing-bridge-otel` - which is needed to bride the Micrometer Observation API to OpenTelemetry.
* `io.micrometer:micrometer-tracing-reporter-wavefront` - which is needed to report traces to Wavefront. * `io.micrometer:micrometer-tracing-reporter-wavefront` - which is needed to report traces to Wavefront.
[[actuator.micrometer-tracing.tracer-implementations.brave-zipkin]] [[actuator.micrometer-tracing.tracer-implementations.brave-zipkin]]
==== OpenZipkin Brave with Zipkin ==== OpenZipkin Brave With Zipkin
* `io.micrometer:micrometer-tracing-bridge-brave` - which is needed to bridge the Micrometer Observation API to Brave. * `io.micrometer:micrometer-tracing-bridge-brave` - which is needed to bridge the Micrometer Observation API to Brave.
* `io.zipkin.reporter2:zipkin-reporter-brave` - which is needed to report traces to Zipkin. * `io.zipkin.reporter2:zipkin-reporter-brave` - which is needed to report traces to Zipkin.
NOTE: If your project doesn't use Spring MVC or Spring WebFlux, the `io.zipkin.reporter2:zipkin-sender-urlconnection` dependency is needed, too. NOTE: If your project doesn't use Spring MVC or Spring WebFlux, the `io.zipkin.reporter2:zipkin-sender-urlconnection` dependency is needed, too.
[[actuator.micrometer-tracing.tracer-implementations.brave-wavefront]] [[actuator.micrometer-tracing.tracer-implementations.brave-wavefront]]
==== OpenZipkin Brave with Wavefront ==== OpenZipkin Brave With Wavefront
* `io.micrometer:micrometer-tracing-bridge-brave` - which is needed to bridge the Micrometer Observation API to Brave. * `io.micrometer:micrometer-tracing-bridge-brave` - which is needed to bridge the Micrometer Observation API to Brave.
* `io.micrometer:micrometer-tracing-reporter-wavefront` - which is needed to report traces to Wavefront. * `io.micrometer:micrometer-tracing-reporter-wavefront` - which is needed to report traces to Wavefront.
[[actuator.micrometer-tracing.creating-spans]]
=== Creating custom spans
[[actuator.micrometer-tracing.creating-spans]]
=== Creating Custom Spans
You can create your own spans by starting an observation. You can create your own spans by starting an observation.
For this, inject `ObservationRegistry` into your component: For this, inject `ObservationRegistry` into your component:

View File

@ -67,11 +67,21 @@ public class MockServerRestTemplateCustomizer implements RestTemplateCustomizer
this(SimpleRequestExpectationManager::new); this(SimpleRequestExpectationManager::new);
} }
/**
* Crate a new {@link MockServerRestTemplateCustomizer} instance.
* @param expectationManager the expectation manager class to use
*/
public MockServerRestTemplateCustomizer(Class<? extends RequestExpectationManager> expectationManager) { public MockServerRestTemplateCustomizer(Class<? extends RequestExpectationManager> expectationManager) {
this(() -> BeanUtils.instantiateClass(expectationManager)); this(() -> BeanUtils.instantiateClass(expectationManager));
Assert.notNull(expectationManager, "ExpectationManager must not be null"); Assert.notNull(expectationManager, "ExpectationManager must not be null");
} }
/**
* Crate a new {@link MockServerRestTemplateCustomizer} instance.
* @param expectationManagerSupplier a supplier that provides the
* {@link RequestExpectationManager} to use
* @since 3.0.0
*/
public MockServerRestTemplateCustomizer(Supplier<? extends RequestExpectationManager> expectationManagerSupplier) { public MockServerRestTemplateCustomizer(Supplier<? extends RequestExpectationManager> expectationManagerSupplier) {
Assert.notNull(expectationManagerSupplier, "ExpectationManagerSupplier must not be null"); Assert.notNull(expectationManagerSupplier, "ExpectationManagerSupplier must not be null");
this.expectationManagerSupplier = expectationManagerSupplier; this.expectationManagerSupplier = expectationManagerSupplier;

View File

@ -76,11 +76,14 @@ class NativeImagePluginAction implements PluginApplicationAction {
} }
private Iterable<Configuration> removeDevelopmentOnly(Set<Configuration> configurations) { private Iterable<Configuration> removeDevelopmentOnly(Set<Configuration> configurations) {
return configurations.stream().filter(( return configurations.stream().filter(this::isNotDevelopmentOnly)
configuration) -> !SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME.equals(configuration.getName()))
.collect(Collectors.toCollection(LinkedHashSet::new)); .collect(Collectors.toCollection(LinkedHashSet::new));
} }
private boolean isNotDevelopmentOnly(Configuration configuration) {
return !SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME.equals(configuration.getName());
}
private void configureTestNativeBinaryClasspath(SourceSetContainer sourceSets, GraalVMExtension graalVmExtension, private void configureTestNativeBinaryClasspath(SourceSetContainer sourceSets, GraalVMExtension graalVmExtension,
String sourceSetName) { String sourceSetName) {
SourceSetOutput output = sourceSets.getByName(SpringBootAotPlugin.AOT_TEST_SOURCE_SET_NAME).getOutput(); SourceSetOutput output = sourceSets.getByName(SpringBootAotPlugin.AOT_TEST_SOURCE_SET_NAME).getOutput();

View File

@ -38,6 +38,7 @@ import org.springframework.util.StringUtils;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Moritz Halbritter
*/ */
class StartupInfoLogger { class StartupInfoLogger {

View File

@ -222,44 +222,39 @@ class SpringBootJoranConfigurator extends JoranConfigurator {
} }
private Class<?> determineType(Model model, Supplier<Object> parentSupplier) { private Class<?> determineType(Model model, Supplier<Object> parentSupplier) {
String className = null; String className = (model instanceof ComponentModel componentModel) ? componentModel.getClassName() : null;
if (model instanceof ComponentModel) {
className = ((ComponentModel) model).getClassName();
}
if (className == null) {
String tag = model.getTag();
if (tag != null) {
className = this.modelInterpretationContext.getDefaultNestedComponentRegistry()
.findDefaultComponentTypeByTag(tag);
if (className == null) {
Class<?> type = inferTypeFromParent(parentSupplier, tag);
if (type != null) {
return type;
}
}
}
}
if (className != null) { if (className != null) {
className = this.modelInterpretationContext.getImport(className); return loadImportType(className);
return loadComponentType(className); }
String tag = model.getTag();
if (tag != null) {
className = this.modelInterpretationContext.getDefaultNestedComponentRegistry()
.findDefaultComponentTypeByTag(tag);
if (className != null) {
return loadImportType(className);
}
return inferTypeFromParent(parentSupplier, tag);
} }
return null; return null;
} }
private Class<?> loadImportType(String className) {
return loadComponentType(this.modelInterpretationContext.getImport(className));
}
private Class<?> inferTypeFromParent(Supplier<Object> parentSupplier, String tag) { private Class<?> inferTypeFromParent(Supplier<Object> parentSupplier, String tag) {
Object parent = parentSupplier.get(); Object parent = parentSupplier.get();
if (parent != null) { if (parent != null) {
try { try {
Class<?> typeFromSetter = new PropertySetter( PropertySetter propertySetter = new PropertySetter(
this.modelInterpretationContext.getBeanDescriptionCache(), parent) this.modelInterpretationContext.getBeanDescriptionCache(), parent);
.getClassNameViaImplicitRules(tag, AggregationType.AS_COMPLEX_PROPERTY, Class<?> typeFromPropertySetter = propertySetter.getClassNameViaImplicitRules(tag,
this.modelInterpretationContext.getDefaultNestedComponentRegistry()); AggregationType.AS_COMPLEX_PROPERTY,
if (typeFromSetter != null) { this.modelInterpretationContext.getDefaultNestedComponentRegistry());
return typeFromSetter; return typeFromPropertySetter;
}
} }
catch (Exception ex) { catch (Exception ex) {
// Continue return null;
} }
} }
return null; return null;

View File

@ -234,7 +234,6 @@ class LogbackConfigurationAotContributionTests {
public static class Outer { public static class Outer {
public void setImplementation(Implementation implementation) { public void setImplementation(Implementation implementation) {
} }
} }
@ -243,7 +242,6 @@ class LogbackConfigurationAotContributionTests {
@DefaultClass(Implementation.class) @DefaultClass(Implementation.class)
public void setContract(Contract contract) { public void setContract(Contract contract) {
} }
} }