Java monitoring over SSH

2 minute read | By Denis Fuenzalida, Rodrigo Gallazzi

This post will cover how to remotely connect to the JVM when running on Azure App Service with Java.

Overview

We can connect to a remote running Java VM and inspect the Java virtual machine and Management Extensions (JMX) in real time, using port forwarding over SSH (the same technique used for debugging Java apps.)

The internals of the JVM can be explored using different tools, including JConsole (bundled with the JDK), Java Mission Control (from Oracle, free) and Visual VM (free, now part of the GraalVM project)

Configuration for Java (standalone) and Apache Tomcat

We’ll use port 1234 for debugging and standalone Web app running on Java 17 on App Service. The same technique can be also used with Apache Tomcat.

Create JAVA_OPTS on the portal with the following value (all in one line): -Dcom.sun.management.jmxremote=true -Djava.rmi.server.hostname=localhost -Dcom.sun.management.jmxremote.port=1234 -Dcom.sun.management.jmxremote.rmi.port=1234 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.local.only=true

Add environment variables

Using Kudu, you can confirm that port 1234 is listening for connections. Open a new SSH connection and run the following commands: On Alpine-based images (Java 8, Java 11, Tomcat 8.5, Tomcat 9) the command is:

netstat -lp | grep java

netstat command

On Ubuntu-based images (Java 17, Tomcat 10), install the net-tools package to have the netstat command, then run netstat -ap | grep java instead:

netstat command 2

Create a remote connection and SSH tunnel

Run in one terminal:

az webapp create-remote-connection --resource-group myjava17jmxdemo-rg --name myjava17jmxdemo --port 9000

Run in another terminal:

ssh -L 1234:127.0.0.1:1234 root@127.0.0.1 -p 9000 -m hmac-sha1

Enter the password shown in the output of the “az webapp” command when asked

password screenshot

Now you can use several alternatives to connect to your application and inspect the runtime.

JConsole (bundled with the JDK)

Run Jconsole, then create a remote connection to 127.0.0.1:1234

Jconsole connection

Click on “Insecure connection” (it’s over the SSH tunnel so it’s OK)

Jconsole connection

Beans annotated to be exposed through JMX will show up under Mbeans (see the Spring Boot code example futher down the page):

Jconsole Beans

Java Mission Control JDK Mission Control-Oracle.com-

Create a new JVM connection. Hit Finish, open the connection “localhost:1234” and double-click on “Mbean server”:

JVM Connection

JMC Screen

Visual VM Visual VM Homepage

Launch with: visualvm.exe --jdkhome "C:\Program Files\Microsoft\jdk-17.0.6.10-hotspot"

Add a new JMX connection:

VisualVM

VisualVM screen

Exposing beans / resources through JMX

Example of a JMX-enabled resource with a minimal Spring Boot app:

 package com.example.demo;

 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.jmx.export.annotation.ManagedResource;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;

 @SpringBootApplication
 @RestController
 @ManagedResource
 public class DemoApplication {
    @GetMapping("/")

    public String root() {
        return "it works!";
    }

    @GetMapping("/hello")
        public String hello(@RequestParam(value="name", defaultValue="World") String name) {
        String reply = String.format("Hello, %s!", name);
        return reply;
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
 }

See also

JMX Ports - Baeldung Spring Boot Reference Documentation