Skip to content

Commit

Permalink
Add a sample name filter for in-/excluding metrics by name or name pr…
Browse files Browse the repository at this point in the history
…efix (#680)

Signed-off-by: Fabian Stäber <fabian@fstab.de>
  • Loading branch information
fstab committed Aug 26, 2021
1 parent 17c98eb commit e68daf2
Show file tree
Hide file tree
Showing 35 changed files with 1,898 additions and 458 deletions.
12 changes: 10 additions & 2 deletions README.md
Expand Up @@ -672,9 +672,14 @@ There are HTTPServer, Servlet, SpringBoot, and Vert.x integrations included in t
The simplest of these is the HTTPServer:

```java
HTTPServer server = new HTTPServer(1234);
HTTPServer server = new HTTPServer.Builder()
.withPort(1234)
.build();
```

The `HTTPServer.Builder` supports configuration of a `SampleNameFilter` which can be used to
restrict the time series being exported by name.

To add Prometheus exposition to an existing HTTP server using servlets, see the `MetricsServlet`.
It also serves as a simple example of how to write a custom endpoint.

Expand All @@ -689,11 +694,14 @@ server.setHandler(context);
context.addServlet(new ServletHolder(new MetricsServlet()), "/metrics");
```

Like the HTTPServer, the `MetricsServlet` can be configured with a `SampleNameFilter` which can
be used to restrict the time series being exported by name. See `integration_tests/servlet_jakarta_exporter_webxml/`
for an example how to configure this in `web.xml`.

All HTTP exposition integrations support restricting which time series to return
using `?name[]=` URL parameters. Due to implementation limitations, this may
have false negatives.


## Exporting to a Pushgateway

The [Pushgateway](https://github.com/prometheus/pushgateway)
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/example_application/pom.xml
Expand Up @@ -30,7 +30,7 @@
</dependencies>

<build>
<finalName>${artifactId}</finalName>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/exemplars_otel/pom.xml
Expand Up @@ -54,7 +54,7 @@
</dependencies>

<build>
<finalName>${artifactId}</finalName>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
1 change: 1 addition & 0 deletions integration_tests/pom.xml
Expand Up @@ -26,6 +26,7 @@
<module>exemplars_otel_agent</module>
<module>example_application</module>
<module>java_versions</module>
<module>servlet_jakarta_exporter_webxml</module>
</modules>

<build>
Expand Down
116 changes: 116 additions & 0 deletions integration_tests/servlet_jakarta_exporter_webxml/pom.xml
@@ -0,0 +1,116 @@
<?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">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.prometheus</groupId>
<artifactId>integration_tests</artifactId>
<version>0.11.1-SNAPSHOT</version>
</parent>

<artifactId>servlet_jakarta_exporter_webxml</artifactId>
<name>Integration Test - Servlet Jakarta Exporter web.xml</name>
<packaging>war</packaging>

<properties>
<otel.version>1.2.0</otel.version>
</properties>

<dependencies>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_servlet_jakarta</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>

<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
</project>
@@ -0,0 +1,27 @@
package io.prometheus.client.it.servlet.jakarta;

import io.prometheus.client.hotspot.DefaultExports;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;

public class ExampleServlet extends HttpServlet {

@Override
public void init() {
DefaultExports.initialize();
}

@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
resp.setStatus(200);
resp.setContentType("text/plain");
Writer writer = new BufferedWriter(resp.getWriter());
writer.write("Hello, world!\n");
writer.close();
}
}
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<!-- Anyone still using web.xml in 2021? -->
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>io.prometheus.client.it.servlet.jakarta.ExampleServlet</servlet-class>
<load-on-startup/>
</servlet>
<filter>
<filter-name>prometheus</filter-name>
<filter-class>io.prometheus.client.servlet.jakarta.filter.MetricsFilter</filter-class>
<init-param>
<param-name>metric-name</param-name>
<param-value>requests</param-value>
</init-param>
</filter>
<servlet>
<servlet-name>prometheus-exporter</servlet-name>
<servlet-class>io.prometheus.client.servlet.jakarta.exporter.MetricsServlet</servlet-class>
<init-param>
<param-name>name-must-not-start-with</param-name>
<param-value>
jvm_threads_deadlocked
jvm_memory_pool
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>prometheus-exporter</servlet-name>
<url-pattern>/metrics</url-pattern>
</servlet-mapping>
<filter-mapping>
<filter-name>prometheus</filter-name>
<servlet-name>example</servlet-name>
</filter-mapping>
</web-app>
@@ -0,0 +1,113 @@
package io.prometheus.client.it.servlet.jakarta;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

public class ServletJakartaExporterWebXmlIT {

private final OkHttpClient client = new OkHttpClient();

private static class DockerContainer extends GenericContainer<DockerContainer> {
DockerContainer() {
super(new ImageFromDockerfile("servlet-jakarta-exporter-webxml")
.withFileFromPath("servlet_jakarta_exporter_webxml.war", Paths.get("target/servlet_jakarta_exporter_webxml.war"))
.withFileFromClasspath("Dockerfile", "Dockerfile"));
}
}

@Rule
public DockerContainer dockerContainer = new DockerContainer()
.withExposedPorts(8080)
.waitingFor(Wait.forLogMessage(".*oejs.Server:main: Started Server.*", 1));

@Test
public void testSampleNameFilter() throws IOException, InterruptedException {
callExampleServlet();
List<String> metrics = scrapeMetrics();
assertContains(metrics, "requests_bucket");
assertContains(metrics, "requests_count");
assertContains(metrics, "requests_sum");
assertContains(metrics, "requests_created");
assertContains(metrics, "requests_status_total");
assertContains(metrics, "requests_status_created");
assertContains(metrics, "jvm_gc_collection_seconds_count");
assertNotContains(metrics, "jvm_threads_deadlocked");
assertNotContains(metrics, "jvm_memory_pool");

List<String> filteredMetrics = scrapeMetricsWithNameFilter("requests_count", "requests_sum");
assertNotContains(filteredMetrics, "requests_bucket");
assertContains(filteredMetrics, "requests_count");
assertContains(filteredMetrics, "requests_sum");
assertNotContains(filteredMetrics, "requests_created");
assertNotContains(filteredMetrics, "requests_status_total");
assertNotContains(filteredMetrics, "requests_status_created");
assertNotContains(filteredMetrics, "jvm_gc_collection_seconds_count");
assertNotContains(filteredMetrics, "jvm_threads_deadlocked");
assertNotContains(filteredMetrics, "jvm_memory_pool");
}

private void assertContains(List<String> metrics, String prefix) {
for (String metric : metrics) {
if (metric.startsWith(prefix)) {
return;
}
}
Assert.fail("metric not found: " + prefix);
}
private void assertNotContains(List<String> metrics, String prefix) {
for (String metric : metrics) {
if (metric.startsWith(prefix)) {
Assert.fail("unexpected metric found: " + metric);
}
}
}
private void callExampleServlet() throws IOException {
Request request = new Request.Builder()
.url("http://localhost:" + dockerContainer.getMappedPort(8080) + "/hello")
.build();
try (Response response = client.newCall(request).execute()) {
Assert.assertEquals("Hello, world!\n", response.body().string());
}
}

private List<String> scrapeMetrics() throws IOException {
Request request = new Request.Builder()
.url("http://localhost:" + dockerContainer.getMappedPort(8080) + "/metrics")
.header("Accept", "application/openmetrics-text; version=1.0.0; charset=utf-8")
.build();
try (Response response = client.newCall(request).execute()) {
return Arrays.asList(response.body().string().split("\\n"));
}
}

private List<String> scrapeMetricsWithNameFilter(String... names) throws IOException {
StringBuilder param = new StringBuilder();
boolean first = true;
for (String name : names) {
if (!first) {
param.append("&");
}
param.append("name[]=").append(name);
first = false;
}
Request request = new Request.Builder()
.url("http://localhost:" + dockerContainer.getMappedPort(8080) + "/metrics?" + param)
.header("Accept", "application/openmetrics-text; version=1.0.0; charset=utf-8")
.build();
try (Response response = client.newCall(request).execute()) {
return Arrays.asList(response.body().string().split("\\n"));
}
}
}
@@ -0,0 +1,2 @@
FROM jetty:11.0.6
COPY servlet_jakarta_exporter_webxml.war /var/lib/jetty/webapps/ROOT.war
@@ -0,0 +1,14 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>

<root level="info">
<appender-ref ref="STDOUT"/>
</root>

<logger name="org.testcontainers" level="INFO"/>
<logger name="com.github.dockerjava" level="WARN"/>
</configuration>
4 changes: 0 additions & 4 deletions pom.xml
Expand Up @@ -88,10 +88,6 @@
<artifactId>maven-install-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
Expand Down

0 comments on commit e68daf2

Please sign in to comment.