{"id":96977,"date":"2019-09-02T09:51:21","date_gmt":"2019-09-02T06:51:21","guid":{"rendered":"http:\/\/www.javacodegeeks.com\/?p=96977"},"modified":"2019-09-11T12:01:09","modified_gmt":"2019-09-11T09:01:09","slug":"monitor-your-java-apps-spring-boot-actuator","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html","title":{"rendered":"Monitor Your Java Apps with Spring Boot Actuator"},"content":{"rendered":"<p><span style=\"font-size: 20px;\"><strong>Friends don\u2019t let friends write user auth. Tired of managing your own users?<\/strong><a href=\"https:\/\/developer.okta.com\/signup\/?utm_campaign=text_website_all_multiple_dev_dev_java-app-actuator_null&amp;utm_source=jcg&amp;utm_medium=cpc\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\"> Try Okta\u2019s API and Java SDKs today. Authenticate, manage, and secure users in any application within minutes.<\/a><\/span><\/p>\n<p>Have you worked with Spring Boot Actuator yet? It\u2019s an immensely helpful library that helps you monitor app health and interactions with the app &#8211; perfect for going to production! Spring Boot Actuator includes a built-in endpoint for tracing HTTP calls to your application &#8211; very useful for monitoring OpenID Connect (OIDC) requests &#8211; but unfortunately the default implementation does not trace body contents. In this post, I\u2019ll show you how to extend the httptrace endpoint for capturing contents and tracing the OIDC flow.<\/p>\n<p>Let\u2019s get started!<\/p>\n<h2 class=\"wp-block-heading\" id=\"create-an-openid-connect-app-with-spring-initializr-and-okta\">Create an OpenID Connect App with Spring Initializr and Okta<\/h2>\n<p>You can use the excellent&nbsp;<a href=\"https:\/\/start.spring.io\/\">Spring Initializr<\/a>&nbsp;website or API for creating a sample OIDC application with Okta integration:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:bash\">curl https:\/\/start.spring.io\/starter.zip \\\n  dependencies==web,okta \\\n  packageName==com.okta.developer.demo -d\n<\/pre>\n<p>Before running your OIDC application however, you will need an Okta account. Okta is a developer service that handles storing user accounts and implementing user management (including OIDC) for you. Go ahead and register for a&nbsp;<a href=\"https:\/\/developer.okta.com\/signup\/?utm_campaign=text_website_all_multiple_dev_dev_java-app-actuator_null&amp;utm_source=jcg&amp;utm_medium=cpc\">free developer account<\/a> to continue.<\/p>\n<p>Once you login to your Okta account, go to the Dashboard and then to the&nbsp;<strong>Applications<\/strong>&nbsp;section. Add a new Web application, and then in the General section get the client credentials:&nbsp;<strong>Client ID<\/strong>&nbsp;and&nbsp;<strong>Client Secret<\/strong>.<\/p>\n<p>You will need the&nbsp;<strong>Issuer<\/strong>&nbsp;which is the organization URL as well, which you can find at the top right corner in the Dashboard home.&nbsp;<strong>Note<\/strong>: By default, the built-in&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">Everyone<\/code>&nbsp;Okta group is assigned to this application, so any users in your Okta org will be able to authenticate to it.<\/p>\n<p>With your Client ID, Client Secret. and the Issuer in place, start your application by passing the credentials through the command line:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:bash\">OKTA_OAUTH2_REDIRECTURI=\/authorization-code\/callback \\\nOKTA_OAUTH2_ISSUER=&lt;issuer&gt;\/oauth2 \\\nOKTA_OAUTH2_CLIENT_ID=&lt;client id&gt; \\\nOKTA_OAUTH2_CLIENT_SECRET=&lt;client secret&gt; \\\n.\/mvnw spring-boot:run\n<\/pre>\n<h2 class=\"wp-block-heading\" id=\"add-test-controller-to-the-spring-boot-app\">Add Test Controller to the Spring Boot App<\/h2>\n<p>It\u2019s a good practice to add a simple controller for testing the authentication flow. By default, access will only be allowed to authenticated users.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Controller\n@RequestMapping(value = \"\/hello\")\npublic class HelloController {\n\n    @GetMapping(value = \"\/greeting\")\n    @ResponseBody\n    public String getGreeting(Principal user) {\n        return \"Good morning \" + user.getName();\n    }\n}\n<\/pre>\n<p>You can test this out by restarting the app and browsing to&nbsp;<a href=\"http:\/\/localhost:8080\/hello\/greeting\">\/hello\/greeting<\/a>.<\/p>\n<h2 class=\"wp-block-heading\" id=\"add-spring-boot-actuator-dependency\">Add Spring Boot Actuator Dependency<\/h2>\n<p>Enable Spring Boot Actuator by adding the starter Maven dependency to the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">pom.xml file<\/code>:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:xml\">&lt;dependency&gt;\n  &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n  &lt;artifactId&gt;spring-boot-starter-actuator&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n<\/pre>\n<p>To enable the httptrace endpoint, edit the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">src\/main\/resources\/application.properties<\/code>&nbsp;and add the following line:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:bash\">management.endpoints.web.exposure.include=info,health,httptrace\n<\/pre>\n<p>You can test the out-of-the-box actuator features running the application browsing to&nbsp;<a href=\"http:\/\/localhost:8080\/hello\/greeting\">\/hello\/greeting<\/a>, and logging in.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Under the auto-configuration, Spring Security filters have higher precedence than filters added by the httptrace actuator.<\/p>\n<\/blockquote>\n<p>This means only authenticated calls are traced by default. We are going to change that here soon, but for now, you can see what is traced at&nbsp;<a href=\"http:\/\/localhost:8080\/actuator\/httptrace\">\/actuator\/httptrace<\/a>. The response should look like this JSON payload:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:js\">{\n   \"traces\":[\n      {\n         \"timestamp\":\"2019-05-19T05:38:42.726Z\",\n         \"principal\":{\n            \"name\":\"***\"\n         },\n         \"session\":{\n            \"id\":\"***\"\n         },\n         \"request\":{\n            \"method\":\"GET\",\n            \"uri\":\"http:\/\/localhost:8080\/\",\n            \"headers\":{},\n            \"remoteAddress\":\"0:0:0:0:0:0:0:1\"\n         },\n         \"response\":{\n            \"status\":200,\n            \"headers\":{}\n         },\n         \"timeTaken\":145\n      }\n   ]\n}\n<\/pre>\n<h2 class=\"wp-block-heading\" id=\"add-custom-http-tracing-to-your-spring-boot-app\">Add Custom HTTP Tracing to your Spring Boot App<\/h2>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>HTTP tracing is not very flexible. Andy Wilkinson, the author of the httptrace actuator, suggests&nbsp;<a href=\"https:\/\/github.com\/spring-projects\/spring-boot\/issues\/12953\">implementing your own endpoint<\/a>&nbsp;if body tracing is required.<\/p>\n<\/blockquote>\n<p>Alternatively, with some custom filters, we can enhance the base implementation without much work. In the following sections I\u2019ll show you how to:<\/p>\n<ul class=\"wp-block-list\">\n<li>Create a filter for capturing request and response body<\/li>\n<li>Configure the filters precedence for tracing OIDC calls<\/li>\n<li>Create the httptrace endpoint extension with a custom trace repository to store additional data<\/li>\n<\/ul>\n<h2 class=\"wp-block-heading\" id=\"use-spring-boot-actuator-to-capture-request-and-response-body-contents\">Use Spring Boot Actuator to Capture Request and Response Body Contents<\/h2>\n<p>Next, create a filter for tracing the request and response body contents. This filter will have precedence over the httptrace filter, so the cached body contents are available when the actuator saves the trace.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Component\n@ConditionalOnProperty(prefix = \"management.trace.http\", name = \"enabled\", matchIfMissing = true)\npublic class ContentTraceFilter extends OncePerRequestFilter {\n\n    private ContentTraceManager traceManager;\n\n    @Value(\"${management.trace.http.tracebody:false}\")\n    private boolean traceBody;\n\n   public ContentTraceFilter(ContentTraceManager traceManager) {\n        super();\n        this.traceManager = traceManager;\n    }\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request,\n            HttpServletResponse response, FilterChain filterChain)\n            throws ServletException, IOException {\n\n        if (!isRequestValid(request) || !traceBody) {\n            filterChain.doFilter(request, response);\n            return;\n        }\n\n        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(\n                request, 1000);\n        ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(\n                response);\n        try {\n            filterChain.doFilter(wrappedRequest, wrappedResponse);\n            traceManager.updateBody(wrappedRequest, wrappedResponse);\n        } finally {\n            wrappedResponse.copyBodyToResponse();\n        }\n    }\n\n    private boolean isRequestValid(HttpServletRequest request) {\n        try {\n            new URI(request.getRequestURL().toString());\n            return true;\n        } catch (URISyntaxException ex) {\n            return false;\n        }\n    }\n\n}\n<\/pre>\n<p>Notice the call to a&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">ContentTraceManager<\/code>, a simple&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">@RequestScope<\/code>&nbsp;bean that will store the additional data:<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Component\n@RequestScope\n@ConditionalOnProperty(prefix = \"management.trace.http\", name = \"enabled\", matchIfMissing = true)\npublic class ContentTraceManager {\n\n    private ContentTrace trace;\n\n    public ContentTraceManager(ContentTrace trace) {\n        this.trace=trace;\n    }\n\n    protected static Logger logger = LoggerFactory\n            .getLogger(ContentTraceManager.class);\n\n    public void updateBody(ContentCachingRequestWrapper wrappedRequest,\n            ContentCachingResponseWrapper wrappedResponse) {\n\n        String requestBody = getRequestBody(wrappedRequest);\n        getTrace().setRequestBody(requestBody);\n\n        String responseBody = getResponseBody(wrappedResponse);\n        getTrace().setResponseBody(responseBody);\n    }\n\n    protected String getRequestBody(\n            ContentCachingRequestWrapper wrappedRequest) {\n        try {\n            if (wrappedRequest.getContentLength() &lt;= 0) {\n                return null;\n            }\n            return new String(wrappedRequest.getContentAsByteArray(), 0,\n                    wrappedRequest.getContentLength(),\n                    wrappedRequest.getCharacterEncoding());\n        } catch (UnsupportedEncodingException e) {\n            logger.error(\n                    \"Could not read cached request body: \" + e.getMessage());\n            return null;\n        }\n\n    }\n\n    protected String getResponseBody(\n            ContentCachingResponseWrapper wrappedResponse) {\n\n        try {\n            if (wrappedResponse.getContentSize() &lt;= 0) {\n                return null;\n            }\n            return new String(wrappedResponse.getContentAsByteArray(), 0,\n                    wrappedResponse.getContentSize(),\n                    wrappedResponse.getCharacterEncoding());\n        } catch (UnsupportedEncodingException e) {\n            logger.error(\n                    \"Could not read cached response body: \" + e.getMessage());\n            return null;\n        }\n\n    }\n\n    public ContentTrace getTrace() {\n        if (trace == null) {\n            trace = new ContentTrace();\n        }\n        return trace;\n    }\n}\n<\/pre>\n<p>For modeling the trace with additional data, compose a custom&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">ContentTrace<\/code>&nbsp;class with the built-in&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">HttpTrace<\/code>&nbsp;information, adding properties for storing the body contents.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">public class ContentTrace {\n\n    protected HttpTrace httpTrace;\n\n    protected String requestBody;\n\n    protected String responseBody;\n\n    protected Authentication principal;\n\n    public ContentTrace() {\n    }\n\n    public void setHttpTrace(HttpTrace httpTrace) {\n        this.httpTrace = httpTrace;\n    }\n}\n<\/pre>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Add setters and getters for&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">httpTrace<\/code>,&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">principal<\/code>,&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">requestBody<\/code>&nbsp;and&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">responseBody<\/code>.<\/p>\n<\/blockquote>\n<h3 class=\"wp-block-heading\" id=\"configure-filter-precedence\">Configure Filter Precedence<\/h3>\n<p>For capturing requests to OIDC endpoints in your application, the tracing filters have to sit before Spring Security filters. As long as&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">ContentTraceFilter<\/code>&nbsp;has precedence over&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">HttpTraceFilter<\/code>, both can be placed before or after&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">SecurityContextPersistenceFilter<\/code>, the first one in the Spring Security filter chain.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Configuration\n@ConditionalOnProperty(prefix = \"management.trace.http\", name = \"enabled\", matchIfMissing = true)\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    private HttpTraceFilter httpTraceFilter;\n    private ContentTraceFilter contentTraceFilter;\n\n    public WebSecurityConfig(\n        HttpTraceFilter httpTraceFilter, ContentTraceFilter contentTraceFilter\n    ) {\n        this.httpTraceFilter = httpTraceFilter;\n        this.contentTraceFilter = contentTraceFilter;\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.addFilterBefore(contentTraceFilter,\n                SecurityContextPersistenceFilter.class)\n                .addFilterAfter(httpTraceFilter,\n                        SecurityContextPersistenceFilter.class)\n                .authorizeRequests().anyRequest().authenticated()\n                .and().oauth2Client()\n                .and().oauth2Login();\n    }\n}\n<\/pre>\n<h2 class=\"wp-block-heading\" id=\"tracing-the-authenticated-user\">Tracing the Authenticated User<\/h2>\n<p>We\u2019re installing the trace filters before the Spring Security filter chain. This means that the Principal is no longer available when the HttpTraceFilter saves the trace. We can restore this trace data with a new filter and the ContentTraceManager.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Component\n@ConditionalOnProperty(prefix = \"management.trace.http\", name = \"enabled\", matchIfMissing = true)\npublic class PrincipalTraceFilter extends OncePerRequestFilter {\n\n    private ContentTraceManager traceManager;\n    private HttpTraceProperties traceProperties;\n\n    public PrincipalTraceFilter(\n        ContentTraceManager traceManager,\n        HttpTraceProperties traceProperties\n    ) {\n        super();\n        this.traceManager = traceManager;\n        this.traceProperties = traceProperties;\n    }\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request,\n            HttpServletResponse response,\n            FilterChain filterChain)\n            throws ServletException, IOException {\n\n        if (!isRequestValid(request)) {\n            filterChain.doFilter(request, response);\n            return;\n        }\n        try {\n            filterChain.doFilter(request, response);\n\n        } finally {\n            if (traceProperties.getInclude().contains(Include.PRINCIPAL)) {\n                traceManager.updatePrincipal();\n            }\n        }\n\n    }\n\n    private boolean isRequestValid(HttpServletRequest request) {\n        try {\n            new URI(request.getRequestURL().toString());\n            return true;\n        } catch (URISyntaxException ex) {\n            return false;\n        }\n    }\n\n}\n<\/pre>\n<p>Add the missing&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">ContentTraceManager<\/code>&nbsp;class for updating the principal:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">public class ContentTraceManager {\n\n    public void updatePrincipal() {\n        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();\n        if (authentication != null) {\n            getTrace().setPrincipal(authentication);\n        }\n  }\n}\n<\/pre>\n<p>The&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">PrincipalTraceFilter<\/code>&nbsp;must have lower precedence than the Spring Security filter chain, so the authenticated principal is available when requested from the security context. Modify the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">WebSecurityConfig<\/code>&nbsp;to insert the filter after the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">FilterSecurityInterceptor<\/code>, the last filter in the security chain.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Configuration\n@ConditionalOnProperty(prefix = \"management.trace.http\", name = \"enabled\", matchIfMissing = true)\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    private HttpTraceFilter httpTraceFilter;\n    private ContentTraceFilter contentTraceFilter;\n    private PrincipalTraceFilter principalTraceFilter;\n\n    public WebSecurityConfig(\n        HttpTraceFilter httpTraceFilter,\n        ContentTraceFilter contentTraceFilter,\n        PrincipalTraceFilter principalTraceFilter\n    ) {\n        super();\n        this.httpTraceFilter = httpTraceFilter;\n        this.contentTraceFilter = contentTraceFilter;\n        this.principalTraceFilter = principalTraceFilter;\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.addFilterBefore(contentTraceFilter,\n                SecurityContextPersistenceFilter.class)\n                .addFilterAfter(httpTraceFilter,\n                        SecurityContextPersistenceFilter.class)\n                .addFilterAfter(principalTraceFilter,\n                        FilterSecurityInterceptor.class)\n                .authorizeRequests().anyRequest().authenticated()\n                .and().oauth2Client()\n                .and().oauth2Login();\n    }\n}\n<\/pre>\n<h2 class=\"wp-block-heading\" id=\"httptrace-endpoint-extension\">HTTPTrace Endpoint Extension<\/h2>\n<p>Finally, define the endpoint enhancement using the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">@EndpointWebExtension<\/code>&nbsp;annotation. Implement a&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">CustomHttpTraceRepository<\/code>&nbsp;to store and retrieve a&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">ContentTrace<\/code>&nbsp;with the additional data.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Component\n@EndpointWebExtension(endpoint = HttpTraceEndpoint.class)\n@ConditionalOnProperty(prefix = \"management.trace.http\", name = \"enabled\", matchIfMissing = true)\npublic class HttpTraceEndpointExtension {\n\n    private CustomHttpTraceRepository repository;\n\n    public HttpTraceEndpointExtension(CustomHttpTraceRepository repository) {\n        super();\n        this.repository = repository;\n    }\n\n    @ReadOperation\n    public ContentTraceDescriptor contents() {\n        List&lt;ContentTrace&gt; traces = repository.findAllWithContent();\n        return new ContentTraceDescriptor(traces);\n    }\n}\n<\/pre>\n<p>Redefine a descriptor for the endpoint return type:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">public class ContentTraceDescriptor {\n\n    protected List&lt;ContentTrace&gt; traces;\n\n    public ContentTraceDescriptor(List&lt;ContentTrace&gt; traces) {\n        super();\n        this.traces = traces;\n    }\n\n    public List&lt;ContentTrace&gt; getTraces() {\n        return traces;\n    }\n\n    public void setTraces(List&lt;ContentTrace&gt; traces) {\n        this.traces = traces;\n    }\n\n}\n<\/pre>\n<p>Create the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">CustomHttpTraceRepository<\/code>&nbsp;implementing the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">HttpTraceRepository<\/code>&nbsp;interface:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">@Component\n@ConditionalOnProperty(prefix = \"management.trace.http\", name = \"enabled\", matchIfMissing = true)\npublic class CustomHttpTraceRepository implements HttpTraceRepository {\n\n    private final List&lt;ContentTrace&gt; contents = new LinkedList&lt;&gt;();\n\n    private ContentTraceManager traceManager;\n\n    public CustomHttpTraceRepository(ContentTraceManager traceManager) {\n        super();\n        this.traceManager = traceManager;\n    }\n\n    @Override\n    public void add(HttpTrace trace) {\n        synchronized (this.contents) {\n            ContentTrace contentTrace = traceManager.getTrace();\n            contentTrace.setHttpTrace(trace);\n            this.contents.add(0, contentTrace);\n        }\n    }\n\n    @Override\n    public List&lt;HttpTrace&gt; findAll() {\n        synchronized (this.contents) {\n            return contents.stream().map(ContentTrace::getHttpTrace)\n                    .collect(Collectors.toList());\n        }\n    }\n\n    public List&lt;ContentTrace&gt; findAllWithContent() {\n        synchronized (this.contents) {\n            return Collections.unmodifiableList(new ArrayList&lt;&gt;(this.contents));\n        }\n    }\n\n}\n<\/pre>\n<h2 class=\"wp-block-heading\" id=\"inspect-openid-connect-http-trace\">Inspect OpenID Connect HTTP Trace<\/h2>\n<p>Modify the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">application.properties<\/code>&nbsp;file for tracing all available data by adding the following line:<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:bash\">management.trace.http.include=request-headers,response-headers,cookie-headers,principal,time-taken,authorization-header,remote-address,session-id<\/pre>\n<p>Run the application again and call the secured controller&nbsp;<a href=\"http:\/\/localhost:8080\/hello\/greeting\">\/hello\/greeting<\/a>. Authenticate against Okta and then inspect the traces at&nbsp;<a href=\"http:\/\/localhost:8080\/actuator\/httptrace\">\/actuator\/httptrace<\/a>.<\/p>\n<p>You should now see OIDC calls in the trace as well as the request and response contents. For example, in the trace below, a request to the application authorization endpoint redirects to the Okta authorization server, initiating the OIDC authorization code flow.<\/p>\n<pre class=\"wp-block-preformatted gutter: false;brush:java\">{\n    \"httpTrace\": {\n        \"timestamp\": \"2019-05-22T00:52:22.383Z\",\n        \"principal\": null,\n        \"session\": {\n            \"id\": \"C2174F5E5F85B313B2284639EE4016E7\"\n        },\n        \"request\": {\n            \"method\": \"GET\",\n            \"uri\": \"http:\/\/localhost:8080\/oauth2\/authorization\/okta\",\n            \"headers\": {\n                \"cookie\": [\n                    \"JSESSIONID=C2174F5E5F85B313B2284639EE4016E7\"\n                ],\n                \"accept-language\": [\n                    \"en-US,en;q=0.9\"\n                ],\n                \"upgrade-insecure-requests\": [\n                    \"1\"\n                ],\n                \"host\": [\n                    \"localhost:8080\"\n                ],\n                \"connection\": [\n                    \"keep-alive\"\n                ],\n                \"accept-encoding\": [\n                    \"gzip, deflate, br\"\n                ],\n                \"accept\": [\n                    \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8\"\n                ],\n                \"user-agent\": [\n                    \"Mozilla\/5.0 (X11; Linux x86_64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/66.0.3359.181 Safari\/537.36\"\n                ]\n            },\n            \"remoteAddress\": \"0:0:0:0:0:0:0:1\"\n        },\n        \"response\": {\n            \"status\": 302,\n            \"headers\": {\n                \"X-Frame-Options\": [\n                    \"DENY\"\n                ],\n                \"Cache-Control\": [\n                    \"no-cache, no-store, max-age=0, must-revalidate\"\n                ],\n                \"X-Content-Type-Options\": [\n                    \"nosniff\"\n                ],\n                \"Expires\": [\n                    \"0\"\n                ],\n                \"Pragma\": [\n                    \"no-cache\"\n                ],\n                \"X-XSS-Protection\": [\n                    \"1; mode=block\"\n                ],\n                \"Location\": [\n                    \"https:\/\/dev-239352.okta.com\/oauth2\/default\/v1\/authorize?response_type=code&amp;client_id=0oalrp4qx3Do43VyI356&amp;scope=openid%20profile%20email&amp;state=1uzHRyaHVmyKcpb7eAvJVrdJTZ6wTgkPv3fsC14qdOk%3D&amp;redirect_uri=http:\/\/localhost:8080\/authorization-code\/callback\"\n                ]\n            }\n        },\n        \"timeTaken\": 9\n    },\n    \"requestBody\": null,\n    \"responseBody\": null\n}\n<\/pre>\n<p>All of the code in this post can be found on GitHub in the&nbsp;<a href=\"https:\/\/github.com\/oktadeveloper\/okta-spring-boot-custom-actuator-example\">okta-spring-boot-custom-actuator-example<\/a>&nbsp;repository.<\/p>\n<h2 class=\"wp-block-heading\" id=\"learn-more\">Learn More<\/h2>\n<p>That\u2019s all there is to it! You just learned how to configure and extend the&nbsp;<code class=\"highlighter-rouge\" style=\"font-size: 13px;\">httptrace<\/code>&nbsp;actuator endpoint for monitoring your OIDC application. For more insights about Spring Boot Actuator, Spring Boot in general, or user authentication, check out the links below:<\/p>\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/developer.okta.com\/blog\/2019\/05\/22\/java-microservices-spring-boot-spring-cloud?utm_campaign=text_website_all_multiple_dev_dev_java-app-actuator_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Java Microservices with Spring Boot and Spring Cloud<\/a><\/li>\n<li><a href=\"https:\/\/docs.spring.io\/spring-boot\/docs\/current\/reference\/html\/production-ready-endpoints.html\">Spring Boot Actuator Endpoints<\/a><\/li>\n<li><a href=\"https:\/\/docs.spring.io\/spring-boot\/docs\/current\/reference\/html\/production-ready-endpoints.html#production-ready-endpoints-custom\">Implementing Custom Endpoints<\/a><\/li>\n<li><a href=\"https:\/\/developer.okta.com\/quickstart\/#\/okta-sign-in-page\/java\/spring?utm_campaign=text_website_all_multiple_dev_dev_java-app-actuator_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Okta Authentication Quickstart Guides Java Spring<\/a><\/li>\n<\/ul>\n<p>As always, if you have any comments or questions about this post, feel free to comment below. Don\u2019t miss out on any of our cool content in the future by following us on&nbsp;<a href=\"https:\/\/twitter.com\/oktadev\">Twitter<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/www.youtube.com\/c\/oktadev\">YouTube<\/a>.<\/p>\n<p><a data-saferedirecturl=\"https:\/\/www.google.com\/url?q=https:\/\/developer.okta.com\/blog\/2019\/07\/17\/monitoring-with-actuator&amp;source=gmail&amp;ust=1567146141794000&amp;usg=AFQjCNGJFiz7Mspv6QSuvU9avZWQgmwqUQ\" href=\"https:\/\/developer.okta.com\/blog\/2019\/07\/17\/monitoring-with-actuator?utm_campaign=text_website_all_multiple_dev_dev_java-app-actuator_null&amp;utm_source=jcg&amp;utm_medium=cpc\">&#8220;Monitor Your Java Apps with Spring Boot Actuator&#8221;<\/a>&nbsp;was originally published on the Okta Developer blog on July 17, 2019.<\/p>\n<p><span style=\"font-size: 20px;\"><strong>Friends don\u2019t let friends write user auth. Tired of managing your own users?<\/strong><a href=\"https:\/\/developer.okta.com\/signup\/?utm_campaign=text_website_all_multiple_dev_dev_java-app-actuator_null&amp;utm_source=jcg&amp;utm_medium=cpc\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\"> Try Okta\u2019s API and Java SDKs today. Authenticate, manage, and secure users in any application within minutes.<\/a><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Friends don\u2019t let friends write user auth. Tired of managing your own users? Try Okta\u2019s API and Java SDKs today. Authenticate, manage, and secure users in any application within minutes. Have you worked with Spring Boot Actuator yet? It\u2019s an immensely helpful library that helps you monitor app health and interactions with the app &#8211; &hellip;<\/p>\n","protected":false},"author":100428,"featured_media":240,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[30,854,1926],"class_list":["post-96977","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-spring","tag-spring-boot","tag-spring-boot-actuator"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Monitor Your Java Apps with Spring Boot Actuator - Java Code Geeks<\/title>\n<meta name=\"description\" content=\"Interested to learn about Spring Boot Actuator? Check our article explaining how to Monitor Your Java Apps with Spring Boot Actuator\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Monitor Your Java Apps with Spring Boot Actuator - Java Code Geeks\" \/>\n<meta property=\"og:description\" content=\"Interested to learn about Spring Boot Actuator? Check our article explaining how to Monitor Your Java Apps with Spring Boot Actuator\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2019-09-02T06:51:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-09-11T09:01:09+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Jimena Garbarino\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jimena Garbarino\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html\"},\"author\":{\"name\":\"Jimena Garbarino\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/a5a31a2e38d1966173d27442b15e8f5b\"},\"headline\":\"Monitor Your Java Apps with Spring Boot Actuator\",\"datePublished\":\"2019-09-02T06:51:21+00:00\",\"dateModified\":\"2019-09-11T09:01:09+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html\"},\"wordCount\":1116,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"keywords\":[\"Spring\",\"Spring Boot\",\"Spring Boot Actuator\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html\",\"name\":\"Monitor Your Java Apps with Spring Boot Actuator - Java Code Geeks\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"datePublished\":\"2019-09-02T06:51:21+00:00\",\"dateModified\":\"2019-09-11T09:01:09+00:00\",\"description\":\"Interested to learn about Spring Boot Actuator? Check our article explaining how to Monitor Your Java Apps with Spring Boot Actuator\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"width\":150,\"height\":150,\"caption\":\"spring-interview-questions-answers\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/09\\\/monitor-your-java-apps-spring-boot-actuator.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Monitor Your Java Apps with Spring Boot Actuator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/a5a31a2e38d1966173d27442b15e8f5b\",\"name\":\"Jimena Garbarino\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9d90792ef285836e7872046fa29035c7bae53493e466020b6dfa490464afc278?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9d90792ef285836e7872046fa29035c7bae53493e466020b6dfa490464afc278?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9d90792ef285836e7872046fa29035c7bae53493e466020b6dfa490464afc278?s=96&d=mm&r=g\",\"caption\":\"Jimena Garbarino\"},\"sameAs\":[\"https:\\\/\\\/developer.okta.com\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/jimena-garbarino\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Monitor Your Java Apps with Spring Boot Actuator - Java Code Geeks","description":"Interested to learn about Spring Boot Actuator? Check our article explaining how to Monitor Your Java Apps with Spring Boot Actuator","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html","og_locale":"en_US","og_type":"article","og_title":"Monitor Your Java Apps with Spring Boot Actuator - Java Code Geeks","og_description":"Interested to learn about Spring Boot Actuator? Check our article explaining how to Monitor Your Java Apps with Spring Boot Actuator","og_url":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_published_time":"2019-09-02T06:51:21+00:00","article_modified_time":"2019-09-11T09:01:09+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","type":"image\/jpeg"}],"author":"Jimena Garbarino","twitter_card":"summary_large_image","twitter_creator":"@javacodegeeks","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Jimena Garbarino","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html"},"author":{"name":"Jimena Garbarino","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/a5a31a2e38d1966173d27442b15e8f5b"},"headline":"Monitor Your Java Apps with Spring Boot Actuator","datePublished":"2019-09-02T06:51:21+00:00","dateModified":"2019-09-11T09:01:09+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html"},"wordCount":1116,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","keywords":["Spring","Spring Boot","Spring Boot Actuator"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html","url":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html","name":"Monitor Your Java Apps with Spring Boot Actuator - Java Code Geeks","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","datePublished":"2019-09-02T06:51:21+00:00","dateModified":"2019-09-11T09:01:09+00:00","description":"Interested to learn about Spring Boot Actuator? Check our article explaining how to Monitor Your Java Apps with Spring Boot Actuator","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","width":150,"height":150,"caption":"spring-interview-questions-answers"},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2019\/09\/monitor-your-java-apps-spring-boot-actuator.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"Monitor Your Java Apps with Spring Boot Actuator"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/a5a31a2e38d1966173d27442b15e8f5b","name":"Jimena Garbarino","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/9d90792ef285836e7872046fa29035c7bae53493e466020b6dfa490464afc278?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/9d90792ef285836e7872046fa29035c7bae53493e466020b6dfa490464afc278?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9d90792ef285836e7872046fa29035c7bae53493e466020b6dfa490464afc278?s=96&d=mm&r=g","caption":"Jimena Garbarino"},"sameAs":["https:\/\/developer.okta.com"],"url":"https:\/\/www.javacodegeeks.com\/author\/jimena-garbarino"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/96977","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/100428"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=96977"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/96977\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/240"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=96977"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=96977"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=96977"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}