June 12th, 2025
0 reactions

Monitor your Quarkus native application on Azure

Introduction

Quarkus is a general-purpose Java framework focused on efficient use of resources, fast startup, and rapid development. It allows developers to create and run services in the Java Virtual Machine (JVM) or native binary executables (native mode).

In this blog post we are going to focus on using Quarkus to create and monitor a native mode Quarkus application on Azure.

A simple service will be created from scratch to demonstrate the capability of sending OpenTelemetry data to Azure. 

Why native?

Quarkus Native Mode refers to building and running a Quarkus application as a native binary executable, using GraalVM to compile the application Ahead Of Time (AOT) into a platform-specific binary. That is, there is no JVM required at runtime.

Recommended scenarios for native mode:

  • Fast start-up required (function)
  • No JDK/JRE available on the host machine
  • Reduced attack surface (No reflection, less classes loaded, See: https://www.graalvm.org/why-graalvm )  
  • Applications running for a short amount of time (function)
  • High density deployments with many instances running in parallel
  • Application with a low CPU or memory limits
  • Applications running with constrained resources like:
      • Edge services
      • IoT services 

Disadvantages:

  • Longer build times
  • Increased memory at build time
  • Reduced GC options. GraalVM CE only supports the serial and epsilon garbage collectors (GC). The Serial GC is single threaded, and the Epsilon GC is a no-op garbage collector used to disable garbage collection if your application is allocation free.
  • Compared with a warmed-up application running on the JVM, the throughput is usually less

Create a demo application

Generate the application

We’ll be building a service from scratch, but Quarkus has a quick start process to help us!

Let’s go to https://code.quarkus.io/, a website where Quarkus projects can be easily generated. For this case, you only need to select the following extensions:

    • quarkus-rest-jackson – REST server with Jackson serialization capabilities.
    • quarkus-hibernate-orm – data persistence
    • quarkus-agroal – DB connection pool
    • quarkus-jdbc-h2  – H2 in-memory DB connection
    • quarkus-opentelemetry-exporter-azure – OpenTelemetry extension including the telemetry data exporter for Azure

Generate a Quarkus application

Click on “Generate your application” and download the zip file.

Once saved and extracted on your local machine, please open the project on your IDE of choice.

The application will provide a REST endpoint and will access an in-memory database. OpenTelemetry tracing already instruments the REST Server and the Database connection manager. 

Other Quarkus extensions (Kafka, REST, …) can provide additional instrumentation and OpenTelemetry data.

Modify the GreetingResource class

We’ll modify the bundled GreetingResource class to add an entry to the in-memory database.

@Path("/hello")
public class GreetingResource {

    @Inject
    EntityManager manager;

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    @Transactional
    public String hello() {
        MyEntity o = new MyEntity();
        o.field = "data to store";
        manager.persist(o);
        return "Hello from Quarkus REST";
    }
}

Configure database properties

Add the following properties to the application.properties file to allow you application to access to the H2 database:

quarkus.datasource.kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:default
quarkus.hibernate-orm.database.generation=drop-and-create

Configure the telemetry features

Enable additional telemetry

We are going to enable OpenTelemetry metrics and logs. In addition, we will enable the JDBC telemetry to provide OpenTelemetry JDBC spans that is to say SQL executions in the OpenTelemetry trace.

Update your application.properties file:

quarkus.otel.logs.enabled=true
quarkus.log.metrics.enabled=true
quarkus.datasource.jdbc.telemetry=true

Also add the OpenTelemetry JDBC dependency to the pom.xml:

<dependency>
   <groupId>io.opentelemetry.instrumentation</groupId>
   <artifactId>opentelemetry-jdbc</artifactId>
</dependency>

Configure the Application Insights connection string

Create an Azure Application Insights resource if you don’t have one.

Go to the Azure portal, search for your Application Insights resource, or create a new one. On the overview page of your resource, you will find a connection string in the top right corner.

Copy the connection string

Create an APPLICATIONINSIGHTS_CONNECTION_STRING environment variable with the connection string value. 

The connection string must be kept private and must not be committed to public repositories.

Build and run Java native executable

To build the native executable you need to:

  1. Have Graalvm CE 23.0.x.x+ from Oracle Labs installed, or Mandrel 23.1.x.x from Red Hat. 
  2. Have Maven 3.9.8+ or Gradle 8.10+ installed.
  3. All this can be installed with Sdkman
  4. Set the GRAALVM_HOME environment variable to the location of your GraalVM installation. Example for Mac and Linux: 
export GRAALVM_HOME=/Users/me/.sdkman/candidates/java/23.0.2-graalce

To build the executable, you just need to execute:

./mvn clean package -Dnative

You can then execute your native executable locally with:

./target/<your app name>-1.0.0-SNAPSHOT-runner

 You can use this command to call the application endpoint:

curl -X POST http://localhost:8080/hello

Visualize the telemetry data on Azure

We can now come back to our Application Insights resource.

On the left menu, we click on Application map.

We can then see that our application is calling an H2 database.

Application map image

Now we click on Transaction search on the left menu.

The Application Insights UI displays the OpenTelemetry data the following way:

OpenTelemetry Application Insights
Server spans Requests
Log Traces
Other Span Types (Client, Internal, etc.) Dependencies

On the Transaction search we can spot that our Quarkus native application has started up very quickly from the following application log:

Log start up image

We can also see the POST call together with SQL activities (get connection, select, insert):

Request and dependencies image

Now, we double click on the POST request. 

We can see that SELECT and INSERT statements executed from the POST call:

End to end select image

End to end insert image

Deploy a Java native image on Azure

Build a container image

You can create a container image with a Quarkus native executable by following this Docker file example.

Deploy the container image on Azure

You can now deploy the image to the Azure service of your choice: Azure App Service, Azure Kubernetes Service, Azure Container Apps or Azure Functions. You will need to set the APPLICATIONINSIGHTS_CONNECTION_STRING or the QUARKUS_OTEL_AZURE_APPLICATIONINSIGHTS_CONNECTION_STRING environment variable with the connections string value retrieved from the Azure environment. The quarkus-opentelemetry-exporter-azure extension will work on all Azure services.

You can find an example on how to deploy a Quarkus native image application on Azure Container Apps in this Github repository.

Conclusion

In this blog post we explain how to enable and send OpenTelemetry telemetry to Azure with a Quarkus native application. You can find the code for the blog post demo here. We look forward to hearing more of your feedback! Please drop us a mail at JavaO11y@microsoft.com or create a GitHub issue.

Category
Java

Author

Jean Bisutti is a software engineer at Microsoft working on Azure Monitor.

Bruno Baptista
Principal Engineer

Bruno Baptista is a Principal Engineer at RedHat with over 15 years as an enterprise-level engineer. Currently, he focuses on observability, leveraging tools such as Micrometer and OpenTelemetry within the Quarkus team.

0 comments