Skip to content

Commit

Permalink
Adds new module for Prometheus PushGateway (#848)
Browse files Browse the repository at this point in the history
PR adds support for  Prometheus PushGateway. Once module is included in classpath, it will be enabled by default.

Added:

- new module
- docs for new module
- tests for new module
  • Loading branch information
n0tl3ss authored Oct 25, 2024
1 parent edc76cc commit df795b1
Show file tree
Hide file tree
Showing 14 changed files with 479 additions and 1 deletion.
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ managed-metrics-core = '4.2.28'
managed-micrometer = '1.13.4'
managed-micrometer-tracing = '1.3.4'
managed-new-relic-registry = '0.10.0'
managed-prometheus-metrics-exporter-pushgateway = '1.3.1'
reflections = '0.10.2'

jcache = "1.1.1"
Expand Down Expand Up @@ -47,6 +48,8 @@ boms-micrometer = { module = 'io.micrometer:micrometer-bom', version.ref = 'mana
grpc-api = { module = 'io.grpc:grpc-api' }
hdr-histogram = { module = 'org.hdrhistogram:HdrHistogram', version.ref = 'hdr-histogram' }

managed-prometheus-metrics-exporter-pushgateway = { module = 'io.prometheus:prometheus-metrics-exporter-pushgateway', version.ref = 'managed-prometheus-metrics-exporter-pushgateway' }

managed-metrics-core = { module = 'io.dropwizard.metrics:metrics-core', version.ref = 'managed-metrics-core' }
managed-micrometer-registry-new-relic-telemetry = { module = 'com.newrelic.telemetry:micrometer-registry-new-relic', version.ref = 'managed-new-relic-registry' }
managed-micrometer-core = { module = 'io.micrometer:micrometer-core', version.ref = 'managed-micrometer' }
Expand Down
16 changes: 16 additions & 0 deletions micrometer-registry-prometheus-pushgateway/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id 'io.micronaut.build.internal.micrometer-registry'
}

dependencies {
api libs.micrometer.registry.prometheus
api projects.micronautMicrometerRegistryPrometheus
api libs.managed.prometheus.metrics.exporter.pushgateway
testImplementation(mnSerde.micronaut.serde.jackson)
}

micronautBuild {
binaryCompatibility {
enabledAfter '5.9.0' // adjust as needed
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright 2017-2024 original authors
*
* Licensed 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
*
* https://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.
*/
package io.micronaut.configuration.metrics.micrometer.prometheus.pushgateway;

import io.micronaut.configuration.metrics.micrometer.prometheus.PrometheusMeterRegistryFactory;
import io.micronaut.context.annotation.BootstrapContextCompatible;
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.StringUtils;
import io.prometheus.metrics.exporter.pushgateway.PushGateway;

import java.time.Duration;
import java.util.Map;

/**
* Configuration for the {@link PushGateway}.
*/
@ConfigurationProperties(PrometheusPushGatewayFactory.PROMETHEUS_PUSHGATEWAY_CONFIG)
@BootstrapContextCompatible
@Requires(property = PrometheusPushGatewayFactory.PROMETHEUS_PUSHGATEWAY_ENABLED, value = StringUtils.TRUE, defaultValue = StringUtils.TRUE)
@Requires(property = PrometheusMeterRegistryFactory.PROMETHEUS_ENABLED, value = StringUtils.TRUE, defaultValue = StringUtils.TRUE)
@Internal
final class PrometheusPushGatewayConfig {

@ConfigurationBuilder(prefixes = "", excludes = {"basicAuth", "groupingKey"})
final PushGateway.Builder builder = PushGateway.builder();

private String basicAuthUsername;
private String basicAuthPassword;
private boolean enabled;
private Duration interval;
private Duration initialDelay;
private Map<String, String> groupingKeys;

/**
* @return Map of the grouping keys.
*/
@Nullable
public Map<String, String> getGroupingKeys() {
return groupingKeys;
}

/**
* @param groupingKeys Map of the grouping keys.
*/
public void setGroupingKeys(@Nullable Map<String, String> groupingKeys) {
this.groupingKeys = groupingKeys;
}

/**
* @return username for basic auth.
*/
@Nullable
public String getBasicAuthUsername() {
return basicAuthUsername;
}

/**
* @param basicAuthUsername the username for basic auth.
*/
public void setBasicAuthUsername(@Nullable String basicAuthUsername) {
this.basicAuthUsername = basicAuthUsername;
}

/**
* @return password for the basic auth.
*/
@Nullable
public String getBasicAuthPassword() {
return basicAuthPassword;
}

/**
* @param basicAuthPassword the password for basic auth.
*/
public void setBasicAuthPassword(@Nullable String basicAuthPassword) {
this.basicAuthPassword = basicAuthPassword;
}

/**
* @return interval of {@link PrometheusPushGatewayScheduler#pushData()}.
*/
@Nullable
public Duration getInterval() {
return interval;
}

/**
* @param interval interval for {@link PrometheusPushGatewayScheduler#pushData()}.
*/
public void setInterval(@Nullable Duration interval) {
this.interval = interval;
}

/**
* @return initialDelay of {@link PrometheusPushGatewayScheduler#pushData()}.
*/
@Nullable
public Duration getInitialDelay() {
return initialDelay;
}

/**
* @param initialDelay interval for {@link PrometheusPushGatewayScheduler#pushData()}.
*/
public void setInitialDelay(@Nullable Duration initialDelay) {
this.initialDelay = initialDelay;
}

/**
* @return is PushGateway feature enabled.
*/
public boolean isEnabled() {
return enabled;
}

/**
* @param enabled is PushGateway feature enabled.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2017-2019 original authors
*
* Licensed 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
*
* https://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.
*/
package io.micronaut.configuration.metrics.micrometer.prometheus.pushgateway;

import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.util.StringUtils;
import io.prometheus.metrics.exporter.pushgateway.PushGateway;
import jakarta.inject.Singleton;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_EXPORT;

/**
* Creates a Prometheus {@link PushGateway}.
*/
@Factory
@Internal
class PrometheusPushGatewayFactory {

public static final String PROMETHEUS_PUSHGATEWAY_CONFIG = MICRONAUT_METRICS_EXPORT + ".prometheus.pushgateway";
public static final String PROMETHEUS_PUSHGATEWAY_ENABLED = PROMETHEUS_PUSHGATEWAY_CONFIG + ".enabled";

/**
* Create a PushGateway bean if global metrics are enabled
* , Prometheus is enabled and PushGateway is enabled. Will be true by default when this
* configuration is included in project.
*
* @return PushGateway
*/
@Singleton
@Requires(beans = PrometheusPushGatewayConfig.class)
@Requires(beans = PrometheusMeterRegistry.class)
PushGateway pushGateway(PrometheusMeterRegistry prometheusMeterRegistry, PrometheusPushGatewayConfig prometheusRegistryConfig) {
PushGateway.Builder builder = prometheusRegistryConfig.builder.registry(prometheusMeterRegistry.getPrometheusRegistry());

if (!StringUtils.isEmpty(prometheusRegistryConfig.getBasicAuthUsername()) && !StringUtils.isEmpty(prometheusRegistryConfig.getBasicAuthPassword())) {
builder.basicAuth(prometheusRegistryConfig.getBasicAuthUsername(), prometheusRegistryConfig.getBasicAuthPassword());
}

if (prometheusRegistryConfig.getGroupingKeys() != null && !prometheusRegistryConfig.getGroupingKeys().isEmpty()) {
prometheusRegistryConfig.getGroupingKeys().forEach(builder::groupingKey);
}

return builder.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2017-2024 original authors
*
* Licensed 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
*
* https://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.
*/
package io.micronaut.configuration.metrics.micrometer.prometheus.pushgateway;

import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.Internal;
import io.micronaut.scheduling.annotation.Scheduled;
import io.prometheus.metrics.exporter.pushgateway.PushGateway;
import jakarta.inject.Singleton;

import java.io.IOException;

/**
* PrometheusPushGatewayScheduler pushes data to Prometheus pushGateway.
*/
@Singleton
@Requires(beans = PushGateway.class)
@Internal
final class PrometheusPushGatewayScheduler {

private final PushGateway pushGateway;

PrometheusPushGatewayScheduler(PushGateway pushGateway) {
this.pushGateway = pushGateway;
}

@Scheduled(
fixedDelay = "${micronaut.metrics.export.prometheus.pushgateway.interval:1m}",
initialDelay = "${micronaut.metrics.export.prometheus.pushgateway.initial-delay:1m}")
void pushData() throws IOException {
pushGateway.push();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2017-2019 original authors
*
* Licensed 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
*
* https://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.
*/
/**
* Micronaut integration with Prometheus PushGateway.
*/
@Configuration
@Requires(property = PrometheusPushGatewayFactory.PROMETHEUS_PUSHGATEWAY_ENABLED, notEquals = StringUtils.FALSE)
@Requires(property = PrometheusMeterRegistryFactory.PROMETHEUS_ENABLED, notEquals = StringUtils.FALSE)
package io.micronaut.configuration.metrics.micrometer.prometheus.pushgateway;

import io.micronaut.configuration.metrics.micrometer.prometheus.PrometheusMeterRegistryFactory;
import io.micronaut.context.annotation.Configuration;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.util.StringUtils;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.micronaut.configuration.metrics.micrometer.prometheus.controllers

import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Body
import io.micronaut.http.annotation.Consumes
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Header
import io.micronaut.http.annotation.Put

@Controller("/metrics/job/test")
class MetricsTestController {

private static String authHeader = ""

@Get
String hello() {
return authHeader
}

@Put
@Consumes(MediaType.TEXT_PLAIN)
String test(@Body String body, @Header("Authorization") auth) {
MetricsTestController.authHeader = auth
return "OK"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.micronaut.configuration.metrics.micrometer.prometheus.pushgateway

import io.micrometer.core.instrument.MeterRegistry
import io.micronaut.context.ApplicationContext
import io.prometheus.metrics.exporter.pushgateway.PushGateway
import spock.lang.Specification
import spock.lang.Unroll

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_ENABLED
import static PrometheusPushGatewayFactory.PROMETHEUS_PUSHGATEWAY_ENABLED

class PrometheusPushGatewayFactorySpec extends Specification {

void "verify PushGateway is created by default when this configuration used"() {
when:
ApplicationContext context = ApplicationContext.run()

then:
context.getBeansOfType(MeterRegistry).size() == 1
context.getBeansOfType(MeterRegistry)*.class*.simpleName.containsAll(['PrometheusMeterRegistry'])
context.getBeansOfType(PushGateway)

cleanup:
context.stop()
}

@Unroll
void "verify PrometheusMeterRegistry bean exists = #result when config #cfg = #setting"() {
when:
ApplicationContext context = ApplicationContext.run([(cfg): setting])

then:
context.findBean(PushGateway).isPresent() == result

cleanup:
context.stop()

where:
cfg | setting | result
MICRONAUT_METRICS_ENABLED | false | false
MICRONAUT_METRICS_ENABLED | true | true
PROMETHEUS_PUSHGATEWAY_ENABLED | true | true
PROMETHEUS_PUSHGATEWAY_ENABLED | false | false
}
}
Loading

0 comments on commit df795b1

Please sign in to comment.