Commit 01f7805c authored by Phillip Webb's avatar Phillip Webb

Merge branch '2.0.x'

parents ff35d141 b1399db9
......@@ -17,6 +17,7 @@
package org.springframework.boot.actuate.metrics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
......@@ -81,15 +82,15 @@ public class MetricsEndpoint {
public MetricResponse metric(@Selector String requiredMetricName,
@Nullable List<String> tag) {
List<Tag> tags = parseTags(tag);
List<Meter> meters = new ArrayList<>();
collectMeters(meters, this.registry, requiredMetricName, tags);
Collection<Meter> meters = findFirstMatchingMeters(this.registry,
requiredMetricName, tags);
if (meters.isEmpty()) {
return null;
}
Map<Statistic, Double> samples = getSamples(meters);
Map<String, Set<String>> availableTags = getAvailableTags(meters);
tags.forEach((t) -> availableTags.remove(t.getKey()));
Meter.Id meterId = meters.get(0).getId();
Meter.Id meterId = meters.iterator().next().getId();
return new MetricResponse(requiredMetricName, meterId.getDescription(),
meterId.getBaseUnit(), asList(samples, Sample::new),
asList(availableTags, AvailableTag::new));
......@@ -112,18 +113,23 @@ public class MetricsEndpoint {
return Tag.of(parts[0], parts[1]);
}
private void collectMeters(List<Meter> meters, MeterRegistry registry, String name,
private Collection<Meter> findFirstMatchingMeters(MeterRegistry registry, String name,
Iterable<Tag> tags) {
if (registry instanceof CompositeMeterRegistry) {
((CompositeMeterRegistry) registry).getRegistries()
.forEach((member) -> collectMeters(meters, member, name, tags));
}
else {
meters.addAll(registry.find(name).tags(tags).meters());
return findFirstMatchingMeters((CompositeMeterRegistry) registry, name, tags);
}
return registry.find(name).tags(tags).meters();
}
private Collection<Meter> findFirstMatchingMeters(CompositeMeterRegistry composite,
String name, Iterable<Tag> tags) {
return composite.getRegistries().stream()
.map((registry) -> findFirstMatchingMeters(registry, name, tags))
.filter((matching) -> !matching.isEmpty()).findFirst()
.orElse(Collections.emptyList());
}
private Map<Statistic, Double> getSamples(List<Meter> meters) {
private Map<Statistic, Double> getSamples(Collection<Meter> meters) {
Map<Statistic, Double> samples = new LinkedHashMap<>();
meters.forEach((meter) -> mergeMeasurements(samples, meter));
return samples;
......@@ -138,7 +144,7 @@ public class MetricsEndpoint {
return Statistic.MAX.equals(statistic) ? Double::max : Double::sum;
}
private Map<String, Set<String>> getAvailableTags(List<Meter> meters) {
private Map<String, Set<String>> getAvailableTags(Collection<Meter> meters) {
Map<String, Set<String>> availableTags = new HashMap<>();
meters.forEach((meter) -> mergeAvailableTags(availableTags, meter));
return availableTags;
......
......@@ -91,6 +91,42 @@ public class MetricsEndpointTests {
assertThat(getCount(response)).hasValue(4.0);
}
@Test
public void findFirstMatchingMetersFromNestedRegistries() {
CompositeMeterRegistry composite = new CompositeMeterRegistry();
SimpleMeterRegistry firstLevel0 = new SimpleMeterRegistry();
CompositeMeterRegistry firstLevel1 = new CompositeMeterRegistry();
SimpleMeterRegistry secondLevel = new SimpleMeterRegistry();
composite.add(firstLevel0);
composite.add(firstLevel1);
firstLevel1.add(secondLevel);
secondLevel.counter("cache", "result", "hit", "host", "1").increment(2);
secondLevel.counter("cache", "result", "miss", "host", "1").increment(2);
secondLevel.counter("cache", "result", "hit", "host", "2").increment(2);
MetricsEndpoint endpoint = new MetricsEndpoint(composite);
MetricsEndpoint.MetricResponse response = endpoint.metric("cache",
Collections.emptyList());
assertThat(response.getName()).isEqualTo("cache");
assertThat(availableTagKeys(response)).containsExactly("result", "host");
assertThat(getCount(response)).hasValue(6.0);
response = endpoint.metric("cache", Collections.singletonList("result:hit"));
assertThat(availableTagKeys(response)).containsExactly("host");
assertThat(getCount(response)).hasValue(4.0);
}
@Test
public void matchingMeterNotFoundInNestedRegistries() {
CompositeMeterRegistry composite = new CompositeMeterRegistry();
CompositeMeterRegistry firstLevel = new CompositeMeterRegistry();
SimpleMeterRegistry secondLevel = new SimpleMeterRegistry();
composite.add(firstLevel);
firstLevel.add(secondLevel);
MetricsEndpoint endpoint = new MetricsEndpoint(composite);
MetricsEndpoint.MetricResponse response = endpoint.metric("invalid.metric.name",
Collections.emptyList());
assertThat(response).isNull();
}
@Test
public void metricTagValuesAreDeduplicated() {
this.registry.counter("cache", "host", "1", "region", "east", "result", "hit");
......
......@@ -652,6 +652,21 @@ details.
[[boot-features-encrypting-properties]]
=== Encrypting Properties
Spring Boot does not provide any built in support for encrypting property values, however,
it does provide the hook points necessary to modify values contained in the Spring
`Environment`. The `EnvironmentPostProcessor` interface allows you to manipulate the
`Environment` before the application starts. See <<howto-customize-the-environment-or-application-context>>
for details.
If you're looking for a secure way to store credentials and passwords, the
https://cloud.spring.io/spring-cloud-vault/[Spring Cloud Vault] project provides
support for storing externalized configuration in
https://www.vaultproject.io/[HashiCorp Vault].
[[boot-features-external-config-yaml]]
=== Using YAML Instead of Properties
http://yaml.org[YAML] is a superset of JSON and, as such, is a convenient format for
......@@ -6959,6 +6974,10 @@ that the driver exits after each test and that a new instance is injected. If yo
not want this behavior, you can add `@Scope("singleton")` to your `WebDriver` `@Bean`
definition.
WARNING: The `webDriver` scope created by Spring Boot will replace any user defined scope
of the same name. If you define your own `webDriver` scope you may find it stops working
when you use `@WebMvcTest`.
If you have Spring Security on the classpath, `@WebMvcTest` will also scan `WebSecurityConfigurer`
beans. Instead of disabling security completely for such tests, you can use Spring Security's test support.
More details on how to use Spring Security's `MockMvc` support can be found in
......
......@@ -749,16 +749,23 @@ listings for Maven and Gradle:
.Gradle
[source,groovy,indent=0,subs="attributes"]
----
configurations {
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
}
dependencies {
compile("org.springframework.boot:spring-boot-devtools")
developmentOnly("org.springframework.boot:spring-boot-devtools")
}
----
NOTE: Developer tools are automatically disabled when running a fully packaged
application. If your application is launched from `java -jar` or if it is started from a
special classloader, then it is considered a "`production application`". Flagging the
dependency as optional in Maven or using `compileOnly` in Gradle is a best practice that
prevents devtools from being transitively applied to other modules that use your project.
dependency as optional in Maven or using a custom`developmentOnly` configuration in
Gradle (as shown above) is a best practice that prevents devtools from being transitively
applied to other modules that use your project.
TIP: Repackaged archives do not contain devtools by default. If you want to use a
<<using-boot-devtools-remote,certain remote devtools feature>>, you need to disable the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment