From 63f0cb8683e2e0ab6a12d5887bd109bdc276a13b Mon Sep 17 00:00:00 2001 From: Paul Ambrose Date: Sat, 29 Apr 2017 22:31:16 -0700 Subject: [PATCH] 1.2.0 dev (#3) * Add admin servlets (ping, threaddump, healthcheck) --- .travis.yml | 2 +- Makefile | 5 +- README.md | 26 +++- bin/docker-agent.sh | 4 +- bin/docker-proxy.sh | 4 +- etc/compose/proxy.yml | 3 +- etc/config/config.conf | 16 +++ etc/docker/agent.df | 1 + etc/docker/proxy.df | 1 + examples/simple.conf | 2 + pom.xml | 48 ++++++- src/main/java/io/prometheus/Agent.java | 52 ++++--- src/main/java/io/prometheus/Proxy.java | 66 +++++---- .../io/prometheus/agent/AgentOptions.java | 8 +- .../java/io/prometheus/agent/PathContext.java | 2 +- .../io/prometheus/common/AdminConfig.java | 40 ++++++ .../io/prometheus/common/AdminService.java | 72 ++++++++++ .../io/prometheus/common/BaseOptions.java | 40 ++++-- .../java/io/prometheus/common/ConfigVals.java | 38 ++++- .../java/io/prometheus/common/EnvVars.java | 4 +- .../io/prometheus/common/GenericService.java | 135 ++++++++++++++---- .../common/GenericServiceListener.java | 14 +- .../io/prometheus/common/MetricsService.java | 47 +++--- src/main/java/io/prometheus/common/Utils.java | 35 ++++- .../common/ZipkinReporterService.java | 20 ++- src/main/java/io/prometheus/package-info.java | 2 +- .../proxy/AgentContextCleanupService.java | 48 ++++--- .../io/prometheus/proxy/ProxyGrpcService.java | 42 ++++-- .../io/prometheus/proxy/ProxyHttpService.java | 18 ++- .../io/prometheus/proxy/ProxyOptions.java | 8 +- src/main/resources/reference.conf | 4 + src/test/java/io/prometheus/AdminTest.java | 108 ++++++++++++++ .../java/io/prometheus/AutoValueTest.java | 80 +++++++---- ...a => InProcessTestNoAdminMetricsTest.java} | 12 +- ...=> InProcessTestWithAdminMetricsTest.java} | 9 +- ....java => NettyTestNoAdminMetricsTest.java} | 8 +- ...ava => NettyTestWithAdminMetricsTest.java} | 8 +- src/test/java/io/prometheus/OptionsTest.java | 2 +- .../java/io/prometheus/TestConstants.java | 4 +- src/test/java/io/prometheus/TestUtils.java | 37 +++-- src/test/java/io/prometheus/Tests.java | 29 ++-- 41 files changed, 832 insertions(+), 272 deletions(-) create mode 100644 src/main/java/io/prometheus/common/AdminConfig.java create mode 100644 src/main/java/io/prometheus/common/AdminService.java create mode 100644 src/test/java/io/prometheus/AdminTest.java rename src/test/java/io/prometheus/{InProcessTestWithMetricsTest.java => InProcessTestNoAdminMetricsTest.java} (86%) rename src/test/java/io/prometheus/{InProcessTestNoMetricsTest.java => InProcessTestWithAdminMetricsTest.java} (88%) rename src/test/java/io/prometheus/{NettyTestNoMetricsTest.java => NettyTestNoAdminMetricsTest.java} (89%) rename src/test/java/io/prometheus/{NettyTestWithMetricsTest.java => NettyTestWithAdminMetricsTest.java} (90%) diff --git a/.travis.yml b/.travis.yml index 3a0b6d69..75026073 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,4 +8,4 @@ notifications: - pambrose@mac.com after_success: - - ./mvnw clean test jacoco:report coveralls:report + - ./mvnw jacoco:report coveralls:report diff --git a/Makefile b/Makefile index 08dd8d9c..7c79988a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=1.1.0 +VERSION=1.2.0 default: build @@ -25,6 +25,9 @@ build-coverage: report-coverage: ./mvnw -DrepoToken=${COVERALLS_TOKEN} clean package test jacoco:report coveralls:report +sonar: + ./mvnw sonar:sonar -Dsonar.host.url=http://localhost:9000 + distro: build mkdir target/distro mv target/proxy-jar-with-dependencies.jar target/distro/prometheus-proxy.jar diff --git a/README.md b/README.md index a0570bb9..de440fd4 100644 --- a/README.md +++ b/README.md @@ -84,24 +84,24 @@ scrape_configs: The docker images are available via: ```bash -$ docker pull pambrose/prometheus-proxy:1.1.0 -$ docker pull pambrose/prometheus-agent:1.1.0 +$ docker pull pambrose/prometheus-proxy:1.2.0 +$ docker pull pambrose/prometheus-agent:1.2.0 ``` Start the proxy and an agent in separate shells on your local machine: ```bash -$ docker run --rm -p 8082:8082 -p 50051:50051 -p 8080:8080 \ +$ docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ -e HOSTNAME=${HOSTNAME} \ -e METRICS_ENABLED=true \ - pambrose/prometheus-proxy:1.1.0 + pambrose/prometheus-proxy:1.2.0 ``` ```bash -$ docker run --rm -p 8083:8083 \ +$ docker run --rm -p 8083:8083 -p 8093:8093 \ -e HOSTNAME=${HOSTNAME} \ -e AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-agent:1.1.0 + pambrose/prometheus-agent:1.2.0 ``` Using the config file [simple.conf](https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf), @@ -129,6 +129,8 @@ The only required argument is an Agent config value, which should have an `agent | -c --config | PROXY_CONFIG | | | Agent config file or url | | -p --port | PROXY_PORT | proxy.http.port | 8080 | Proxy listen port | | -a --agent_port | AGENT_PORT | proxy.agent.port | 50051 | Grpc listen port | +| -r --admin | ADMIN_ENABLED | proxy.admin.enabled | false | Enable admin servlets | +| -i --admin_port | ADMIN_PORT | proxy.admin.port | 8092 | Admin servlets port | | -e --metrics | METRICS_ENABLED | proxy.metrics.enabled | false | Enable proxy metrics | | -m --metrics_port | METRICS_PORT | proxy.metrics.port | 8082 | Proxy metrics listen port | | -v --version | | | | Print version info and exit | @@ -143,6 +145,8 @@ The only required argument is an Agent config value, which should have an `agent | -c --config | AGENT_CONFIG | | | Agent config file or url (required) | | -p --proxy | PROXY_HOSTNAME | agent.proxy.hostname | | Proxy hostname (can include :port) | | -n --name | AGENT_NAME | agent.name | | Agent name | +| -r --admin | ADMIN_ENABLED | agent.admin.enabled | false | Enable admin servlets | +| -i --admin_port | ADMIN_PORT | agent.admin.port | 8093 | Admin servlets port | | -e --metrics | METRICS_ENABLED | agent.metrics.enabled | false | Enable agent metrics | | -m --metrics_port | METRICS_PORT | agent.metrics.port | 8083 | Agent metrics listen port | | -v --version | | | | Print version info and exit | @@ -157,6 +161,16 @@ Misc notes: * Option values are evaluated in the order: CLI, enviroment vars, and finally config file vals * Property values can be set as a java -D arg to or as a proxy or agent jar -D arg. +### Admin Servlets + +Three admin servlets are available when the `proxy.admin.enabled` or `agent.admin.enabled` properties are enabled: + * /ping + * /threaddump + * /healthcheck + +Descriptions of the servlets are [here](http://metrics.dropwizard.io/3.2.2/manual/servlets.html). +The path names can be changed in the configuration file. + ## Grafana [Grafana](https://grafana.com) dashboards for the Proxy and Agent are [here](https://github.com/pambrose/prometheus-proxy/tree/master/grafana). diff --git a/bin/docker-agent.sh b/bin/docker-agent.sh index e285d17e..96fdfa8c 100755 --- a/bin/docker-agent.sh +++ b/bin/docker-agent.sh @@ -1,6 +1,6 @@ #!/bin/sh -docker run --rm -p 8083:8083 \ +docker run --rm -p 8083:8083 -p 8093:8093 \ -e HOSTNAME=${HOSTNAME} \ -e AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-agent:1.1.0 \ No newline at end of file + pambrose/prometheus-agent:1.2.0 \ No newline at end of file diff --git a/bin/docker-proxy.sh b/bin/docker-proxy.sh index ae18ac9e..252d28ef 100755 --- a/bin/docker-proxy.sh +++ b/bin/docker-proxy.sh @@ -1,6 +1,6 @@ #!/bin/sh -docker run --rm -p 8082:8082 -p 50051:50051 -p 8080:8080 \ +docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ -e HOSTNAME=${HOSTNAME} \ -e PROXY_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-proxy:1.1.0 \ No newline at end of file + pambrose/prometheus-proxy:1.2.0 \ No newline at end of file diff --git a/etc/compose/proxy.yml b/etc/compose/proxy.yml index 73164bfc..fff03e58 100644 --- a/etc/compose/proxy.yml +++ b/etc/compose/proxy.yml @@ -1,9 +1,10 @@ prometheus-proxy: autoredeploy: true - image: 'pambrose/prometheus-proxy:1.1.0' + image: 'pambrose/prometheus-proxy:1.2.0' ports: - '8080:8080' - '8082:8082' + - '8092:8092' - '50051:50051' environment: - PROXY_CONFIG=https://dl.dropboxusercontent.com/u/481551/prometheus/cloud-proxy.conf diff --git a/etc/config/config.conf b/etc/config/config.conf index 6f73cc0d..f6ea2d35 100644 --- a/etc/config/config.conf +++ b/etc/config/config.conf @@ -10,6 +10,14 @@ proxy { agent.port = 50051 // Listen port for agent connections + admin { + enabled = false // Enable Admin servlets + port = 8092 // Admin servlets port + pingPath = "ping" // Ping servlet path + healthCheckPath = "healthcheck" // HealthCheck servlet path + theadtDumpPath = "threaddump" // ThreadDump servlet path + } + metrics { enabled = false // Enable Proxy metrics port = 8082 // Listen port for metrics endpoint @@ -62,6 +70,14 @@ agent { port = 50051 } + admin { + enabled = false // Enable Admin servlets + port = 8093 // Admin servlets port + pingPath = "ping" // Ping servlet path + healthCheckPath = "healthcheck" // HealthCheck servlet path + theadtDumpPath = "threaddump" // ThreadDump servlet path + } + metrics { enabled = false // Enable Agent metrics port = 8083 // Listen port for metrics endpoint diff --git a/etc/docker/agent.df b/etc/docker/agent.df index 34438251..1461dc79 100644 --- a/etc/docker/agent.df +++ b/etc/docker/agent.df @@ -9,5 +9,6 @@ RUN ./mvnw -DskipTests=true clean package && \ rm -rf /root/.m2 .mvn etc src target/*.jar target/protoc* target/maven* target/generated* target/archive* target/test-classes EXPOSE 8083 +expose 8093 ENTRYPOINT ["/prometheus-proxy/target/bin/agent.sh"] diff --git a/etc/docker/proxy.df b/etc/docker/proxy.df index 34c0c14d..64f5ccfe 100644 --- a/etc/docker/proxy.df +++ b/etc/docker/proxy.df @@ -10,6 +10,7 @@ RUN ./mvnw -DskipTests=true clean package && \ EXPOSE 8080 EXPOSE 8082 +EXPOSE 8092 EXPOSE 50051 ENTRYPOINT ["/prometheus-proxy/target/bin/proxy.sh"] \ No newline at end of file diff --git a/examples/simple.conf b/examples/simple.conf index d0906be0..3d9acf0b 100644 --- a/examples/simple.conf +++ b/examples/simple.conf @@ -1,9 +1,11 @@ proxy { + admin.enabled: true metrics.enabled: true } agent { proxy.hostname = ${HOSTNAME} + admin.enabled: true metrics.enabled: true pathConfigs: [ diff --git a/pom.xml b/pom.xml index f9fcd897..ca95050d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.prometheus prometheus-proxy - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT 1.3.1 @@ -21,6 +21,7 @@ 3.7.0 1.4.1 0.3.0 + 3.2.2 4.2.0 0.7.0 @@ -117,6 +118,30 @@ + + io.dropwizard.metrics + metrics-core + ${metrics.version} + + + + io.dropwizard.metrics + metrics-healthchecks + ${metrics.version} + + + + io.dropwizard.metrics + metrics-servlets + ${metrics.version} + + + + io.dropwizard.metrics + metrics-jvm + ${metrics.version} + + com.squareup.okhttp3 okhttp @@ -256,6 +281,27 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + 1.4.1 + + + enforce-maven + + enforce + + + + + ${maven.version} + + + + + + + maven-jar-plugin ${maven-jar-plugin.version} diff --git a/src/main/java/io/prometheus/Agent.java b/src/main/java/io/prometheus/Agent.java index 2e2b58de..2f96d285 100644 --- a/src/main/java/io/prometheus/Agent.java +++ b/src/main/java/io/prometheus/Agent.java @@ -4,9 +4,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.ServiceManager; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.protobuf.Empty; import io.grpc.ClientInterceptor; @@ -22,9 +20,9 @@ import io.prometheus.agent.PathContext; import io.prometheus.agent.RequestFailureException; import io.prometheus.client.Summary; +import io.prometheus.common.AdminConfig; import io.prometheus.common.ConfigVals; import io.prometheus.common.GenericService; -import io.prometheus.common.GenericServiceListener; import io.prometheus.common.MetricsConfig; import io.prometheus.common.Utils; import io.prometheus.common.ZipkinConfig; @@ -98,15 +96,18 @@ public class Agent private final BlockingQueue scrapeResponseQueue; private final RateLimiter reconnectLimiter; private final List> pathConfigs; - private final ServiceManager serviceManager; - public Agent(final AgentOptions options, - final MetricsConfig metricsConfig, - final ZipkinConfig zipkinConfig, - final String inProcessServerName, - final boolean testMode) { - super(options.getConfigVals(), metricsConfig, zipkinConfig, testMode); + public Agent(final AgentOptions options, final String inProcessServerName, final boolean testMode) { + super(options.getConfigVals(), + AdminConfig.create(options.isAdminEnabled(), + options.getAdminPort(), + options.getConfigVals().agent.admin), + MetricsConfig.create(options.isMetricsEnabled(), + options.getMetricsPort(), + options.getConfigVals().agent.metrics), + ZipkinConfig.create(options.getConfigVals().agent.internal.zipkin), + testMode); this.inProcessServerName = inProcessServerName; this.agentName = isNullOrEmpty(options.getAgentName()) ? format("Unnamed-%s", Utils.getHostName()) @@ -131,9 +132,9 @@ public Agent(final AgentOptions options, this.pathConfigs = this.getConfigVals().pathConfigs.stream() .map(v -> ImmutableMap.of("name", v.name, "path", v.path, - "url", v.url)) + "pingUrl", v.url)) .peek(v -> logger.info("Proxy path /{} will be assigned to {}", - v.get("path"), v.get("url"))) + v.get("path"), v.get("pingUrl"))) .collect(Collectors.toList()); @@ -149,25 +150,17 @@ public Agent(final AgentOptions options, this.resetGrpcStubs(); - this.serviceManager = new ServiceManager(this.newServiceList()); - this.serviceManager.addListener(this.newListener()); - - logger.info("Created {}", this); + this.init(); } public static void main(final String[] argv) throws IOException, InterruptedException { final AgentOptions options = new AgentOptions(argv, true); - final MetricsConfig metricsConfig = MetricsConfig.create(options.getMetricsEnabled(), - options.getMetricsPort(), - options.getConfigVals().agent.metrics); - final ZipkinConfig zipkinConfig = ZipkinConfig.create(options.getConfigVals().agent.internal.zipkin); logger.info(Utils.getBanner("banners/agent.txt")); logger.info(Utils.getVersionDesc()); - final Agent agent = new Agent(options, metricsConfig, zipkinConfig, null, false); - agent.addListener(new GenericServiceListener(agent), MoreExecutors.directExecutor()); + final Agent agent = new Agent(options, null, false); agent.startAsync(); } @@ -205,6 +198,13 @@ protected void run() { } } + @Override + protected void registerHealtChecks() { + super.registerHealtChecks(); + this.getHealthCheckRegistry().register("scrape_response_queue_check", + Utils.queueHealthCheck(scrapeResponseQueue, 25)); + } + @Override protected String serviceName() { return format("%s %s", this.getClass().getSimpleName(), this.agentName); } @@ -383,7 +383,7 @@ private void registerPaths() throws RequestFailureException { for (final Map agentConfig : this.pathConfigs) { final String path = agentConfig.get("path"); - final String url = agentConfig.get("url"); + final String url = agentConfig.get("pingUrl"); this.registerPath(path, url); } } @@ -569,11 +569,9 @@ public String toString() { return MoreObjects.toStringHelper(this) .add("agentId", this.getAgentId()) .add("agentName", this.agentName) - .add("metricsPort", - this.isMetricsEnabled() ? this.getMetricsService().getPort() : "Disabled") - .add("metricsPath", - this.isMetricsEnabled() ? "/" + this.getMetricsService().getPath() : "Disabled") .add("proxyHost", this.getProxyHost()) + .add("adminService", this.isAdminEnabled() ? this.getAdminService() : "Disabled") + .add("metricsService", this.isMetricsEnabled() ? this.getMetricsService() : "Disabled") .toString(); } } diff --git a/src/main/java/io/prometheus/Proxy.java b/src/main/java/io/prometheus/Proxy.java index e3e3b6a6..891d63bf 100644 --- a/src/main/java/io/prometheus/Proxy.java +++ b/src/main/java/io/prometheus/Proxy.java @@ -2,12 +2,10 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.Maps; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.ServiceManager; import io.grpc.Attributes; +import io.prometheus.common.AdminConfig; import io.prometheus.common.ConfigVals; import io.prometheus.common.GenericService; -import io.prometheus.common.GenericServiceListener; import io.prometheus.common.MetricsConfig; import io.prometheus.common.Utils; import io.prometheus.common.ZipkinConfig; @@ -33,52 +31,53 @@ public class Proxy private static final Logger logger = LoggerFactory.getLogger(Proxy.class); - public static final String AGENT_ID = "agent-id"; - public static final Attributes.Key ATTRIB_AGENT_ID = Attributes.Key.of(AGENT_ID); - private final Map agentContextMap = Maps.newConcurrentMap(); // Map agent_id to AgentContext - private final Map pathMap = Maps.newConcurrentMap(); // Map path to AgentContext - private final Map scrapeRequestMap = Maps.newConcurrentMap(); // Map scrape_id to agent_id + public static final String AGENT_ID = "agent-id"; + public static final Attributes.Key ATTRIB_AGENT_ID = Attributes.Key.of(AGENT_ID); + + private final Map agentContextMap = Maps.newConcurrentMap(); // Map agent_id to AgentContext + private final Map pathMap = Maps.newConcurrentMap(); // Map path to AgentContext + private final Map scrapeRequestMap = Maps.newConcurrentMap(); // Map scrape_id to agent_id private final ProxyMetrics metrics; private final ProxyGrpcService grpcService; private final ProxyHttpService httpService; private final AgentContextCleanupService agentCleanupService; - private final ServiceManager serviceManager; public Proxy(final ProxyOptions options, - final MetricsConfig metricsConfig, - final ZipkinConfig zipkinConfig, final int proxyPort, final String inProcessServerName, final boolean testMode) throws IOException { - super(options.getConfigVals(), metricsConfig, zipkinConfig, testMode); + super(options.getConfigVals(), + AdminConfig.create(options.isAdminEnabled(), + options.getAdminPort(), + options.getConfigVals().proxy.admin), + MetricsConfig.create(options.isMetricsEnabled(), + options.getMetricsPort(), + options.getConfigVals().proxy.metrics), + ZipkinConfig.create(options.getConfigVals().proxy.internal.zipkin), + testMode); this.metrics = this.isMetricsEnabled() ? new ProxyMetrics(this) : null; this.grpcService = isNullOrEmpty(inProcessServerName) ? ProxyGrpcService.create(this, options.getAgentPort()) : ProxyGrpcService.create(this, inProcessServerName); this.httpService = new ProxyHttpService(this, proxyPort); - this.agentCleanupService = new AgentContextCleanupService(this); + this.agentCleanupService = this.getConfigVals().internal.staleAgentCheckEnabled + ? new AgentContextCleanupService(this) : null; - this.serviceManager = new ServiceManager(this.newServiceList(this.httpService, this.agentCleanupService)); - this.serviceManager.addListener(this.newListener()); + this.addServices(this.grpcService, this.httpService, this.agentCleanupService); - logger.info("Created {}", this); + this.init(); } public static void main(final String[] argv) throws IOException, InterruptedException { final ProxyOptions options = new ProxyOptions(argv); - final MetricsConfig metricsConfig = MetricsConfig.create(options.getMetricsEnabled(), - options.getMetricsPort(), - options.getConfigVals().proxy.metrics); - final ZipkinConfig zipkinConfig = ZipkinConfig.create(options.getConfigVals().proxy.internal.zipkin); logger.info(Utils.getBanner("banners/proxy.txt")); logger.info(Utils.getVersionDesc()); - final Proxy proxy = new Proxy(options, metricsConfig, zipkinConfig, options.getProxyPort(), null, false); - proxy.addListener(new GenericServiceListener(proxy), MoreExecutors.directExecutor()); + final Proxy proxy = new Proxy(options, options.getProxyPort(), null, false); proxy.startAsync(); } @@ -88,7 +87,11 @@ protected void startUp() super.startUp(); this.grpcService.startAsync(); this.httpService.startAsync(); - this.agentCleanupService.startAsync(); + + if (this.agentCleanupService != null) + this.agentCleanupService.startAsync(); + else + logger.info("Agent eviction thread not started"); } @Override @@ -96,7 +99,8 @@ protected void shutDown() throws Exception { this.grpcService.stopAsync(); this.httpService.stopAsync(); - this.agentCleanupService.stopAsync(); + if (this.agentCleanupService != null) + this.agentCleanupService.stopAsync(); super.shutDown(); } @@ -107,6 +111,14 @@ protected void run() { } } + @Override + protected void registerHealtChecks() { + super.registerHealtChecks(); + this.getHealthCheckRegistry().register("scrape_response_map_check", + Utils.mapHealthCheck(scrapeRequestMap, 25)); + this.getHealthCheckRegistry().register("grpc_service", this.grpcService.getHealthCheck()); + } + public void addAgentContext(final AgentContext agentContext) { this.agentContextMap.put(agentContext.getAgentId(), agentContext); } @@ -211,11 +223,9 @@ public int getTotalAgentRequestQueueSize() { @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("metricsPort", - this.isMetricsEnabled() ? this.getMetricsService().getPort() : "Disabled") - .add("metricsPath", - this.isMetricsEnabled() ? "/" + this.getMetricsService().getPath() : "Disabled") .add("proxyPort", this.httpService.getPort()) + .add("adminService", this.isAdminEnabled() ? this.getAdminService() : "Disabled") + .add("metricsService", this.isMetricsEnabled() ? this.getMetricsService() : "Disabled") .toString(); } } diff --git a/src/main/java/io/prometheus/agent/AgentOptions.java b/src/main/java/io/prometheus/agent/AgentOptions.java index e14e0c3c..7fec4739 100644 --- a/src/main/java/io/prometheus/agent/AgentOptions.java +++ b/src/main/java/io/prometheus/agent/AgentOptions.java @@ -1,13 +1,13 @@ package io.prometheus.agent; import com.beust.jcommander.Parameter; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import io.prometheus.Agent; import io.prometheus.common.BaseOptions; import io.prometheus.common.ConfigVals; import io.prometheus.common.EnvVars; +import java.util.Collections; import java.util.List; import static io.prometheus.common.EnvVars.AGENT_CONFIG; @@ -23,7 +23,7 @@ public class AgentOptions private String agentName = null; public AgentOptions(final List args, final boolean exitOnMissingConfig) { - this(Iterables.toArray(args != null ? args : ImmutableList.of(), String.class), exitOnMissingConfig); + this(Iterables.toArray(args != null ? args : Collections.emptyList(), String.class), exitOnMissingConfig); } public AgentOptions(final String[] argv, final boolean exitOnMissingConfig) { @@ -44,8 +44,10 @@ protected void assignConfigVals(final ConfigVals configVals) { if (this.agentName == null) this.agentName = EnvVars.AGENT_NAME.getEnv(configVals.agent.name); + this.assignAdminEnabled(configVals.agent.admin.enabled); + this.assignAdminPort(configVals.agent.admin.port); + this.assignMetricsEnabled(configVals.agent.metrics.enabled); this.assignMetricsPort(configVals.agent.metrics.port); - this.assignEnableMetrics(configVals.agent.metrics.enabled); } public String getProxyHostname() { return this.proxyHostname; } diff --git a/src/main/java/io/prometheus/agent/PathContext.java b/src/main/java/io/prometheus/agent/PathContext.java index a6973553..ad14521b 100644 --- a/src/main/java/io/prometheus/agent/PathContext.java +++ b/src/main/java/io/prometheus/agent/PathContext.java @@ -55,7 +55,7 @@ public Response fetchUrl(final ScrapeRequest scrapeRequest) public String toString() { return MoreObjects.toStringHelper(this) .add("path", "/" + path) - .add("url", url) + .add("pingUrl", url) .toString(); } } diff --git a/src/main/java/io/prometheus/common/AdminConfig.java b/src/main/java/io/prometheus/common/AdminConfig.java new file mode 100644 index 00000000..80e26079 --- /dev/null +++ b/src/main/java/io/prometheus/common/AdminConfig.java @@ -0,0 +1,40 @@ +package io.prometheus.common; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class AdminConfig { + public static AdminConfig create(final boolean enabled, + final int port, + final ConfigVals.Proxy2.Admin2 admin) { + + return new AutoValue_AdminConfig(enabled, + port, + admin.pingPath, + admin.healthCheckPath, + admin.theadtDumpPath); + } + + public static AdminConfig create(final boolean enabled, + final int port, + final ConfigVals.Agent.Admin admin) { + + return new AutoValue_AdminConfig(enabled, + port, + admin.pingPath, + admin.healthCheckPath, + admin.theadtDumpPath); + } + + public abstract boolean enabled(); + + public abstract int port(); + + public abstract String pingPath(); + + public abstract String healthCheckPath(); + + public abstract String theadtDumpPath(); +} + + diff --git a/src/main/java/io/prometheus/common/AdminService.java b/src/main/java/io/prometheus/common/AdminService.java new file mode 100644 index 00000000..8f2814ff --- /dev/null +++ b/src/main/java/io/prometheus/common/AdminService.java @@ -0,0 +1,72 @@ +package io.prometheus.common; + +import com.codahale.metrics.servlets.HealthCheckServlet; +import com.codahale.metrics.servlets.PingServlet; +import com.codahale.metrics.servlets.ThreadDumpServlet; +import com.google.common.base.MoreObjects; +import com.google.common.util.concurrent.AbstractIdleService; +import com.google.common.util.concurrent.MoreExecutors; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.lang.String.format; + +public class AdminService + extends AbstractIdleService { + + private final int port; + private final String pingPath; + private final String healthCheckPath; + private final String threadDumpPath; + private final Server server; + + public AdminService(final GenericService service, + final int port, + final String pingPath, + final String healthCheckPath, + final String threadDumpPath) { + this.port = port; + this.pingPath = pingPath; + this.healthCheckPath = healthCheckPath; + this.threadDumpPath = threadDumpPath; + this.server = new Server(this.port); + + final ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + this.server.setHandler(context); + + if (!isNullOrEmpty(this.pingPath)) + context.addServlet(new ServletHolder(new PingServlet()), "/" + this.pingPath); + if (!isNullOrEmpty(this.healthCheckPath)) + context.addServlet(new ServletHolder(new HealthCheckServlet(service.getHealthCheckRegistry())), + "/" + this.healthCheckPath); + if (!isNullOrEmpty(this.threadDumpPath)) + context.addServlet(new ServletHolder(new ThreadDumpServlet()), + "/" + this.threadDumpPath); + + this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); + } + + @Override + protected void startUp() + throws Exception { + this.server.start(); + } + + @Override + protected void shutDown() + throws Exception { + this.server.stop(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("ping", format(":%d /%s", this.port, this.pingPath)) + .add("healthcheck", format(":%d /%s", this.port, this.healthCheckPath)) + .add("threaddump", format(":%d /%s", this.port, this.threadDumpPath)) + .toString(); + } +} diff --git a/src/main/java/io/prometheus/common/BaseOptions.java b/src/main/java/io/prometheus/common/BaseOptions.java index c7a0dad1..64f83380 100644 --- a/src/main/java/io/prometheus/common/BaseOptions.java +++ b/src/main/java/io/prometheus/common/BaseOptions.java @@ -16,6 +16,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import static io.prometheus.common.EnvVars.ADMIN_ENABLED; +import static io.prometheus.common.EnvVars.ADMIN_PORT; import static io.prometheus.common.EnvVars.METRICS_ENABLED; import static io.prometheus.common.EnvVars.METRICS_PORT; import static java.lang.String.format; @@ -31,12 +33,16 @@ public abstract class BaseOptions { private final String programName; private final ConfigVals configVals; - @Parameter(names = {"-c", "--conf", "--config"}, description = "Configuration file or url") + @Parameter(names = {"-c", "--conf", "--config"}, description = "Configuration file or pingUrl") private String configName = null; - @Parameter(names = {"-m", "--metrics_port"}, description = "Metrics listen port") - private Integer metricsPort = null; + @Parameter(names = {"-r", "--admin"}, description = "Admin servlets enabled") + private Boolean adminEnabled = null; + @Parameter(names = {"-i", "--admin_port"}, description = "Admin servlets port") + private Integer adminPort = null; @Parameter(names = {"-e", "--metrics"}, description = "Metrics enabled") private Boolean metricsEnabled = null; + @Parameter(names = {"-m", "--metrics_port"}, description = "Metrics listen port") + private Integer metricsPort = null; @Parameter(names = {"-v", "--version"}, description = "Print version info and exit", validateWith = Utils.VersionValidator.class) private boolean version = false; @Parameter(names = {"-u", "--usage"}, help = true) @@ -74,14 +80,24 @@ private void parseArgs(final String[] argv) { } } - protected void assignMetricsPort(final int configVal) { - if (this.metricsPort == null) - this.metricsPort = METRICS_PORT.getEnv(configVal); + protected void assignAdminEnabled(final boolean defaultVal) { + if (this.adminEnabled == null) + this.adminEnabled = ADMIN_ENABLED.getEnv(defaultVal); + } + + protected void assignAdminPort(final int defaultVal) { + if (this.adminPort == null) + this.adminPort = ADMIN_PORT.getEnv(defaultVal); } - protected void assignEnableMetrics(final boolean configVal) { + protected void assignMetricsEnabled(final boolean defaultVal) { if (this.metricsEnabled == null) - this.metricsEnabled = METRICS_ENABLED.getEnv(configVal); + this.metricsEnabled = METRICS_ENABLED.getEnv(defaultVal); + } + + protected void assignMetricsPort(final int defaultVal) { + if (this.metricsPort == null) + this.metricsPort = METRICS_PORT.getEnv(defaultVal); } private void readConfig(final String envConfig, final boolean exitOnMissingConfig) { @@ -105,9 +121,13 @@ private void readConfig(final String envConfig, final boolean exitOnMissingConfi }); } - public int getMetricsPort() { return this.metricsPort; } + public boolean isAdminEnabled() { return this.adminEnabled; } - public boolean getMetricsEnabled() { return this.metricsEnabled; } + public int getAdminPort() { return this.adminPort; } + + public boolean isMetricsEnabled() { return this.metricsEnabled; } + + public int getMetricsPort() { return this.metricsPort; } public Map getDynamicParams() { return this.dynamicParams; } } diff --git a/src/main/java/io/prometheus/common/ConfigVals.java b/src/main/java/io/prometheus/common/ConfigVals.java index 679eefc7..4672dbca 100644 --- a/src/main/java/io/prometheus/common/ConfigVals.java +++ b/src/main/java/io/prometheus/common/ConfigVals.java @@ -1,4 +1,4 @@ -// generated by tscfg 0.8.0 on Wed Apr 26 23:19:38 PDT 2017 +// generated by tscfg 0.8.0 on Sat Apr 29 11:36:36 PDT 2017 // source: etc/config/config.conf package io.prometheus.common; @@ -12,12 +12,14 @@ public ConfigVals(com.typesafe.config.Config c) { } public static class Agent { + public final Agent.Admin admin; public final Agent.Internal internal; public final Agent.Metrics metrics; public final java.lang.String name; public final java.util.List pathConfigs; public final Agent.Proxy proxy; public Agent(com.typesafe.config.Config c) { + this.admin = new Agent.Admin(c.getConfig("admin")); this.internal = new Agent.Internal(c.getConfig("internal")); this.metrics = new Agent.Metrics(c.getConfig("metrics")); this.name = c.hasPathOrNull("name") ? c.getString("name") : ""; @@ -33,6 +35,22 @@ public Agent(com.typesafe.config.Config c) { return java.util.Collections.unmodifiableList(al); } + public static class Admin { + public final boolean enabled; + public final java.lang.String healthCheckPath; + public final java.lang.String pingPath; + public final int port; + public final java.lang.String theadtDumpPath; + + public Admin(com.typesafe.config.Config c) { + this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); + this.healthCheckPath = c.hasPathOrNull("healthCheckPath") ? c.getString("healthCheckPath") : "healthcheck"; + this.pingPath = c.hasPathOrNull("pingPath") ? c.getString("pingPath") : "ping"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8093; + this.theadtDumpPath = c.hasPathOrNull("theadtDumpPath") ? c.getString("theadtDumpPath") : "threaddump"; + } + } + public static class Internal { public final int heartbeatCheckPauseMillis; public final boolean heartbeatEnabled; @@ -129,17 +147,35 @@ public Proxy(com.typesafe.config.Config c) { } public static class Proxy2 { + public final Proxy2.Admin2 admin; public final Proxy2.Agent2 agent; public final Proxy2.Http http; public final Proxy2.Internal2 internal; public final Proxy2.Metrics2 metrics; public Proxy2(com.typesafe.config.Config c) { + this.admin = new Proxy2.Admin2(c.getConfig("admin")); this.agent = new Proxy2.Agent2(c.getConfig("agent")); this.http = new Proxy2.Http(c.getConfig("http")); this.internal = new Proxy2.Internal2(c.getConfig("internal")); this.metrics = new Proxy2.Metrics2(c.getConfig("metrics")); } + public static class Admin2 { + public final boolean enabled; + public final java.lang.String healthCheckPath; + public final java.lang.String pingPath; + public final int port; + public final java.lang.String theadtDumpPath; + + public Admin2(com.typesafe.config.Config c) { + this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); + this.healthCheckPath = c.hasPathOrNull("healthCheckPath") ? c.getString("healthCheckPath") : "healthcheck"; + this.pingPath = c.hasPathOrNull("pingPath") ? c.getString("pingPath") : "ping"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8092; + this.theadtDumpPath = c.hasPathOrNull("theadtDumpPath") ? c.getString("theadtDumpPath") : "threaddump"; + } + } + public static class Agent2 { public final int port; diff --git a/src/main/java/io/prometheus/common/EnvVars.java b/src/main/java/io/prometheus/common/EnvVars.java index e4694fe9..43be97db 100644 --- a/src/main/java/io/prometheus/common/EnvVars.java +++ b/src/main/java/io/prometheus/common/EnvVars.java @@ -16,8 +16,10 @@ public enum EnvVars { AGENT_NAME, // Common + METRICS_ENABLED, METRICS_PORT, - METRICS_ENABLED; + ADMIN_ENABLED, + ADMIN_PORT; private String getEnv() { return getenv(this.name()); } diff --git a/src/main/java/io/prometheus/common/GenericService.java b/src/main/java/io/prometheus/common/GenericService.java index 0351a177..5d00bc5f 100644 --- a/src/main/java/io/prometheus/common/GenericService.java +++ b/src/main/java/io/prometheus/common/GenericService.java @@ -1,8 +1,16 @@ package io.prometheus.common; +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.health.HealthCheck; +import com.codahale.metrics.health.HealthCheckRegistry; +import com.codahale.metrics.health.jvm.ThreadDeadlockHealthCheck; import com.github.kristofa.brave.Brave; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Lists; import com.google.common.util.concurrent.AbstractExecutionThreadService; +import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.Service; import com.google.common.util.concurrent.ServiceManager; import org.slf4j.Logger; @@ -10,8 +18,8 @@ import java.io.Closeable; import java.io.IOException; -import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import static java.lang.String.format; @@ -21,23 +29,45 @@ public abstract class GenericService private static final Logger logger = LoggerFactory.getLogger(GenericService.class); + private final MetricRegistry metricRegistry = new MetricRegistry(); + private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); + private final List services = Lists.newArrayList(this); private final ConfigVals configVals; + private final boolean testMode; + private final JmxReporter jmxReporter; private final MetricsService metricsService; private final ZipkinReporterService zipkinReporterService; - private final boolean testMode; + private final AdminService adminService; + private ServiceManager serviceManager = null; public GenericService(final ConfigVals configVals, + final AdminConfig adminConfig, final MetricsConfig metricsConfig, final ZipkinConfig zipkinConfig, final boolean testMode) { this.configVals = configVals; this.testMode = testMode; + this.jmxReporter = JmxReporter.forRegistry(this.metricRegistry).build(); + + if (adminConfig.enabled()) { + this.adminService = new AdminService(this, + adminConfig.port(), + adminConfig.pingPath(), + adminConfig.healthCheckPath(), + adminConfig.theadtDumpPath()); + this.addService(this.adminService); + } + else { + logger.info("Admin service disabled"); + this.adminService = null; + } + if (metricsConfig.enabled()) { final int port = metricsConfig.port(); final String path = metricsConfig.path(); - logger.info("Metrics server enabled with {} /{}", port, path); this.metricsService = new MetricsService(port, path); + this.addService(this.metricsService); SystemMetrics.initialize(metricsConfig.standardExportsEnabled(), metricsConfig.memoryPoolsExportsEnabled(), metricsConfig.garbageCollectorExportsEnabled(), @@ -46,38 +76,54 @@ public GenericService(final ConfigVals configVals, metricsConfig.versionInfoExportsEnabled()); } else { - logger.info("Metrics server disabled"); + logger.info("Metrics service disabled"); this.metricsService = null; } if (zipkinConfig.enabled()) { - final String zipkinHost = format("http://%s:%d/%s", - zipkinConfig.hostname(), zipkinConfig.port(), zipkinConfig.path()); - logger.info("Zipkin reporter enabled for {}", zipkinHost); - this.zipkinReporterService = new ZipkinReporterService(zipkinHost, zipkinConfig.serviceName()); + final String zipkinUrl = format("http://%s:%d/%s", + zipkinConfig.hostname(), zipkinConfig.port(), zipkinConfig.path()); + this.zipkinReporterService = new ZipkinReporterService(zipkinUrl, zipkinConfig.serviceName()); + this.addService(this.zipkinReporterService); } else { - logger.info("Zipkin reporter disabled"); + logger.info("Zipkin reporter service disabled"); this.zipkinReporterService = null; } + + this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); + } + + public void init() { + this.serviceManager = new ServiceManager(this.services); + this.serviceManager.addListener(this.newListener()); + this.registerHealtChecks(); } @Override protected void startUp() throws Exception { super.startUp(); + if (this.jmxReporter != null) + this.jmxReporter.start(); if (this.isMetricsEnabled()) this.metricsService.startAsync(); + if (adminService != null) + this.adminService.startAsync(); Runtime.getRuntime().addShutdownHook(Utils.shutDownHookAction(this)); } @Override protected void shutDown() throws Exception { + if (adminService != null) + this.adminService.shutDown(); if (this.isMetricsEnabled()) this.metricsService.stopAsync(); if (this.isZipkinEnabled()) this.zipkinReporterService.shutDown(); + if (this.jmxReporter != null) + this.jmxReporter.stop(); super.shutDown(); } @@ -87,46 +133,75 @@ public void close() this.stopAsync(); } + protected void addService(final Service service) { + this.services.add(service); + } + + protected void addServices(final Service service, final Service... services) { + this.services.addAll(Lists.asList(service, services)); + } + + protected void registerHealtChecks() { + this.getHealthCheckRegistry().register("thread_deadlock", new ThreadDeadlockHealthCheck()); + if (this.isMetricsEnabled()) + this.getHealthCheckRegistry().register("metrics_service", this.metricsService.getHealthCheck()); + + this.getHealthCheckRegistry() + .register( + "all_services_running", + new HealthCheck() { + @Override + protected Result check() + throws Exception { + final ImmutableMultimap sbs = serviceManager.servicesByState(); + return sbs.keySet().size() == 1 && sbs.containsKey(State.RUNNING) + ? Result.healthy() + : Result.unhealthy("Incorrect state: " + + Joiner.on(", ") + .join(sbs.entries() + .stream() + .filter(kv -> kv.getKey() != State.RUNNING) + .peek(kv -> logger.warn("Incorrect state - {}: {}", + kv.getKey(), kv.getValue())) + .map(kv -> format("%s: %s", kv.getKey(), kv.getValue())) + .collect(Collectors.toList()))); + } + }); + } + protected ServiceManager.Listener newListener() { + final String serviceName = this.getClass().getSimpleName(); return new ServiceManager.Listener() { @Override - public void healthy() { - logger.info("All {} services healthy", this.getClass().getSimpleName()); - } + public void healthy() { logger.info("All {} services healthy", serviceName); } @Override - public void stopped() { - logger.info("All {} services stopped", this.getClass().getSimpleName()); - } + public void stopped() { logger.info("All {} services stopped", serviceName); } @Override - public void failure(final Service service) { - logger.info("{} service failed: {}", this.getClass().getSimpleName(), service); - } + public void failure(final Service service) { logger.info("{} service failed: {}", serviceName, service); } }; } - protected List newServiceList(Service... services) { - final List serviceList = Lists.newArrayList(this); - if (this.isMetricsEnabled()) - serviceList.add(this.getMetricsService()); - if (this.isZipkinEnabled()) - serviceList.add(this.getZipkinReporterService()); - serviceList.addAll(Arrays.asList(services)); - return serviceList; - } + protected ConfigVals getGenericConfigVals() { return this.configVals; } - public boolean isMetricsEnabled() { return this.metricsService != null; } + public MetricRegistry getMetricRegistry() { return this.metricRegistry; } + + public HealthCheckRegistry getHealthCheckRegistry() { return this.healthCheckRegistry; } public boolean isTestMode() { return this.testMode; } public boolean isZipkinEnabled() { return this.zipkinReporterService != null; } + public boolean isAdminEnabled() { return this.adminService != null; } + + public boolean isMetricsEnabled() { return this.metricsService != null; } + + protected AdminService getAdminService() { return this.adminService; } + protected MetricsService getMetricsService() { return this.metricsService; } public ZipkinReporterService getZipkinReporterService() { return this.zipkinReporterService; } public Brave getBrave() { return this.getZipkinReporterService().getBrave(); } - - protected ConfigVals getGenericConfigVals() { return this.configVals; } } diff --git a/src/main/java/io/prometheus/common/GenericServiceListener.java b/src/main/java/io/prometheus/common/GenericServiceListener.java index 6e4a19bd..9485e481 100644 --- a/src/main/java/io/prometheus/common/GenericServiceListener.java +++ b/src/main/java/io/prometheus/common/GenericServiceListener.java @@ -9,39 +9,39 @@ public class GenericServiceListener private static final Logger logger = LoggerFactory.getLogger(GenericServiceListener.class); - private final String name; + private final Service service; public GenericServiceListener(Service service) { - this.name = service.getClass().getSimpleName(); + this.service = service; } @Override public void starting() { super.starting(); - logger.info("Starting {}", this.name); + logger.info("Starting {}", this.service); } @Override public void running() { super.running(); - logger.info("{} is running", this.name); + logger.info("Running {}", this.service); } @Override public void stopping(Service.State from) { super.stopping(from); - logger.info("{} is stopping", this.name); + logger.info("Stopping {}", this.service); } @Override public void terminated(Service.State from) { super.terminated(from); - logger.info("{} terminated", this.name); + logger.info("Terminated {}", this.service); } @Override public void failed(Service.State from, Throwable t) { super.failed(from, t); - logger.info("{} failed on {}", this.name, from, t); + logger.info("Failed on {} {}", from, this.service, t); } } diff --git a/src/main/java/io/prometheus/common/MetricsService.java b/src/main/java/io/prometheus/common/MetricsService.java index 6be79143..ff7a4b38 100644 --- a/src/main/java/io/prometheus/common/MetricsService.java +++ b/src/main/java/io/prometheus/common/MetricsService.java @@ -1,48 +1,48 @@ package io.prometheus.common; +import com.codahale.metrics.health.HealthCheck; +import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.AbstractIdleService; +import com.google.common.util.concurrent.MoreExecutors; import io.prometheus.client.exporter.MetricsServlet; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; import static java.lang.String.format; public class MetricsService extends AbstractIdleService { - private static final Logger logger = LoggerFactory.getLogger(MetricsService.class); - private final int port; private final String path; private final Server server; + private final HealthCheck healthCheck = + new HealthCheck() { + @Override + protected Result check() + throws Exception { + return server.isRunning() ? Result.healthy() : Result.unhealthy("Jetty server not running"); + } + }; public MetricsService(final int port, final String path) { this.port = port; this.path = path; this.server = new Server(this.port); - } - @Override - protected void startUp() - throws IOException { final ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); this.server.setHandler(context); context.addServlet(new ServletHolder(new MetricsServlet()), "/" + this.path); - try { - this.server.start(); - final String url = this.url(); - logger.info("Started metrics server started at {}", url); - } - catch (Exception e) { - logger.error("Unsuccessful starting server", e); - throw new IOException(e.getMessage()); - } + + this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); + } + + @Override + protected void startUp() + throws Exception { + this.server.start(); } @Override @@ -51,9 +51,16 @@ protected void shutDown() this.server.stop(); } - public String url() { return format("http://localhost:%d/%s", this.port, this.path); } + public HealthCheck getHealthCheck() { return this.healthCheck; } public int getPort() { return this.port; } public String getPath() { return this.path; } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("url", format("http://localhost:%d/%s", this.port, this.path)) + .toString(); + } } diff --git a/src/main/java/io/prometheus/common/Utils.java b/src/main/java/io/prometheus/common/Utils.java index e85ed2f0..c2cc240e 100644 --- a/src/main/java/io/prometheus/common/Utils.java +++ b/src/main/java/io/prometheus/common/Utils.java @@ -3,6 +3,7 @@ import com.beust.jcommander.IParameterValidator; import com.beust.jcommander.JCommander; import com.beust.jcommander.internal.Console; +import com.codahale.metrics.health.HealthCheck; import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.base.Splitter; @@ -25,6 +26,8 @@ import java.net.URL; import java.net.UnknownHostException; import java.util.List; +import java.util.Map; +import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -87,7 +90,7 @@ public static Config readConfig(final String cliConfig, if (Strings.isNullOrEmpty(configName)) { if (exitOnMissingConfig) { - logger.error("A configuration file or url must be specified with --getConfig or ${}", envConfig); + logger.error("A configuration file or pingUrl must be specified with --getConfig or ${}", envConfig); System.exit(1); } return fallback; @@ -101,7 +104,7 @@ public static Config readConfig(final String cliConfig, } catch (Exception e) { logger.error(e.getCause() instanceof FileNotFoundException - ? format("Invalid getConfig url: %s", configName) + ? format("Invalid getConfig pingUrl: %s", configName) : format("Exception: %s - %s", e.getClass().getSimpleName(), e.getMessage()), e); } } @@ -151,12 +154,32 @@ public static String getHostName() { } } + public static HealthCheck queueHealthCheck(final Queue queue, final int size) { + return new HealthCheck() { + @Override + protected Result check() + throws Exception { + return queue.size() < size ? Result.healthy() : Result.unhealthy("Large size: %d", queue.size()); + } + }; + } + + public static HealthCheck mapHealthCheck(final Map map, final int size) { + return new HealthCheck() { + @Override + protected Result check() + throws Exception { + return map.size() < size ? Result.healthy() : Result.unhealthy("Large size: %d", map.size()); + } + }; + } + public static void sleepForMillis(final long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { - // Thread.currentThread().interrupt(); + // Ignore } } @@ -165,7 +188,7 @@ public static void sleepForSecs(final long secs) { Thread.sleep(toMillis(secs)); } catch (InterruptedException e) { - // Thread.currentThread().interrupt(); + // Ignore } } @@ -176,9 +199,9 @@ public static String getVersionDesc() { public static Thread shutDownHookAction(final Service service) { return new Thread(() -> { - JCommander.getConsole().println(format("*** Shutting down %s ***", service.getClass().getSimpleName())); + JCommander.getConsole().println(format("*** %s shutting down ***", service.getClass().getSimpleName())); service.stopAsync(); - JCommander.getConsole().println(format("*** %s shut down ***", service.getClass().getSimpleName())); + JCommander.getConsole().println(format("*** %s shut down complete ***", service.getClass().getSimpleName())); }); } diff --git a/src/main/java/io/prometheus/common/ZipkinReporterService.java b/src/main/java/io/prometheus/common/ZipkinReporterService.java index 16743165..5c632228 100644 --- a/src/main/java/io/prometheus/common/ZipkinReporterService.java +++ b/src/main/java/io/prometheus/common/ZipkinReporterService.java @@ -4,7 +4,9 @@ import brave.Tracing; import com.github.kristofa.brave.Brave; import com.github.kristofa.brave.TracerAdapter; +import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.AbstractIdleService; +import com.google.common.util.concurrent.MoreExecutors; import zipkin.Span; import zipkin.reporter.AsyncReporter; import zipkin.reporter.Sender; @@ -15,14 +17,20 @@ public class ZipkinReporterService extends AbstractIdleService { + private final String url; + private final String serviceName; private final Sender sender; private final AsyncReporter reporter; private final Brave brave; public ZipkinReporterService(final String url, final String serviceName) { - this.sender = OkHttpSender.create(url); + this.url = url; + this.serviceName = serviceName; + this.sender = OkHttpSender.create(this.url); this.reporter = AsyncReporter.builder(this.sender).build(); - this.brave = TracerAdapter.newBrave(this.newTracer(serviceName)); + this.brave = TracerAdapter.newBrave(this.newTracer(this.serviceName)); + + this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); } public Tracer newTracer(final String serviceName) { @@ -46,4 +54,12 @@ protected void shutDown() } public Brave getBrave() { return this.brave; } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("serviceName", serviceName) + .add("pingUrl", url) + .toString(); + } } diff --git a/src/main/java/io/prometheus/package-info.java b/src/main/java/io/prometheus/package-info.java index 72483fb4..28524125 100644 --- a/src/main/java/io/prometheus/package-info.java +++ b/src/main/java/io/prometheus/package-info.java @@ -1,4 +1,4 @@ -@VersionAnnotation(version = "1.1.0", date = "05/01/17") +@VersionAnnotation(version = "1.2.0", date = "05/01/17") package io.prometheus; import io.prometheus.common.VersionAnnotation; \ No newline at end of file diff --git a/src/main/java/io/prometheus/proxy/AgentContextCleanupService.java b/src/main/java/io/prometheus/proxy/AgentContextCleanupService.java index 64a99bd7..b47f659e 100644 --- a/src/main/java/io/prometheus/proxy/AgentContextCleanupService.java +++ b/src/main/java/io/prometheus/proxy/AgentContextCleanupService.java @@ -1,7 +1,10 @@ package io.prometheus.proxy; +import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.AbstractExecutionThreadService; +import com.google.common.util.concurrent.MoreExecutors; import io.prometheus.Proxy; +import io.prometheus.common.GenericServiceListener; import io.prometheus.common.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,33 +18,34 @@ public class AgentContextCleanupService public AgentContextCleanupService(final Proxy proxy) { this.proxy = proxy; + this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); } @Override protected void run() throws Exception { - if (this.proxy.getConfigVals().internal.staleAgentCheckEnabled) { - final long maxInactivitySecs = this.proxy.getConfigVals().internal.maxAgentInactivitySecs; - final long threadPauseSecs = this.proxy.getConfigVals().internal.staleAgentCheckPauseSecs; - logger.info("Agent eviction thread started ({} secs max inactivity secs with {} secs pause)", - maxInactivitySecs, threadPauseSecs); - - while (this.isRunning()) { - this.proxy.getAgentContextMap().forEach( - (agentId, agentContext) -> { - final long inactivitySecs = agentContext.inactivitySecs(); - if (inactivitySecs > maxInactivitySecs) { - logger.info("Evicting agent after {} secs of inactivty {}", inactivitySecs, agentContext); - this.proxy.removeAgentContext(agentId); - this.proxy.getMetrics().agentEvictions.inc(); - } - }); - - Utils.sleepForSecs(threadPauseSecs); - } - } - else { - logger.info("Agent eviction thread not started"); + final long maxInactivitySecs = this.proxy.getConfigVals().internal.maxAgentInactivitySecs; + final long threadPauseSecs = this.proxy.getConfigVals().internal.staleAgentCheckPauseSecs; + while (this.isRunning()) { + this.proxy.getAgentContextMap() + .forEach( + (agentId, agentContext) -> { + final long inactivitySecs = agentContext.inactivitySecs(); + if (inactivitySecs > maxInactivitySecs) { + logger.info("Evicting agent after {} secs of inactivty {}", inactivitySecs, agentContext); + this.proxy.removeAgentContext(agentId); + this.proxy.getMetrics().agentEvictions.inc(); + } + }); + Utils.sleepForSecs(threadPauseSecs); } } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("max inactivity secs", this.proxy.getConfigVals().internal.maxAgentInactivitySecs) + .add("pause secs", this.proxy.getConfigVals().internal.staleAgentCheckPauseSecs) + .toString(); + } } diff --git a/src/main/java/io/prometheus/proxy/ProxyGrpcService.java b/src/main/java/io/prometheus/proxy/ProxyGrpcService.java index 4c417fe6..750e9044 100644 --- a/src/main/java/io/prometheus/proxy/ProxyGrpcService.java +++ b/src/main/java/io/prometheus/proxy/ProxyGrpcService.java @@ -1,8 +1,11 @@ package io.prometheus.proxy; +import com.codahale.metrics.health.HealthCheck; +import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.util.concurrent.AbstractIdleService; +import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.ServerInterceptor; @@ -10,6 +13,7 @@ import io.grpc.ServerServiceDefinition; import io.grpc.inprocess.InProcessServerBuilder; import io.prometheus.Proxy; +import io.prometheus.common.GenericServiceListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,10 +28,12 @@ public class ProxyGrpcService private static final Logger logger = LoggerFactory.getLogger(ProxyGrpcService.class); private final String serverName; + private final int port; private final boolean inProcessServer; private final Server grpcServer; - private ProxyGrpcService(final Proxy proxy, final int grpcPort, final String serverName) { + private ProxyGrpcService(final Proxy proxy, final int port, final String serverName) { + this.port = port; this.serverName = serverName; this.inProcessServer = !isNullOrEmpty(serverName); @@ -49,10 +55,11 @@ private ProxyGrpcService(final Proxy proxy, final int grpcPort, final String ser .addService(serviceDef) .addTransportFilter(new ProxyTransportFilter(proxy)) .build() - : ServerBuilder.forPort(grpcPort) + : ServerBuilder.forPort(this.port) .addService(serviceDef) .addTransportFilter(new ProxyTransportFilter(proxy)) .build(); + this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); } public static ProxyGrpcService create(final Proxy proxy, final int grpcPort) { @@ -67,16 +74,35 @@ public static ProxyGrpcService create(final Proxy proxy, final String serverName protected void startUp() throws IOException { this.grpcServer.start(); - if (this.inProcessServer) - logger.info("Started InProcess gRPC server {}", this.serverName); - else - logger.info("Started gRPC server listening on {}", this.grpcServer.getPort()); } @Override - protected void shutDown() { - this.grpcServer.shutdown(); + protected void shutDown() { this.grpcServer.shutdown(); } + + public HealthCheck getHealthCheck() { + return new HealthCheck() { + @Override + protected Result check() + throws Exception { + return grpcServer.isShutdown() || grpcServer.isShutdown() ? Result.unhealthy("gRPC server not runing") + : Result.healthy(); + } + }; } public int getPort() { return this.grpcServer.getPort(); } + + @Override + public String toString() { + final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this); + if (this.inProcessServer) { + helper.add("serverType", "InProcess"); + helper.add("serverName", this.serverName); + } + else { + helper.add("serverType", "Netty"); + helper.add("port", this.port); + } + return helper.toString(); + } } diff --git a/src/main/java/io/prometheus/proxy/ProxyHttpService.java b/src/main/java/io/prometheus/proxy/ProxyHttpService.java index e94379a9..4e13466a 100644 --- a/src/main/java/io/prometheus/proxy/ProxyHttpService.java +++ b/src/main/java/io/prometheus/proxy/ProxyHttpService.java @@ -3,9 +3,12 @@ import brave.Span; import brave.Tracer; import com.github.kristofa.brave.sparkjava.BraveTracing; +import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.AbstractIdleService; +import com.google.common.util.concurrent.MoreExecutors; import io.prometheus.Proxy; import io.prometheus.common.ConfigVals; +import io.prometheus.common.GenericServiceListener; import io.prometheus.grpc.ScrapeResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,11 +44,12 @@ public ProxyHttpService(final Proxy proxy, final int port) { ? this.proxy.getZipkinReporterService().newTracer("proxy-http") : null; this.configVals = this.proxy.getConfigVals(); + + this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); } @Override protected void startUp() { - logger.info("Started proxy listening on {}", this.port); if (this.proxy.isZipkinEnabled()) { final BraveTracing tracing = BraveTracing.create(this.proxy.getBrave()); this.http.before(tracing.before()); @@ -63,11 +67,11 @@ public void handle(Exception e, Request request, Response response) { this.http.get("/*", (req, res) -> { - res.header("cache-control", "no-cache"); + res.header("cache-control", "must-revalidate,no-cache,no-store"); final Span span = this.tracer != null ? this.tracer.newTrace() .name("round-trip") - .tag("version", "1.1.0") + .tag("version", "1.2.0") .start() : null; try { @@ -159,7 +163,6 @@ private String submitScrapeRequest(final Request req, final Response res, final } finally { final ScrapeRequestWrapper prev = this.proxy.removeFromScrapeRequestMap(scrapeRequest.getScrapeId()); - //System.err.println("After remove size = " + this.proxy.getScrapeMapSize()); if (prev == null) logger.error("Scrape request {} missing in map", scrapeRequest.getScrapeId()); } @@ -191,4 +194,11 @@ private void updateScrapeRequests(final String type) { } public int getPort() { return this.port; } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("port", port) + .toString(); + } } diff --git a/src/main/java/io/prometheus/proxy/ProxyOptions.java b/src/main/java/io/prometheus/proxy/ProxyOptions.java index b37a3ded..7297d5ee 100644 --- a/src/main/java/io/prometheus/proxy/ProxyOptions.java +++ b/src/main/java/io/prometheus/proxy/ProxyOptions.java @@ -1,12 +1,12 @@ package io.prometheus.proxy; import com.beust.jcommander.Parameter; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import io.prometheus.Proxy; import io.prometheus.common.BaseOptions; import io.prometheus.common.ConfigVals; +import java.util.Collections; import java.util.List; import static io.prometheus.common.EnvVars.AGENT_PORT; @@ -22,7 +22,7 @@ public class ProxyOptions private Integer agentPort = null; public ProxyOptions(final List args) { - this(Iterables.toArray(args != null ? args : ImmutableList.of(), String.class)); + this(Iterables.toArray(args != null ? args : Collections.emptyList(), String.class)); } public ProxyOptions(final String[] argv) { @@ -38,8 +38,10 @@ protected void assignConfigVals(final ConfigVals configVals) { if (this.agentPort == null) this.agentPort = AGENT_PORT.getEnv(configVals.proxy.agent.port); + this.assignAdminEnabled(configVals.proxy.admin.enabled); + this.assignAdminPort(configVals.proxy.admin.port); + this.assignMetricsEnabled(configVals.proxy.metrics.enabled); this.assignMetricsPort(configVals.proxy.metrics.port); - this.assignEnableMetrics(configVals.proxy.metrics.enabled); } public int getProxyPort() { return this.proxyPort; } diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 4ec7f5b2..a5c09db3 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -3,6 +3,8 @@ proxy { agent {} + admin {} + metrics { grpc {} } @@ -16,6 +18,8 @@ proxy { agent { proxy {} + admin {} + metrics { grpc {} } diff --git a/src/test/java/io/prometheus/AdminTest.java b/src/test/java/io/prometheus/AdminTest.java new file mode 100644 index 00000000..83d853b8 --- /dev/null +++ b/src/test/java/io/prometheus/AdminTest.java @@ -0,0 +1,108 @@ +package io.prometheus; + +import io.prometheus.client.CollectorRegistry; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import static java.lang.String.format; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; + +public class AdminTest { + + private static final Logger logger = LoggerFactory.getLogger(AdminTest.class); + + private static Proxy PROXY = null; + private static Agent AGENT = null; + + @BeforeClass + public static void setUp() + throws IOException, InterruptedException, TimeoutException { + CollectorRegistry.defaultRegistry.clear(); + PROXY = TestUtils.startProxy(null, true, false); + AGENT = TestUtils.startAgent(null, true, false); + + AGENT.awaitInitialConnection(5, SECONDS); + } + + @AfterClass + public static void takeDown() + throws InterruptedException, TimeoutException { + PROXY.stopAsync(); + PROXY.awaitTerminated(5, SECONDS); + AGENT.stopAsync(); + AGENT.awaitTerminated(5, SECONDS); + } + + @Test + public void proxyPingPathTest() + throws Exception { + String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.pingPath); + Request.Builder request = new Request.Builder().url(url); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(200); + assertThat(response.body().string()).startsWith("pong"); + } + } + + @Test + public void agentPingPathTest() + throws Exception { + String url = format("http://localhost:%d/%s", AGENT.getConfigVals().admin.port, AGENT.getConfigVals().admin.pingPath); + Request.Builder request = new Request.Builder().url(url); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(200); + assertThat(response.body().string()).startsWith("pong"); + } + } + + @Test + public void proxyHealthCheckPathTest() + throws Exception { + String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.healthCheckPath); + Request.Builder request = new Request.Builder().url(url); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(200); + assertThat(response.body().string().length()).isGreaterThan(10); + } + } + + @Test + public void agentHealthCheckPathTest() + throws Exception { + String url = format("http://localhost:%d/%s", AGENT.getConfigVals().admin.port, AGENT.getConfigVals().admin.healthCheckPath); + Request.Builder request = new Request.Builder().url(url); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.body().string().length()).isGreaterThan(10); + } + } + + @Test + public void proxyThreadDumpPathTest() + throws Exception { + String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.theadtDumpPath); + Request.Builder request = new Request.Builder().url(url); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.body().string().length()).isGreaterThan(10); + } + } + + @Test + public void agentThreadDumpPathTest() + throws Exception { + String url = format("http://localhost:%d/%s", AGENT.getConfigVals().admin.port, AGENT.getConfigVals().admin.theadtDumpPath); + Request.Builder request = new Request.Builder().url(url); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.body().string().length()).isGreaterThan(10); + } + } + +} diff --git a/src/test/java/io/prometheus/AutoValueTest.java b/src/test/java/io/prometheus/AutoValueTest.java index 12cfb0cd..1b992a31 100644 --- a/src/test/java/io/prometheus/AutoValueTest.java +++ b/src/test/java/io/prometheus/AutoValueTest.java @@ -4,6 +4,7 @@ import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigParseOptions; import com.typesafe.config.ConfigSyntax; +import io.prometheus.common.AdminConfig; import io.prometheus.common.ConfigVals; import io.prometheus.common.MetricsConfig; import io.prometheus.common.ZipkinConfig; @@ -19,52 +20,71 @@ private ConfigVals configVals(final String str) { } @Test - public void zipkinConfigTest() { - ZipkinConfig z = ZipkinConfig.create(configVals("agent.internal.zipkin.enabled=true").agent.internal.zipkin); - assertThat(z.enabled()).isTrue(); + public void adminConfigTest() { + ConfigVals vals = configVals("agent.admin.enabled=true"); + AdminConfig c = AdminConfig.create(vals.agent.admin.enabled, -1, vals.agent.admin); + assertThat(c.enabled()).isTrue(); - z = ZipkinConfig.create(configVals("agent.internal.zipkin.hostname=testval").agent.internal.zipkin); - assertThat(z.hostname()).isEqualTo("testval"); + vals = configVals("agent.admin.port=888"); + c = AdminConfig.create(vals.agent.admin.enabled, vals.agent.admin.port, vals.agent.admin); + assertThat(c.enabled()).isFalse(); + assertThat(c.port()).isEqualTo(888); - z = ZipkinConfig.create(configVals("agent.internal.zipkin.port=999").agent.internal.zipkin); - assertThat(z.port()).isEqualTo(999); + c = AdminConfig.create(true, 444, configVals("agent.admin.pingPath=a pingpath val").agent.admin); + assertThat(c.pingPath()).isEqualTo("a pingpath val"); - z = ZipkinConfig.create(configVals("agent.internal.zipkin.path=a path val").agent.internal.zipkin); - assertThat(z.path()).isEqualTo("a path val"); + c = AdminConfig.create(true, 444, configVals("agent.admin.healthCheckPath=a healthCheckPath val").agent.admin); + assertThat(c.healthCheckPath()).isEqualTo("a healthCheckPath val"); - z = ZipkinConfig.create(configVals("agent.internal.zipkin.serviceName=a service name").agent.internal.zipkin); - assertThat(z.serviceName()).isEqualTo("a service name"); + c = AdminConfig.create(true, 444, configVals("agent.admin.theadtDumpPath=a theadtDumpPath val").agent.admin); + assertThat(c.theadtDumpPath()).isEqualTo("a theadtDumpPath val"); } @Test public void metricsConfigTest() { - MetricsConfig m = MetricsConfig.create(true, 555, configVals("agent.metrics.enabled=true").agent.metrics); - assertThat(m.enabled()).isTrue(); - - m = MetricsConfig.create(true, 555, configVals("agent.metrics.hostname=testval").agent.metrics); - assertThat(m.port()).isEqualTo(555); + MetricsConfig c = MetricsConfig.create(true, 555, configVals("agent.metrics.enabled=true").agent.metrics); + assertThat(c.enabled()).isTrue(); - m = MetricsConfig.create(true, 555, configVals("agent.metrics.path=a path val").agent.metrics); - assertThat(m.path()).isEqualTo("a path val"); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.hostname=testval").agent.metrics); + assertThat(c.port()).isEqualTo(555); - m = MetricsConfig.create(true, 555, configVals("agent.metrics.standardExportsEnabled=true").agent.metrics); - assertThat(m.standardExportsEnabled()).isTrue(); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.path=a path val").agent.metrics); + assertThat(c.path()).isEqualTo("a path val"); - m = MetricsConfig.create(true, 555, configVals("agent.metrics.memoryPoolsExportsEnabled=true").agent.metrics); - assertThat(m.memoryPoolsExportsEnabled()).isTrue(); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.standardExportsEnabled=true").agent.metrics); + assertThat(c.standardExportsEnabled()).isTrue(); - m = MetricsConfig.create(true, 555, configVals("agent.metrics.garbageCollectorExportsEnabled=true").agent.metrics); - assertThat(m.garbageCollectorExportsEnabled()).isTrue(); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.memoryPoolsExportsEnabled=true").agent.metrics); + assertThat(c.memoryPoolsExportsEnabled()).isTrue(); - m = MetricsConfig.create(true, 555, configVals("agent.metrics.threadExportsEnabled=true").agent.metrics); - assertThat(m.threadExportsEnabled()).isTrue(); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.garbageCollectorExportsEnabled=true").agent.metrics); + assertThat(c.garbageCollectorExportsEnabled()).isTrue(); - m = MetricsConfig.create(true, 555, configVals("agent.metrics.classLoadingExportsEnabled=true").agent.metrics); - assertThat(m.classLoadingExportsEnabled()).isTrue(); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.threadExportsEnabled=true").agent.metrics); + assertThat(c.threadExportsEnabled()).isTrue(); - m = MetricsConfig.create(true, 555, configVals("agent.metrics.versionInfoExportsEnabled=true").agent.metrics); - assertThat(m.versionInfoExportsEnabled()).isTrue(); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.classLoadingExportsEnabled=true").agent.metrics); + assertThat(c.classLoadingExportsEnabled()).isTrue(); + c = MetricsConfig.create(true, 555, configVals("agent.metrics.versionInfoExportsEnabled=true").agent.metrics); + assertThat(c.versionInfoExportsEnabled()).isTrue(); } + @Test + public void zipkinConfigTest() { + ZipkinConfig c = ZipkinConfig.create(configVals("agent.internal.zipkin.enabled=true").agent.internal.zipkin); + assertThat(c.enabled()).isTrue(); + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.hostname=testval").agent.internal.zipkin); + assertThat(c.hostname()).isEqualTo("testval"); + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.port=999").agent.internal.zipkin); + assertThat(c.port()).isEqualTo(999); + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.path=a path val").agent.internal.zipkin); + assertThat(c.path()).isEqualTo("a path val"); + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.serviceName=a service name").agent.internal.zipkin); + assertThat(c.serviceName()).isEqualTo("a service name"); + } } diff --git a/src/test/java/io/prometheus/InProcessTestWithMetricsTest.java b/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.java similarity index 86% rename from src/test/java/io/prometheus/InProcessTestWithMetricsTest.java rename to src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.java index 8369a1aa..f2ea6632 100644 --- a/src/test/java/io/prometheus/InProcessTestWithMetricsTest.java +++ b/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.java @@ -12,9 +12,9 @@ import static java.util.concurrent.TimeUnit.SECONDS; -public class InProcessTestWithMetricsTest { +public class InProcessTestNoAdminMetricsTest { - private static final Logger logger = LoggerFactory.getLogger(InProcessTestWithMetricsTest.class); + private static final Logger logger = LoggerFactory.getLogger(InProcessTestNoAdminMetricsTest.class); private static Proxy PROXY = null; private static Agent AGENT = null; @@ -23,8 +23,8 @@ public class InProcessTestWithMetricsTest { public static void setUp() throws IOException, InterruptedException, TimeoutException { CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy("withmetrics", true); - AGENT = TestUtils.startAgent("withmetrics", true); + PROXY = TestUtils.startProxy("nometrics", false, false); + AGENT = TestUtils.startAgent("nometrics", false, false); AGENT.awaitInitialConnection(10, SECONDS); } @@ -38,6 +38,7 @@ public static void takeDown() AGENT.awaitTerminated(5, SECONDS); } + @Test public void missingPathTest() throws Exception { @@ -74,11 +75,10 @@ public void timeoutTest() Tests.timeoutTest(AGENT); } - @Test public void proxyCallTest() throws Exception { - Tests.proxyCallTest(AGENT, 25, 50, 500); + Tests.proxyCallTest(AGENT, 25, 50, 1000, 25); } } diff --git a/src/test/java/io/prometheus/InProcessTestNoMetricsTest.java b/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.java similarity index 88% rename from src/test/java/io/prometheus/InProcessTestNoMetricsTest.java rename to src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.java index 46d66c36..501b6731 100644 --- a/src/test/java/io/prometheus/InProcessTestNoMetricsTest.java +++ b/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.java @@ -12,9 +12,9 @@ import static java.util.concurrent.TimeUnit.SECONDS; -public class InProcessTestNoMetricsTest { +public class InProcessTestWithAdminMetricsTest { - private static final Logger logger = LoggerFactory.getLogger(InProcessTestNoMetricsTest.class); + private static final Logger logger = LoggerFactory.getLogger(InProcessTestWithAdminMetricsTest.class); private static Proxy PROXY = null; private static Agent AGENT = null; @@ -23,8 +23,8 @@ public class InProcessTestNoMetricsTest { public static void setUp() throws IOException, InterruptedException, TimeoutException { CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy("nometrics", false); - AGENT = TestUtils.startAgent("nometrics", false); + PROXY = TestUtils.startProxy("withmetrics", true, true); + AGENT = TestUtils.startAgent("withmetrics", true, true); AGENT.awaitInitialConnection(10, SECONDS); } @@ -38,7 +38,6 @@ public static void takeDown() AGENT.awaitTerminated(5, SECONDS); } - @Test public void missingPathTest() throws Exception { diff --git a/src/test/java/io/prometheus/NettyTestNoMetricsTest.java b/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.java similarity index 89% rename from src/test/java/io/prometheus/NettyTestNoMetricsTest.java rename to src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.java index fb213e6b..ecbb127e 100644 --- a/src/test/java/io/prometheus/NettyTestNoMetricsTest.java +++ b/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.java @@ -12,9 +12,9 @@ import static java.util.concurrent.TimeUnit.SECONDS; -public class NettyTestNoMetricsTest { +public class NettyTestNoAdminMetricsTest { - private static final Logger logger = LoggerFactory.getLogger(NettyTestNoMetricsTest.class); + private static final Logger logger = LoggerFactory.getLogger(NettyTestNoAdminMetricsTest.class); private static Proxy PROXY = null; private static Agent AGENT = null; @@ -23,8 +23,8 @@ public class NettyTestNoMetricsTest { public static void setUp() throws IOException, InterruptedException, TimeoutException { CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy(null, false); - AGENT = TestUtils.startAgent(null, false); + PROXY = TestUtils.startProxy(null, false, false); + AGENT = TestUtils.startAgent(null, false, false); AGENT.awaitInitialConnection(10, SECONDS); } diff --git a/src/test/java/io/prometheus/NettyTestWithMetricsTest.java b/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.java similarity index 90% rename from src/test/java/io/prometheus/NettyTestWithMetricsTest.java rename to src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.java index 50f309bd..c94ff095 100644 --- a/src/test/java/io/prometheus/NettyTestWithMetricsTest.java +++ b/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.java @@ -13,9 +13,9 @@ import static io.prometheus.common.Utils.sleepForSecs; import static java.util.concurrent.TimeUnit.SECONDS; -public class NettyTestWithMetricsTest { +public class NettyTestWithAdminMetricsTest { - private static final Logger logger = LoggerFactory.getLogger(NettyTestWithMetricsTest.class); + private static final Logger logger = LoggerFactory.getLogger(NettyTestWithAdminMetricsTest.class); private static Proxy PROXY = null; private static Agent AGENT = null; @@ -24,8 +24,8 @@ public class NettyTestWithMetricsTest { public static void setUp() throws IOException, InterruptedException, TimeoutException { CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy(null, true); - AGENT = TestUtils.startAgent(null, true); + PROXY = TestUtils.startProxy(null, true, true); + AGENT = TestUtils.startAgent(null, true, true); AGENT.awaitInitialConnection(10, SECONDS); diff --git a/src/test/java/io/prometheus/OptionsTest.java b/src/test/java/io/prometheus/OptionsTest.java index c7713c72..40c9d621 100644 --- a/src/test/java/io/prometheus/OptionsTest.java +++ b/src/test/java/io/prometheus/OptionsTest.java @@ -59,7 +59,7 @@ public void verifyAgentDefaults() { AgentOptions options = new AgentOptions(newArrayList("--name", "test-name", "--proxy", "host5"), false); - assertThat(options.getMetricsEnabled()).isEqualTo(false); + assertThat(options.isMetricsEnabled()).isEqualTo(false); assertThat(options.getDynamicParams().size()).isEqualTo(0); assertThat(options.getAgentName()).isEqualTo("test-name"); assertThat(options.getProxyHostname()).isEqualTo("host5"); diff --git a/src/test/java/io/prometheus/TestConstants.java b/src/test/java/io/prometheus/TestConstants.java index 87f9874d..b0d69a1b 100644 --- a/src/test/java/io/prometheus/TestConstants.java +++ b/src/test/java/io/prometheus/TestConstants.java @@ -1,7 +1,9 @@ package io.prometheus; +import com.google.common.collect.Lists; import okhttp3.OkHttpClient; +import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -12,7 +14,7 @@ public class TestConstants { static final Random RANDOM = new Random(); static final int REPS = 1000; static final int PROXY_PORT = 9500; - static final String[] argv = {"--config", "https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/etc/test-configs/travis.conf"}; + static final List args = Lists.newArrayList("--config", "https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/etc/test-configs/travis.conf"); private TestConstants() {} } \ No newline at end of file diff --git a/src/test/java/io/prometheus/TestUtils.java b/src/test/java/io/prometheus/TestUtils.java index cc4e1871..df9c9e18 100644 --- a/src/test/java/io/prometheus/TestUtils.java +++ b/src/test/java/io/prometheus/TestUtils.java @@ -1,57 +1,52 @@ package io.prometheus; -import com.google.common.util.concurrent.MoreExecutors; import io.prometheus.agent.AgentOptions; -import io.prometheus.common.GenericServiceListener; -import io.prometheus.common.MetricsConfig; import io.prometheus.common.Utils; -import io.prometheus.common.ZipkinConfig; import io.prometheus.proxy.ProxyOptions; +import org.assertj.core.util.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static java.lang.String.format; + public class TestUtils { private static final Logger logger = LoggerFactory.getLogger(TestUtils.class); - public static Proxy startProxy(String serverName, boolean metrics_enabled) + public static Proxy startProxy(String serverName, boolean adminEnabled, boolean metricsEnabled) throws IOException, TimeoutException { - ProxyOptions options = new ProxyOptions(TestConstants.argv); - final MetricsConfig metricsConfig = MetricsConfig.create(metrics_enabled, - options.getMetricsPort(), - options.getConfigVals().proxy.metrics); - - final ZipkinConfig zipkinConfig = ZipkinConfig.create(options.getConfigVals().proxy.internal.zipkin); + final List args = Lists.newArrayList(TestConstants.args); + args.add(format("-Dproxy.admin.enabled=%s", adminEnabled)); + args.add(format("-Dproxy.metrics.enabled=%s", metricsEnabled)); + ProxyOptions options = new ProxyOptions(args); logger.info(Utils.getBanner("banners/proxy.txt")); logger.info(Utils.getVersionDesc()); - Proxy proxy = new Proxy(options, metricsConfig, zipkinConfig, TestConstants.PROXY_PORT, serverName, true); - proxy.addListener(new GenericServiceListener(proxy), MoreExecutors.directExecutor()); + Proxy proxy = new Proxy(options, TestConstants.PROXY_PORT, serverName, true); proxy.startAsync(); proxy.awaitRunning(5, TimeUnit.SECONDS); return proxy; } - public static Agent startAgent(String serverName, boolean metrics_enabled) + public static Agent startAgent(String serverName, boolean adminEnabled, boolean metricsEnabled) throws IOException, TimeoutException { - AgentOptions options = new AgentOptions(TestConstants.argv, false); - final MetricsConfig metricsConfig = MetricsConfig.create(options.getMetricsEnabled(), - options.getMetricsPort(), - options.getConfigVals().agent.metrics); - final ZipkinConfig zipkinConfig = ZipkinConfig.create(options.getConfigVals().agent.internal.zipkin); + final List args = Lists.newArrayList(TestConstants.args); + args.add(format("-Dagent.admin.enabled=%s", adminEnabled)); + args.add(format("-Dagent.metrics.enabled=%s", metricsEnabled)); + AgentOptions options = new AgentOptions(args, false); logger.info(Utils.getBanner("banners/agent.txt")); logger.info(Utils.getVersionDesc()); - Agent agent = new Agent(options, metricsConfig, zipkinConfig, serverName, true); - agent.addListener(new GenericServiceListener(agent), MoreExecutors.directExecutor()); + Agent agent = new Agent(options, serverName, true); agent.startAsync(); agent.awaitRunning(5, TimeUnit.SECONDS); return agent; diff --git a/src/test/java/io/prometheus/Tests.java b/src/test/java/io/prometheus/Tests.java index ceaa0fd2..81f2ef58 100644 --- a/src/test/java/io/prometheus/Tests.java +++ b/src/test/java/io/prometheus/Tests.java @@ -26,8 +26,8 @@ public static void missingPathTest() throws Exception { String url = format("http://localhost:%d/", TestConstants.PROXY_PORT); Request.Builder request = new Request.Builder().url(url); - try (Response respone = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(respone.code()).isEqualTo(404); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(404); } } @@ -35,8 +35,8 @@ public static void invalidPathTest() throws Exception { String url = format("http://localhost:%d/invalid_path", TestConstants.PROXY_PORT); Request.Builder request = new Request.Builder().url(url); - try (Response respone = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(respone.code()).isEqualTo(404); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(404); } } @@ -118,8 +118,8 @@ public static void invalidAgentUrlTest(final Agent agent) String url = format("http://localhost:%d/%s", TestConstants.PROXY_PORT, badPath); Request.Builder request = new Request.Builder().url(url); - try (Response respone = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(respone.code()).isEqualTo(404); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(404); } agent.unregisterPath(badPath); @@ -144,8 +144,8 @@ public static void timeoutTest(final Agent agent) String proxyUrl = format("http://localhost:%d/%s", TestConstants.PROXY_PORT, proxyPath); Request.Builder request = new Request.Builder().url(proxyUrl); - try (Response respone = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(respone.code()).isEqualTo(404); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(404); } agent.unregisterPath("/" + proxyPath); @@ -155,7 +155,8 @@ public static void timeoutTest(final Agent agent) public static void proxyCallTest(final Agent agent, final int httpServerCount, final int pathCount, - final int queryCount) + final int queryCount, + final long pauseMillis) throws Exception { final int startingPort = 9600; @@ -170,6 +171,7 @@ public static void proxyCallTest(final Agent agent, .forEach(i -> { Service http = Service.ignite(); http.port(startingPort + i) + .threadPool(30, 10, 1000) .get(format("/agent-%d", i), (req, res) -> { res.type("text/plain"); @@ -189,8 +191,10 @@ public static void proxyCallTest(final Agent agent, assertThat(agent.pathMapSize()).isEqualTo(originalSize + pathCount); // Call the proxy sequentially - for (int i = 0; i < queryCount; i++) + for (int i = 0; i < queryCount; i++) { callProxy(pathMap); + Utils.sleepForMillis(pauseMillis); + } // Call the proxy in parallel int threadedQueryCount = 100; @@ -236,8 +240,9 @@ private static void callProxy(final Map pathMap) int httpVal = pathMap.get(index); String url = format("http://localhost:%d/proxy-%d", TestConstants.PROXY_PORT, index); Request.Builder request = new Request.Builder().url(url); - try (Response respone = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - String body = respone.body().string(); + try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { + assertThat(response.code()).isEqualTo(200); + String body = response.body().string(); assertThat(body).isEqualTo(format("value: %d", httpVal)); } }