#12 Spring Boot – Sensitive Actuator

springBootArt

W poprzednim wpisie opisałem endpointy, które były insensitive. Oznacza to, iż nie wymagały logowania. Dziś przedstawię wam pozostałą część. Pakiet Spring Boot Acutator oferuje całą gamę ciekawych rozwiązań. Zapraszam do czytania.

Autoconfig

Zwraca nam informacje o wszystkich autokonfiguracjach. Możemy dowiedzieć się, dlaczego akurat konkretna konfiguracja jest załadowała. W kodzie odpowiedzialnym za to mechanizmem jest OnClassCondition oraz OnBeanCondition:

AuditAutoConfiguration#authenticationAuditListener: [
   {
      condition: "OnClassCondition",
      message: "@ConditionalOnClass found required class 'org.springframework.security.authentication.event.AbstractAuthenticationEvent'; @ConditionalOnMissingClass did not find unwanted class"
   },
   {
      condition: "OnBeanCondition",
      message: "@ConditionalOnMissingBean (types: org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener; SearchStrategy: all) did not find any beans"
   }
]

Beans

Wyświetla listę dostępnych beanów, wraz z jego scopem oraz ścieżką gdzie znajduje się plik (przydatne, gdy chcemy się dowiedzieć jakie mamy dostępne beany w kontekście springowym):

{
 bean: "bookController",
 aliases: [ ],
 scope: "singleton",
 type: "pl.codecouple.books.BookController",
 resource: "file [path/spring-demo/target/classes/pl/codecouple/books/BookController.class]",
 dependencies: [
 "bookServiceImpl"
 ]
}

Configprops

Wyświetla listę wszystkich klas, które oznaczone są adnotacją @ConfigurationProperties wraz z ich wartościami:

spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties: {
prefix: "spring.http.multipart",
   properties: {
      maxRequestSize: "10MB",
      fileSizeThreshold: "0",
      location: null,
      maxFileSize: "1MB",
      enabled: true,
      resolveLazily: false
   }
}

Dump

Gdy pojawiają się problemy z wątkami możemy zrobić “dump’a” /dump, czyli zrzut aktualnie używanych wątków w JVM:

threadName: "http-nio-8082-exec-9",
threadId: 41,
blockedTime: -1,
blockedCount: 0,
...

Env

Wszelkie informacje na temat środowiska, na który pracuje nasza aplikacja uzyskamy dzięki /env:

{
   "profiles": [],
   "server.ports": {
   "local.server.port": 8082
},
   "servletContextInitParams": {},
   "systemProperties": {
      "java.runtime.name": "Java(TM) SE Runtime Environment",
      "spring.output.ansi.enabled": "always",
...

Metrics

Metryki naszej aplikacji znajdziemy pod endpointem /metrics. Znajdują się tu informacje o pamięci, a także liczniki wywołań poszczególnych endpointów:

{
  "mem": 571552,
  "mem.free": 187079,
  "processors": 4,
  "instance.uptime": 327716,
  "uptime": 335840,
  "systemload.average": -1,
...

Mappings

Kolejny endpoint /mappings zawiera informacje o udostępnionych endpointach i związanych z nimi akcjami. W przykładzie poniżej opisany mamy typ metod HTTP, jaki bean jest używany oraz jaka metoda zostanie wywołana:

"{[/books],methods=[GET]}": {
    "bean": "requestMappingHandlerMapping",
    "method": "public java.util.List<pl.diebold.books.Book> pl.diebold.books.BookController.getAllBooks()"
  },
  "{[/books],methods=[POST]}": {
    "bean": "requestMappingHandlerMapping",
    "method": "public void pl.diebold.books.BookController.addNewBook(pl.diebold.books.Book)"
  },

Shutdown

Bardzo fajny, ale zarazem niebezpieczny wyłącznik. Po wywołaniu tego endpointu możemy wyłączyć naszą aplikację (uwaga, żeby nie poszło to na produkcję ;)). Ten adres jako jedyny jest domyślnie wyłączony. Aby go włączyć korzystamy z application.properties:

endpoints.shutdown.enabled=true

Teraz po wysłaniu pustego POST’a wyłączymy aplikację i pojawi nam się fajny komunikat w odpowiedzi:

{ "message": "Shutting down, bye..." }

Trace

Bardzo przydatne podczas procesu developmentu. W prosty sposób możemy podejrzeć ścieżkę wywołań w naszej aplikacji wraz z takimi informacjami jak nagłówek oraz odpowiedź jaką dostaliśmy:

{
   timestamp: 1488116131497,
   info: {
      method: "GET",
      path: "/favicon.ico",
      headers: {
         request: {
            host: "localhost:8082",
            connection: "keep-alive",
            user-agent: "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
            accept: "image/webp,image/*,*/*;q=0.8",
            referer: "http://localhost:8082/autoconfig",
            accept-encoding: "gzip, deflate, sdch, br",
            accept-language: "pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4",
            cookie: "jenkins-timestamper-offset=-7200000; csrftoken=kc8eP3XnleqYXHeLYuhJM6uTeSpsQzcT; Idea-7e059cda=68c638d4-7676-48ce-a117-de1bea07b1bc"
      },
      response: {
            X-Application-Context: "application:8082",
            Last-Modified: "Sun, 19 Feb 2017 10:24:09 GMT",
            Accept-Ranges: "bytes",
            Content-Type: "application/octet-stream",
            Content-Length: "946",
            Date: "Sun, 26 Feb 2017 13:35:31 GMT",
            status: "200"
            }
         }
   }
}

Docs

Nie chcecie za każdym razem wracać do tego wpisu? Oczywiście wracajcie, ale także możecie skorzystać z /docs, w którym znajduje się dokumentacja wszystkich endpointów z pakietu actuators. Aby móc korzystać z /docs należy dodać zależność mavenową:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-actuator-docs</artifactId>
    </dependency>
</dependencies>

Od teraz można pod /docs przeczytać dokumentacje do każdego endpointu.

Konfiguracja

Każdy z endpointów można konfigurować przy wykorzystaniu application.properties. Podstawowe pola, które są w każdym acutatorze to:

  • id – określa adres,
  • enabled – określa czy jest włączony,
  • sensitive – określa czy wymaga zalogowania.
endpoints.beans.id=mybeans #zmieniamy adres na /mybeans
endpoints.beans.enabled=true #/mybeans jest włączony
endpoints.beans.sensitive=false #/mybeans nie wymaga zalogownia

Możemy także w łatwy sposób wyłączyć wszystkie endpointy i zostawić aktywny tylko info:

endpoints.enabled=false
endpoints.info.enabled=true

Podobnie możemy zrobić z ich dostępnością, wszystkie endpointy wymagąją zalogowania oprócz info:

endpoints.sensitive=true
endpoints.info.sensitive=false

Możemy także dodać własny Endpoint. Nasza klasa musi jedynie implementować odpowiedni interfejs. Metoda getId() zwraca nam adres.

@Component
public class MyCustomIndicator implements Endpoint<List<String>>{


    @Override
    public String getId() {
        return "CodeCoupleCustomEndpoint";
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public boolean isSensitive() {
        return false;
    }

    @Override
    public List<String> invoke() {
        return Collections.singletonList("CodeCouple!");
    }
}

Teraz po wejściu na nasz adres plus “/CodeCoupleCustomEndpoint” powinien pokazać się nam napis “CodeCouple!”.

Ostatnim elementem, który chce wam pokazać to zmiana portu dla wszystkich acutatorów. W application.properties należy dodać wpis:

management.port=8083

Od teraz wszystkie endpointy są dostępne pod portem 8083.

2017-03-24 13:20:17.337 INFO 9184 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8083 (http)
2017-03-24 13:20:17.338 INFO 9184 --- [ restartedMain] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
2017-03-24 13:20:17.378 INFO 9184 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8082 (http)