diff --git a/examples/mp-metrics-counted/src/main/java/org/superbiz/rest/WeatherService.java b/examples/mp-metrics-counted/src/main/java/org/superbiz/rest/WeatherService.java index ec856fe80a6..99fb2ca3732 100644 --- a/examples/mp-metrics-counted/src/main/java/org/superbiz/rest/WeatherService.java +++ b/examples/mp-metrics-counted/src/main/java/org/superbiz/rest/WeatherService.java @@ -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() { diff --git a/examples/mp-metrics-gauge/README.adoc b/examples/mp-metrics-gauge/README.adoc new file mode 100644 index 00000000000..84ec2096667 --- /dev/null +++ b/examples/mp-metrics-gauge/README.adoc @@ -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 ++++++=++++++format to supply special tags to a metric.++++++++++++ + +*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. diff --git a/examples/mp-metrics-gauge/pom.xml b/examples/mp-metrics-gauge/pom.xml new file mode 100644 index 00000000000..064d29caeca --- /dev/null +++ b/examples/mp-metrics-gauge/pom.xml @@ -0,0 +1,69 @@ + + + + examples + org.apache.tomee + 8.0.0-SNAPSHOT + + 4.0.0 + + mp-metrics-gauge + war + + + + org.apache.tomee + javaee-api + ${version.javaee-api} + provided + + + org.eclipse.microprofile.metrics + microprofile-metrics-api + ${microprofile.metrics.version} + provided + + + org.apache.tomee + openejb-cxf-rs + ${tomee.version} + test + + + org.jboss.arquillian.junit + arquillian-junit-container + ${version.arquillian.bom} + test + + + org.apache.tomee + arquillian-tomee-remote + ${tomee.version} + test + + + org.apache.tomee + apache-tomee + ${tomee.version} + zip + microprofile + test + + + + + + + org.apache.tomee.maven + tomee-maven-plugin + ${project.version} + + microprofile + ${artifactId} + + + + + \ No newline at end of file diff --git a/examples/mp-metrics-gauge/src/main/java/org/superbiz/rest/WeatherService.java b/examples/mp-metrics-gauge/src/main/java/org/superbiz/rest/WeatherService.java new file mode 100644 index 00000000000..3309ab5f97a --- /dev/null +++ b/examples/mp-metrics-gauge/src/main/java/org/superbiz/rest/WeatherService.java @@ -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; + } +} diff --git a/examples/mp-metrics-gauge/src/test/java/org/superbiz/rest/WeatherServiceTest.java b/examples/mp-metrics-gauge/src/test/java/org/superbiz/rest/WeatherServiceTest.java new file mode 100644 index 00000000000..9e9a8fa9d90 --- /dev/null +++ b/examples/mp-metrics-gauge/src/test/java/org/superbiz/rest/WeatherServiceTest.java @@ -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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.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)); + } +} diff --git a/examples/mp-metrics-gauge/src/test/resources/arquillian.xml b/examples/mp-metrics-gauge/src/test/resources/arquillian.xml new file mode 100644 index 00000000000..3029d48c033 --- /dev/null +++ b/examples/mp-metrics-gauge/src/test/resources/arquillian.xml @@ -0,0 +1,30 @@ + + + + + + + -1 + -1 + microprofile + target/apache-tomee-remote + target/arquillian-test-working-dir + + + \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index b4591a77cad..3a9998b9ad3 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -111,6 +111,7 @@ mp-metrics-counted mp-metrics-histogram mp-metrics-timed + mp-metrics-gauge mp-rest-jwt mp-rest-jwt-jwk mp-rest-jwt-public-key @@ -239,11 +240,19 @@ java9 - [9,) + 9 - - java-modules - + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + +