diff --git a/spring-boot-actuator-docs/src/main/asciidoc/loggers.adoc b/spring-boot-actuator-docs/src/main/asciidoc/loggers.adoc
new file mode 100644
index 0000000000..735fec1104
--- /dev/null
+++ b/spring-boot-actuator-docs/src/main/asciidoc/loggers.adoc
@@ -0,0 +1,61 @@
+=== /loggers
+This endpoint allows you to view and modify the log levels for the loggers in your
+application. It builds on top of the `LoggingSystem` abstraction and supports the same
+logging frameworks. The logging levels are defined by the `LogLevel` enumeration and
+consists of the following values (although not all logging systems support the full set):
+
+* `TRACE`
+* `DEBUG`
+* `INFO`
+* `WARN`
+* `ERROR`
+* `FATAL`
+* `OFF`
+* `null`
+
+The `configuredLevel` property reflects an explicitly configured logger level, while the
+`effectiveLevel` property reflects the logger level inherited from parent loggers. The
+`effectiveLevel` is managed by each logging framework and reflects the propagation rules
+inherent to and configured in that framework. `null` indicates that there is no explicit
+configuration defined.
+
+
+
+==== Listing All Loggers
+Example curl request:
+include::{generated}/loggers/curl-request.adoc[]
+
+Example HTTP request: [small]##link:../health[icon:external-link[role="silver"]]##
+include::{generated}/loggers/http-request.adoc[]
+
+Example HTTP response:
+include::{generated}/loggers/http-response.adoc[]
+
+
+
+==== Getting a Single Logger
+Example curl request:
+include::{generated}/single-logger/curl-request.adoc[]
+
+Example HTTP request: [small]##link:../health[icon:external-link[role="silver"]]##
+include::{generated}/single-logger/http-request.adoc[]
+
+Example HTTP response:
+include::{generated}/single-logger/http-response.adoc[]
+
+
+
+==== Configuring a Logger
+Setting the `configuredLevel` of a logger requires `POSTing` a partial payload to the
+resource. The `configuredLevel` property must contain a string representation of the
+enumeration described above. `null` indicates that the log level should be unset,
+allowing it to inherit configuration from it's parent.
+
+Example curl request:
+include::{generated}/set-logger/curl-request.adoc[]
+
+Example HTTP request: [small]##link:../health[icon:external-link[role="silver"]]##
+include::{generated}/set-logger/http-request.adoc[]
+
+Example HTTP response:
+include::{generated}/set-logger/http-response.adoc[]
diff --git a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java
index 79a931a302..4f933ef598 100644
--- a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java
+++ b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java
@@ -58,6 +58,7 @@ import org.springframework.util.StringUtils;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@@ -108,6 +109,23 @@ public class EndpointDocumentation {
.andDo(document("partial-logfile"));
}
+ @Test
+ public void singleLogger() throws Exception {
+ this.mockMvc
+ .perform(get("/loggers/org.springframework.boot")
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk()).andDo(document("single-logger"));
+ }
+
+ @Test
+ public void setLogger() throws Exception {
+ this.mockMvc
+ .perform(post("/loggers/org.springframework.boot")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\"configuredLevel\": \"DEBUG\"}"))
+ .andExpect(status().isOk()).andDo(document("set-logger"));
+ }
+
@Test
public void endpoints() throws Exception {
final File docs = new File("src/main/asciidoc");
diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml
index 73af07380b..18ee59c4b1 100644
--- a/spring-boot-actuator/pom.xml
+++ b/spring-boot-actuator/pom.xml
@@ -324,6 +324,11 @@
hsqldb
test
+
+ org.skyscreamer
+ jsonassert
+ test
+
org.springframework
spring-test
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java
index 4fe5be0893..b9a97d4780 100644
--- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java
@@ -16,12 +16,14 @@
package org.springframework.boot.actuate.autoconfigure;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.List;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
+import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
import org.springframework.hateoas.ResourceSupport;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
@@ -30,6 +32,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
* Adds endpoint links to {@link ResourceSupport}.
*
* @author Dave Syer
+ * @author Madhura Bhave
*/
class LinksEnhancer {
@@ -47,23 +50,34 @@ class LinksEnhancer {
resource.add(linkTo(LinksEnhancer.class).slash(this.rootPath + self)
.withSelfRel());
}
- Set added = new HashSet();
+ MultiValueMap added = new LinkedMultiValueMap();
for (MvcEndpoint endpoint : this.endpoints.getEndpoints()) {
- if (!endpoint.getPath().equals(self) && !added.contains(endpoint.getPath())) {
- addEndpointLink(resource, endpoint);
+ if (!endpoint.getPath().equals(self)) {
+ String rel = getRel(endpoint);
+ List paths = added.get(rel);
+ if (paths == null || !paths.contains(endpoint.getPath())) {
+ addEndpointLink(resource, endpoint, rel);
+ added.add(rel, endpoint.getPath());
+ }
}
- added.add(endpoint.getPath());
}
}
- private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint) {
+ private String getRel(MvcEndpoint endpoint) {
+ if (endpoint instanceof NamedMvcEndpoint) {
+ return ((NamedMvcEndpoint) endpoint).getName();
+ }
+ String path = endpoint.getPath();
+ return (path.startsWith("/") ? path.substring(1) : path);
+ }
+
+ private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint,
+ String rel) {
Class> type = endpoint.getEndpointType();
type = (type == null ? Object.class : type);
- String path = endpoint.getPath();
- String rel = (path.startsWith("/") ? path.substring(1) : path);
if (StringUtils.hasText(rel)) {
- String fullPath = this.rootPath + endpoint.getPath();
- resource.add(linkTo(type).slash(fullPath).withRel(rel));
+ String href = this.rootPath + endpoint.getPath();
+ resource.add(linkTo(type).slash(href).withRel(rel));
}
}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java
index 334d578295..fbb999a608 100644
--- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java
@@ -20,6 +20,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeSet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.logging.LogLevel;
@@ -35,8 +38,7 @@ import org.springframework.util.Assert;
* @since 1.5.0
*/
@ConfigurationProperties(prefix = "endpoints.loggers")
-public class LoggersEndpoint
- extends AbstractEndpoint