Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public class WeatherService {
@Path("/day/status")
@Counted(monotonic = true, name = "weather_day_status", absolute = true,
displayName = "Weather Day Status",
description = "This metric shows the weather status of the day.")
description = "This metric shows the weather status of the day.",
tags = {"weather=day"})
@GET
@Produces(MediaType.TEXT_PLAIN)
public String dayStatus() {
Expand Down
125 changes: 125 additions & 0 deletions examples/mp-metrics-gauge/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
= MicroProfile Metrics Gauge

:index-group: MicroProfile
:jbake-type: page
:jbake-status: published

This is an example on how to use MicroProfile metrics in TomEE.

.Run the application:
mvn clean install tomee:run

Within the application there is an endpoint that will give you the weather temperature in celsius for the day.

.For the day temperature call:
GET http://localhost:8080/mp-metrics-gauge/weather/day/temperature

.Response:
30

[discrete]
==== Gauge Feature

MicroProfile metrics has a gauge feature. The gauge value and type is equal to the annotated method return value and type.

To use this feature you need to annotate the JAX-RS resource method with @Gauge.

....
@Path("/weather")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApplicationScoped
public class WeatherService {

@Path("/day/temperature")
@Gauge(name = "weather_day_temperature", absolute = true, unit = "celsius",
displayName = "Weather Day Temperature",
description = "This metric shows the day temperature.",
tags = {"weather=temperature"})
@GET
@Produces(MediaType.TEXT_PLAIN)
public Integer dayTemperature() {
return 30;
}
}
....

There are some configurations, as part of @Gauge, that you need to know:

*String name*
Optional. Sets the name of the metric. If not explicitly given the name of the annotated object is used.

*boolean absolute*
If true, uses the given name as the absolute name of the metric. If false, prepends the package name and class name before the given name. Default value is false.

*String displayName*
Optional. A human-readable display name for metadata.

*String description*
Optional. A description of the metric.

*String[] tags*
Optional. Array of Strings in the +++<key>+++=+++<value>+++format to supply special tags to a metric.+++</value>++++++</key>+++

*String unit*
Unit of the metric. Check the MetricUnits class for a set of pre-defined units.

[discrete]
==== Metric data

Check the gauge metric doing a _GET_ request:

.Prometheus format:
GET http://localhost:8080/mp-metrics-gauge/metrics/application/weather_day_temperature

.Response:
# TYPE application:weather_day_temperature_celsius gauge
application:weather_day_temperature_celsius{weather="temperature"} 30.0

[discrete]
===== JSON Format:

For json format add the header _Accept=application/json_ to the request.

{
"weather_day_temperature": 30
}

[discrete]
==== Metric metadata

A metric will have a metadata so you can know more information about it, like displayName, description, tags e etc.

Check the metric metadata doing a _OPTIONS_ request:

.Request
OPTIONS http://localhost:8080/mp-metrics-gauge/metrics/application/weather_day_temperature

.Response:
{
"weather_day_temperature": {
"unit": "celsius",
"displayName": "Weather Day Temperature",
"name": "weather_day_temperature",
"typeRaw": "GAUGE",
"description": "This metric shows the day temperature.",
"type": "gauge",
"value": {
"unit": "celsius",
"displayName": "Weather Day Temperature",
"name": "weather_day_temperature",
"tagsAsString": "weather=\"temperature\"",
"typeRaw": "GAUGE",
"description": "This metric shows the day temperature.",
"type": "gauge",
"reusable": false,
"tags": {
"weather": "temperature"
}
},
"reusable": false,
"tags": "weather=temperature"
}
}

You can also try it out using the WeatherServiceTest.java available in the project.
69 changes: 69 additions & 0 deletions examples/mp-metrics-gauge/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>examples</artifactId>
<groupId>org.apache.tomee</groupId>
<version>8.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>mp-metrics-gauge</artifactId>
<packaging>war</packaging>

<dependencies>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>javaee-api</artifactId>
<version>${version.javaee-api}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.metrics</groupId>
<artifactId>microprofile-metrics-api</artifactId>
<version>${microprofile.metrics.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-cxf-rs</artifactId>
<version>${tomee.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<version>${version.arquillian.bom}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>arquillian-tomee-remote</artifactId>
<version>${tomee.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>apache-tomee</artifactId>
<version>${tomee.version}</version>
<type>zip</type>
<classifier>microprofile</classifier>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.tomee.maven</groupId>
<artifactId>tomee-maven-plugin</artifactId>
<version>${project.version}</version>
<configuration>
<tomeeClassifier>microprofile</tomeeClassifier>
<context>${artifactId}</context>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.superbiz.rest;

import org.eclipse.microprofile.metrics.annotation.Gauge;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/weather")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApplicationScoped
public class WeatherService {

@Path("/day/temperature")
@Gauge(name = "weather_day_temperature", absolute = true, unit = "celsius",
displayName = "Weather Day Temperature",
description = "This metric shows the day temperature.",
tags = {"weather=temperature"})
@GET
@Produces(MediaType.TEXT_PLAIN)
public Integer dayTemperature() {
return 30;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package org.superbiz.rest; /**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.json.Json;
import javax.json.JsonObject;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.StringReader;
import java.net.URL;
import java.util.stream.Stream;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

@RunWith(Arquillian.class)
public class WeatherServiceTest {

@Deployment(testable = false)
public static WebArchive createDeployment() {
final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war")
.addClass(WeatherService.class)
.addAsWebInfResource(new StringAsset("<beans/>"), "beans.xml");
return webArchive;
}

@ArquillianResource
private URL base;

private Client client;

@Before
public void before() {
this.client = ClientBuilder.newClient();
}

@After
public void after() {
this.client.close();
}

@Test
public void testGaugeMetric() {
WebTarget webTarget = this.client.target(this.base.toExternalForm());
final Integer temperature = webTarget
.path("/weather/day/temperature")
.request()
.get(Integer.class);
assertEquals(Integer.valueOf(30), temperature);

final String metricPath = "/metrics/application/weather_day_temperature";
assertPrometheusFormat(metricPath);
assertJsonFormat(metricPath);
}

private void assertPrometheusFormat(final String metricPath) {
WebTarget webTarget = this.client.target(this.base.toExternalForm());
final String metric = webTarget
.path(metricPath)
.request()
.accept(MediaType.TEXT_PLAIN)
.get(String.class);
assertEquals("# TYPE application:weather_day_temperature_celsius gauge\napplication:weather_day_temperature_celsius{weather=\"temperature\"} 30.0\n", metric);
}

private void assertJsonFormat(final String metricPath) {
WebTarget webTarget = this.client.target(this.base.toExternalForm());
final String metric = webTarget
.path(metricPath)
.request()
.accept(MediaType.APPLICATION_JSON)
.get(String.class);

assertNotNull(metric);

JsonObject metricJson = Json.createReader(new StringReader(metric)).readObject();
JsonObject weatherDayTemperature = metricJson.getJsonObject("weather_day_temperature");
assertNotNull(weatherDayTemperature);
assertEquals(weatherDayTemperature.getInt("value"), 30);
}

@Test
public void testGaugeMetricMetadata() {
WebTarget webTarget = this.client.target(this.base.toExternalForm());
final Response response = webTarget
.path("/metrics/application/weather_day_temperature")
.request()
.accept(MediaType.APPLICATION_JSON)
.options();

final String metaData = response.readEntity(String.class);
JsonObject metadataJson = Json.createReader(new StringReader(metaData)).readObject();

String[] expectedKeys = {
"description",
"displayName",
"name",
"reusable",
"tags",
"type",
"typeRaw",
"unit"
};

Stream.of(expectedKeys)
.forEach(text ->
assertTrue(
"Expected: " + text + " to be present in " + metaData,
metadataJson.getJsonObject("weather_day_temperature").get(text) != null));
}
}
Loading