diff --git a/Makefile b/Makefile index 15a5fa2b..e468194b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=1.3.1 +VERSION=1.3.2 default: build @@ -33,8 +33,8 @@ sonar: distro: build mkdir target/distro - mv target/proxy-jar-with-dependencies.jar target/distro/prometheus-proxy.jar - mv target/agent-jar-with-dependencies.jar target/distro/prometheus-agent.jar + mv target/prometheus-proxy-jar-with-dependencies.jar target/distro/prometheus-proxy.jar + mv target/prometheus-agent-jar-with-dependencies.jar target/distro/prometheus-agent.jar site: ./mvnw site diff --git a/README.md b/README.md index cc29882b..e77c35c7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://travis-ci.org/pambrose/prometheus-proxy.svg?branch=master)](https://travis-ci.org/pambrose/prometheus-proxy) [![Coverage Status](https://coveralls.io/repos/github/pambrose/prometheus-proxy/badge.svg?branch=master)](https://coveralls.io/github/pambrose/prometheus-proxy?branch=master) [![Code Climate](https://codeclimate.com/github/pambrose/prometheus-proxy/badges/gpa.svg)](https://codeclimate.com/github/pambrose/prometheus-proxy) - +[![Dependency Status](https://www.versioneye.com/user/projects/5a4c7a110fb24f0536e5b92f/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/5a4c7a110fb24f0536e5b92f) [Prometheus](https://prometheus.io) is an excellent systems monitoring and alerting toolkit, which uses a pull model for collecting metrics. The pull model is problematic when a Prometheus server and its metrics endpoints are separated @@ -84,8 +84,8 @@ scrape_configs: The docker images are available via: ```bash -$ docker pull pambrose/prometheus-proxy:1.3.1 -$ docker pull pambrose/prometheus-agent:1.3.1 +$ docker pull pambrose/prometheus-proxy:1.3.2 +$ docker pull pambrose/prometheus-agent:1.3.2 ``` Start the proxy and an agent in separate shells on your local machine: @@ -94,14 +94,14 @@ Start the proxy and an agent in separate shells on your local machine: $ 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.3.1 + pambrose/prometheus-proxy:1.3.2 ``` ```bash $ 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.3.1 + pambrose/prometheus-agent:1.3.2 ``` Using the config file [simple.conf](https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf), diff --git a/bin/docker-agent.sh b/bin/docker-agent.sh index aab5d37a..66581c20 100755 --- a/bin/docker-agent.sh +++ b/bin/docker-agent.sh @@ -3,4 +3,4 @@ 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.3.1 \ No newline at end of file + pambrose/prometheus-agent:1.3.2 \ No newline at end of file diff --git a/bin/docker-proxy.sh b/bin/docker-proxy.sh index 077d8920..47f7295a 100755 --- a/bin/docker-proxy.sh +++ b/bin/docker-proxy.sh @@ -3,4 +3,4 @@ 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.3.1 \ No newline at end of file + pambrose/prometheus-proxy:1.3.2 \ No newline at end of file diff --git a/docs/release.md b/docs/release.md index a267c063..732b38b3 100644 --- a/docs/release.md +++ b/docs/release.md @@ -12,9 +12,10 @@ 6) Check in branch and merge -7) Create release on github and upload target/distro/prometheus-proxy.jar and target/distro/prometheus-agent.jar +7) Create release on github (https://github.com/pambrose/prometheus-proxy/releases) and +upload the *target/distro/prometheus-proxy.jar* and *target/distro/prometheus-agent.jar* files. 8) Build and push docker images: `make docker-build docker-push` -9) Update the *prometheus-proxy* and *prometheus-agent* repository descriptions on -the Docker hub with the latest version of *README.md*. \ No newline at end of file +9) Update the *prometheus-proxy* and *prometheus-agent* repository descriptions +on Docker hub (https://hub.docker.com) with the latest version of *README.md*. \ No newline at end of file diff --git a/etc/compose/proxy.yml b/etc/compose/proxy.yml index e0ca2ed0..49d2a749 100644 --- a/etc/compose/proxy.yml +++ b/etc/compose/proxy.yml @@ -1,6 +1,6 @@ prometheus-proxy: autoredeploy: true - image: 'pambrose/prometheus-proxy:1.3.1' + image: 'pambrose/prometheus-proxy:1.3.2' ports: - '8080:8080' - '8082:8082' diff --git a/pom.xml b/pom.xml index b96b63aa..32b72344 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ io.prometheus prometheus-proxy - 1.3.1-SNAPSHOT + 1.3.2-SNAPSHOT 1.2.10 @@ -31,8 +31,9 @@ 1.3.2 1.8.0 1.8.0 + 0.7.0 3.4.0 - 0.5.0 + 0.5.1 19.0 2.7.1 9.4.6.v20170531 @@ -47,12 +48,12 @@ 1.7.25 4.12 - 3.8.0 + 3.9.0 2.0.0 - 2.6 - 3.6.1 - 3.0.2 + 2.6 + 3.7.0 + 3.0.2 1.10 3.5.0 @@ -185,6 +186,12 @@ ${grpc.version} + + com.salesforce.servicelibs + grpc-contrib + ${grpc.contrib.version} + + com.squareup.okhttp3 okhttp @@ -387,7 +394,7 @@ maven-jar-plugin - ${maven-jar-plugin.version} + ${maven.jar.plugin.version} @@ -439,7 +446,7 @@ maven-assembly-plugin - ${assembly.version} + ${maven.assembly.version} proxy @@ -449,7 +456,7 @@ package - proxy + prometheus-proxy jar-with-dependencies @@ -470,7 +477,7 @@ package - agent + prometheus-agent jar-with-dependencies diff --git a/src/main/java/io/prometheus/Agent.kt b/src/main/java/io/prometheus/Agent.kt index 7f2cf765..e7fc0b5a 100644 --- a/src/main/java/io/prometheus/Agent.kt +++ b/src/main/java/io/prometheus/Agent.kt @@ -18,79 +18,98 @@ package io.prometheus import brave.Tracing import brave.grpc.GrpcTracing -import com.google.common.base.MoreObjects import com.google.common.base.Preconditions.checkNotNull -import com.google.common.collect.Maps +import com.google.common.collect.Maps.newConcurrentMap import com.google.common.net.HttpHeaders.CONTENT_TYPE import com.google.common.util.concurrent.RateLimiter -import com.google.common.util.concurrent.ThreadFactoryBuilder import com.google.protobuf.Empty import io.grpc.ClientInterceptor import io.grpc.ClientInterceptors.intercept import io.grpc.ManagedChannel import io.grpc.Status import io.grpc.StatusRuntimeException -import io.grpc.inprocess.InProcessChannelBuilder -import io.grpc.netty.NettyChannelBuilder -import io.grpc.stub.StreamObserver import io.prometheus.agent.* import io.prometheus.common.* +import io.prometheus.common.AdminConfig.Companion.newAdminConfig +import io.prometheus.common.MetricsConfig.Companion.newMetricsConfig +import io.prometheus.common.ZipkinConfig.Companion.newZipkinConfig +import io.prometheus.delegate.AtomicDelegates +import io.prometheus.delegate.AtomicDelegates.notNullReference +import io.prometheus.dsl.GrpcDsl.channel +import io.prometheus.dsl.GrpcDsl.streamObserver +import io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.dsl.ThreadDsl.threadFactory import io.prometheus.grpc.* import io.prometheus.grpc.ProxyServiceGrpc.* import okhttp3.OkHttpClient import org.slf4j.LoggerFactory import java.io.IOException -import java.util.concurrent.* +import java.util.concurrent.ArrayBlockingQueue +import java.util.concurrent.CountDownLatch +import java.util.concurrent.ExecutorService import java.util.concurrent.Executors.newCachedThreadPool +import java.util.concurrent.Executors.newFixedThreadPool +import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean +import kotlin.properties.Delegates.notNull class Agent(options: AgentOptions, private val inProcessServerName: String = "", - testMode: Boolean = false) : GenericService(options.configVals, - AdminConfig.create(options.adminEnabled, - options.adminPort!!, - options.configVals.agent.admin), - MetricsConfig.create(options.metricsEnabled, - options.metricsPort!!, - options.configVals.agent.metrics), - ZipkinConfig.create(options.configVals.agent.internal.zipkin), - testMode) { - - private val pathContextMap = Maps.newConcurrentMap() // Map path to PathContext - private val heartbeatService = Executors.newFixedThreadPool(1) + testMode: Boolean = false, + initBlock: (Agent.() -> Unit)? = null) : GenericService(options.configVals, + newAdminConfig(options.adminEnabled, + options.adminPort, + options.configVals.agent.admin), + newMetricsConfig(options.metricsEnabled, + options.metricsPort, + options.configVals.agent.metrics), + newZipkinConfig(options.configVals.agent.internal.zipkin), + testMode) { + private val pathContextMap = newConcurrentMap() // Map path to PathContext + private val heartbeatService = newFixedThreadPool(1) private val initialConnectionLatch = CountDownLatch(1) private val okHttpClient = OkHttpClient() private val scrapeResponseQueue = ArrayBlockingQueue(configVals.internal.scrapeResponseQueueSize) - private val agentName: String = if (options.agentName.isNullOrBlank()) "Unnamed-${io.prometheus.common.hostName}" else options.agentName!! - private val metrics: AgentMetrics? = if (metricsEnabled) AgentMetrics(this) else null - private var blockingStub: ProxyServiceBlockingStub by AtomicDelegates.notNullReference() - private var asyncStub: ProxyServiceStub by AtomicDelegates.notNullReference() + private val agentName: String = if (options.agentName.isBlank()) "Unnamed-$localHostName" else options.agentName + private var metrics: AgentMetrics by notNull() + private var blockingStub: ProxyServiceBlockingStub by notNullReference() + private var asyncStub: ProxyServiceStub by notNullReference() private val readRequestsExecutorService: ExecutorService = - newCachedThreadPool(if (metricsEnabled) - InstrumentedThreadFactory.newInstrumentedThreadFactory("agent_fetch", - "Agent fetch", - true) + newCachedThreadPool(if (isMetricsEnabled) + InstrumentedThreadFactory( + threadFactory { + setNameFormat("agent_fetch" + "-%d") + setDaemon(true) + }, "agent_fetch", "Agent fetch") else - ThreadFactoryBuilder() - .setNameFormat("agent_fetch-%d") - .setDaemon(true) - .build()) + threadFactory { + setNameFormat("agent_fetch-%d") + setDaemon(true) + }) + + private var tracing: Tracing by notNull() + private var grpcTracing: GrpcTracing by notNull() - private val tracing: Tracing? - private val grpcTracing: GrpcTracing? private val hostName: String private val port: Int - private val reconnectLimiter: RateLimiter - private val pathConfigs: List> + private val reconnectLimiter = + RateLimiter.create(1.0 / configVals.internal.reconectPauseSecs).apply { acquire() } // Prime the limiter + + private val pathConfigs = + configVals.pathConfigs + .map { mapOf("name" to it.name, "path" to it.path, "url" to it.url) } + .onEach { logger.info("Proxy path /{} will be assigned to {}", it["path"], it["url"]) } + .toList() private var lastMsgSent: Long by AtomicDelegates.long() + private var grpcStarted: Boolean by AtomicDelegates.boolean(false) + var channel: ManagedChannel by notNullReference() + var agentId: String by notNullReference() + private val proxyHost: String get() = "$hostName:$port" - var channel: ManagedChannel? by AtomicDelegates.nullableReference() - var agentId: String by AtomicDelegates.notNullReference() - val scrapeResponseQueueSize: Int get() = scrapeResponseQueue.size @@ -101,41 +120,35 @@ class Agent(options: AgentOptions, logger.info("Assigning proxy reconnect pause time to ${configVals.internal.reconectPauseSecs} secs") agentId = "" - reconnectLimiter = RateLimiter.create(1.0 / configVals.internal.reconectPauseSecs) - reconnectLimiter.acquire() // Prime the limiter - - pathConfigs = - configVals.pathConfigs - .map { mapOf("name" to it.name, "path" to it.path, "url" to it.url) } - .onEach { logger.info("Proxy path /{} will be assigned to {}", it["path"], it["url"]) } - .toList() - if (options.proxyHostname!!.contains(":")) { - val vals = options.proxyHostname!!.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + if (options.proxyHostname.contains(":")) { + val vals = options.proxyHostname.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() hostName = vals[0] port = Integer.valueOf(vals[1]) } else { - hostName = options.proxyHostname!! + hostName = options.proxyHostname port = 50051 } - if (zipkinEnabled) { - tracing = zipkinReporterService!!.newTracing("grpc_client") + if (isMetricsEnabled) + metrics = AgentMetrics(this) + + if (isZipkinEnabled) { + tracing = zipkinReporterService.newTracing("grpc_client") grpcTracing = GrpcTracing.create(tracing) } - else { - tracing = null - grpcTracing = null - } resetGrpcStubs() initService() + initBlock?.invoke(this) } override fun shutDown() { - tracing?.close() - channel?.shutdownNow() + if (isZipkinEnabled) + tracing.close() + if (grpcStarted) + channel.shutdownNow() heartbeatService.shutdownNow() super.shutDown() } @@ -161,7 +174,7 @@ class Agent(options: AgentOptions, super.registerHealthChecks() healthCheckRegistry .register("scrape_response_queue_check", - queueHealthCheck(scrapeResponseQueue, configVals.internal.scrapeResponseQueueUnhealthySize)) + newQueueHealthCheck(scrapeResponseQueue, configVals.internal.scrapeResponseQueueUnhealthySize)) } override fun serviceName() = "${javaClass.simpleName} $agentName" @@ -170,7 +183,7 @@ class Agent(options: AgentOptions, private fun connectToProxy() { val disconnected = AtomicBoolean(false) - // Reset gRPC stubs if previous iteration had a successful connection, i.e., the agentId != null + // Reset gRPC stubs if previous iteration had a successful connection, i.e., the agentId != "" if (agentId.isNotEmpty()) { resetGrpcStubs() agentId = "" @@ -213,65 +226,74 @@ class Agent(options: AgentOptions, private fun resetGrpcStubs() { logger.info("Creating gRPC stubs") - channel?.shutdownNow() - - val channelBuilder = - if (inProcessServerName.isEmpty()) - NettyChannelBuilder.forAddress(hostName, port) - else - InProcessChannelBuilder.forName(inProcessServerName) + if (grpcStarted) + channel.shutdownNow() + else + grpcStarted = true - if (zipkinEnabled) - channelBuilder.intercept(grpcTracing!!.newClientInterceptor()) + channel = + channel(inProcessServerName = inProcessServerName, hostName = hostName, port = port) { + if (isZipkinEnabled) + intercept(grpcTracing.newClientInterceptor()) + usePlaintext(true) + } - channel = channelBuilder.usePlaintext(true).build() val interceptors = listOf(AgentClientInterceptor(this)) blockingStub = newBlockingStub(intercept(channel, interceptors)) asyncStub = newStub(intercept(channel, interceptors)) } - private fun updateScrapeCounter(type: String) = metrics?.scrapeRequests?.labels(type)?.inc() + private fun updateScrapeCounter(type: String) { + if (isMetricsEnabled) + metrics.scrapeRequests.labels(type).inc() + } private fun fetchUrl(scrapeRequest: ScrapeRequest): ScrapeResponse { - var statusCodeVal = 404 + var statusCode = 404 val path = scrapeRequest.path val scrapeResponse = - ScrapeResponse.newBuilder().apply { - agentId = scrapeRequest.agentId - scrapeId = scrapeRequest.scrapeId - } + ScrapeResponse.newBuilder() + .apply { + agentId = scrapeRequest.agentId + scrapeId = scrapeRequest.scrapeId + } val pathContext = pathContextMap[path] + if (pathContext == null) { logger.warn("Invalid path in fetchUrl(): $path") updateScrapeCounter("invalid_path") - return with(scrapeResponse) { - valid = false - reason = "Invalid path: $path" - statusCode = statusCodeVal - text = "" - contentType = "" - build() - } + return scrapeResponse + .run { + valid = false + reason = "Invalid path: $path" + this.statusCode = statusCode + text = "" + contentType = "" + build() + } } - val requestTimer = metrics?.scrapeRequestLatency?.labels(agentName)?.startTimer() + val requestTimer = if (isMetricsEnabled) metrics.scrapeRequestLatency.labels(agentName).startTimer() else null var reason = "None" + try { pathContext.fetchUrl(scrapeRequest).use { - statusCodeVal = it.code() + statusCode = it.code() if (it.isSuccessful) { updateScrapeCounter("success") return scrapeResponse - .setValid(true) - .setReason("") - .setStatusCode(statusCodeVal) - .setText(it.body()!!.string()) - .setContentType(it.header(CONTENT_TYPE)) - .build() + .run { + valid = true + reason = "" + this.statusCode = statusCode + text = it.body()?.string() ?: "" + contentType = it.header(CONTENT_TYPE) + build() + } } else { - reason = "Unsucessful response code $statusCodeVal" + reason = "Unsucessful response code $statusCode" } } } catch (e: IOException) { @@ -286,25 +308,28 @@ class Agent(options: AgentOptions, updateScrapeCounter("unsuccessful") return scrapeResponse - .setValid(false) - .setReason(reason) - .setStatusCode(statusCodeVal) - .setText("") - .setContentType("") - .build() + .run { + valid = false + this.reason = reason + this.statusCode = statusCode + text = "" + contentType = "" + build() + } } - // If successful, this will create an agentContxt on the Proxy and an interceptor will - // add an agent_id to the headers` + // If successful, this will create an agentContxt on the Proxy and an interceptor will add an agent_id to the headers` private fun connectAgent(): Boolean { return try { logger.info("Connecting to proxy at $proxyHost...") blockingStub.connectAgent(Empty.getDefaultInstance()) logger.info("Connected to proxy at $proxyHost") - metrics?.connects?.labels("success")?.inc() + if (isMetricsEnabled) + metrics.connects.labels("success")?.inc() true } catch (e: StatusRuntimeException) { - metrics?.connects?.labels("failure")?.inc() + if (isMetricsEnabled) + metrics.connects.labels("failure")?.inc() logger.info("Cannot connect to proxy at $proxyHost [${e.message}]") false } @@ -313,16 +338,19 @@ class Agent(options: AgentOptions, @Throws(RequestFailureException::class) private fun registerAgent() { val request = - with(RegisterAgentRequest.newBuilder()) { - agentId = this@Agent.agentId - agentName = this@Agent.agentName - hostName = this@Agent.hostName - build() + RegisterAgentRequest.newBuilder() + .run { + agentId = this@Agent.agentId + agentName = this@Agent.agentName + hostName = this@Agent.hostName + build() + } + blockingStub.registerAgent(request) + .let { + markMsgSent() + if (!it.valid) + throw RequestFailureException("registerAgent() - ${it.reason}") } - val response = blockingStub.registerAgent(request) - markMsgSent() - if (!response.valid) - throw RequestFailureException("registerAgent() - ${response.reason}") initialConnectionLatch.countDown() } @@ -356,42 +384,51 @@ class Agent(options: AgentOptions, fun pathMapSize(): Int { val request = - with(PathMapSizeRequest.newBuilder()) { - agentId = this@Agent.agentId - build() + PathMapSizeRequest.newBuilder() + .run { + agentId = this@Agent.agentId + build() + } + blockingStub.pathMapSize(request) + .let { + markMsgSent() + return it.pathCount } - val response = blockingStub.pathMapSize(request) - markMsgSent() - return response.pathCount } @Throws(RequestFailureException::class) private fun registerPathOnProxy(path: String): Long { val request = - with(RegisterPathRequest.newBuilder()) { - agentId = this@Agent.agentId - this.path = path - build() + RegisterPathRequest.newBuilder() + .run { + agentId = this@Agent.agentId + this.path = path + build() + } + blockingStub.registerPath(request) + .let { + markMsgSent() + if (!it.valid) + throw RequestFailureException("registerPath() - ${it.reason}") + return it.pathId } - val response = blockingStub.registerPath(request) - markMsgSent() - if (!response.valid) - throw RequestFailureException("registerPath() - ${response.reason}") - return response.pathId } @Throws(RequestFailureException::class) private fun unregisterPathOnProxy(path: String) { val request = - with(UnregisterPathRequest.newBuilder()) { - agentId = this@Agent.agentId - this.path = path - build() + UnregisterPathRequest.newBuilder() + .run { + agentId = this@Agent.agentId + this.path = path + build() + } + blockingStub.unregisterPath(request) + .let { + markMsgSent() + if (!it.valid) + throw RequestFailureException("unregisterPath() - ${it.reason}") } - val response = blockingStub.unregisterPath(request) - markMsgSent() - if (!response.valid) - throw RequestFailureException("unregisterPath() - ${response.reason}") } private fun readRequestAction(request: ScrapeRequest): Runnable { @@ -406,26 +443,30 @@ class Agent(options: AgentOptions, } private fun readRequestsFromProxy(disconnected: AtomicBoolean) { - val observer = object : StreamObserver { - override fun onNext(request: ScrapeRequest) { - readRequestsExecutorService.submit(readRequestAction(request)) - } + val agentInfo = + AgentInfo.newBuilder() + .run { + agentId = this@Agent.agentId + build() + } - override fun onError(t: Throwable) { - val status = Status.fromThrowable(t) - logger.info("Error in readRequestsFromProxy(): $status") - disconnected.set(true) - } + val observer = + streamObserver { + onNext { request -> + readRequestsExecutorService.submit(readRequestAction(request)) + } - override fun onCompleted() { - disconnected.set(true) - } - } - val agentInfo = - with(AgentInfo.newBuilder()) { - agentId = this@Agent.agentId - build() + onError { t -> + val status = Status.fromThrowable(t) + logger.error("Error in readRequestsFromProxy(): $status") + disconnected.set(true) + } + + onCompleted { + disconnected.set(true) + } } + asyncStub.readRequestsFromProxy(agentInfo, observer) } @@ -433,32 +474,31 @@ class Agent(options: AgentOptions, val checkMillis = configVals.internal.scrapeResponseQueueCheckMillis.toLong() val observer = asyncStub.writeResponsesToProxy( - object : StreamObserver { - override fun onNext(empty: Empty) { + streamObserver { + onNext { _ -> // Ignore Empty return value } - override fun onError(t: Throwable) { + onError { t -> val s = Status.fromThrowable(t) - logger.info("Error in writeResponsesToProxyUntilDisconnected(): ${s.code} ${s.description}") + logger.error("Error in writeResponsesToProxyUntilDisconnected(): ${s.code} ${s.description}") disconnected.set(true) } - override fun onCompleted() = disconnected.set(true) + onCompleted { disconnected.set(true) } }) while (!disconnected.get()) { try { // Set a short timeout to check if client has disconnected - val response = scrapeResponseQueue.poll(checkMillis, TimeUnit.MILLISECONDS) - if (response != null) { - observer.onNext(response) - markMsgSent() - } + scrapeResponseQueue.poll(checkMillis, TimeUnit.MILLISECONDS) + ?.let { + observer.onNext(it) + markMsgSent() + } } catch (e: InterruptedException) { // Ignore } - } logger.info("Disconnected from proxy at $proxyHost") @@ -475,18 +515,21 @@ class Agent(options: AgentOptions, try { val request = - with(HeartBeatRequest.newBuilder()) { - agentId = this@Agent.agentId - build() + HeartBeatRequest.newBuilder() + .run { + agentId = this@Agent.agentId + build() + } + blockingStub.sendHeartBeat(request) + .let { + markMsgSent() + if (!it.valid) { + logger.error("AgentId $agentId not found on proxy") + throw StatusRuntimeException(Status.NOT_FOUND) + } } - val response = blockingStub.sendHeartBeat(request) - markMsgSent() - if (!response.valid) { - logger.info("AgentId $agentId not found on proxy") - throw StatusRuntimeException(Status.NOT_FOUND) - } } catch (e: StatusRuntimeException) { - logger.info("Hearbeat failed ${e.status}") + logger.error("Hearbeat failed ${e.status}") disconnected.set(true) } } @@ -495,13 +538,13 @@ class Agent(options: AgentOptions, fun awaitInitialConnection(timeout: Long, unit: TimeUnit) = initialConnectionLatch.await(timeout, unit) override fun toString() = - MoreObjects.toStringHelper(this) - .add("agentId", agentId) - .add("agentName", agentName) - .add("proxyHost", proxyHost) - .add("adminService", adminService ?: "Disabled") - .add("metricsService", metricsService ?: "Disabled") - .toString() + toStringElements { + add("agentId", agentId) + add("agentName", agentName) + add("proxyHost", proxyHost) + add("adminService", if (isAdminEnabled) adminService else "Disabled") + add("metricsService", if (isMetricsEnabled) metricsService else "Disabled") + } companion object { private val logger = LoggerFactory.getLogger(Agent::class.java) @@ -510,11 +553,10 @@ class Agent(options: AgentOptions, fun main(argv: Array) { val options = AgentOptions(argv, true) - logger.info(getBanner("banners/agent.txt")) + logger.info(getBanner("banners/agent.txt", logger)) logger.info(getVersionDesc(false)) - val agent = Agent(options = options) - agent.startAsync() + Agent(options = options) { startSync() } } } -} +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/Proxy.kt b/src/main/java/io/prometheus/Proxy.kt index 46324e13..a4e8b149 100644 --- a/src/main/java/io/prometheus/Proxy.kt +++ b/src/main/java/io/prometheus/Proxy.kt @@ -18,45 +18,49 @@ package io.prometheus import com.codahale.metrics.health.HealthCheck import com.google.common.base.Joiner -import com.google.common.base.MoreObjects -import com.google.common.collect.Maps +import com.google.common.collect.Maps.newConcurrentMap import io.grpc.Attributes import io.prometheus.common.* +import io.prometheus.common.AdminConfig.Companion.newAdminConfig +import io.prometheus.common.MetricsConfig.Companion.newMetricsConfig +import io.prometheus.common.ZipkinConfig.Companion.newZipkinConfig +import io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.dsl.MetricsDsl.healthCheck import io.prometheus.grpc.UnregisterPathResponse import io.prometheus.proxy.* +import io.prometheus.proxy.ProxyGrpcService.Companion.newProxyGrpcService import org.slf4j.LoggerFactory import java.util.concurrent.ConcurrentMap +import kotlin.properties.Delegates class Proxy(options: ProxyOptions, - proxyPort: Int, + proxyPort: Int = options.agentPort, inProcessServerName: String = "", - testMode: Boolean = false) : GenericService(options.configVals, - AdminConfig.create(options.adminEnabled, - options.adminPort!!, - options.configVals.proxy.admin), - MetricsConfig.create(options.metricsEnabled, - options.metricsPort!!, - options.configVals.proxy.metrics), - ZipkinConfig.create(options.configVals.proxy.internal.zipkin), - testMode) { - - private val pathMap = Maps.newConcurrentMap() // Map path to AgentContext - private val scrapeRequestMap = Maps.newConcurrentMap() // Map scrape_id to agent_id - - val agentContextMap: ConcurrentMap = Maps.newConcurrentMap() // Map agent_id to AgentContext - val metrics = if (metricsEnabled) ProxyMetrics(this) else null + testMode: Boolean = false, + initBlock: (Proxy.() -> Unit)? = null) : GenericService(options.configVals, + newAdminConfig(options.adminEnabled, + options.adminPort, + options.configVals.proxy.admin), + newMetricsConfig(options.metricsEnabled, + options.metricsPort, + options.configVals.proxy.metrics), + newZipkinConfig(options.configVals.proxy.internal.zipkin), + testMode) { + + private val pathMap = newConcurrentMap() // Map path to AgentContext + private val scrapeRequestMap = newConcurrentMap() // Map scrape_id to agent_id + + val agentContextMap: ConcurrentMap = newConcurrentMap() // Map agent_id to AgentContext + var metrics: ProxyMetrics by Delegates.notNull() private val httpService = ProxyHttpService(this, proxyPort) private val grpcService: ProxyGrpcService = if (inProcessServerName.isEmpty()) - ProxyGrpcService.create(this, options.agentPort!!) + newProxyGrpcService(proxy = this, port = options.agentPort) else - ProxyGrpcService.create(this, inProcessServerName) - private val agentCleanupService = - if (configVals.internal.staleAgentCheckEnabled) - AgentContextCleanupService(this) - else - null + newProxyGrpcService(proxy = this, serverName = inProcessServerName) + + private var agentCleanupService: AgentContextCleanupService by Delegates.notNull() val agentContextSize: Int get() = agentContextMap.size @@ -74,21 +78,31 @@ class Proxy(options: ProxyOptions, get() = agentContextMap.values.map { it.scrapeRequestQueueSize }.sum() init { - addServices(grpcService, httpService, agentCleanupService!!) + if (isMetricsEnabled) + metrics = ProxyMetrics(this) + if (configVals.internal.staleAgentCheckEnabled) + agentCleanupService = AgentContextCleanupService(this) { addServices(this) } + addServices(grpcService, httpService) initService() + initBlock?.invoke(this) } override fun startUp() { super.startUp() - grpcService.startAsync() - httpService.startAsync() - agentCleanupService?.startAsync() ?: logger.info("Agent eviction thread not started") + grpcService.apply { startSync() } + httpService.apply { startSync() } + + if (configVals.internal.staleAgentCheckEnabled) + agentCleanupService.apply { startSync() } + else + logger.info("Agent eviction thread not started") } override fun shutDown() { - grpcService.stopAsync() - httpService.stopAsync() - agentCleanupService?.stopAsync() + grpcService.stopSync() + httpService.stopSync() + if (configVals.internal.staleAgentCheckEnabled) + agentCleanupService.stopSync() super.shutDown() } @@ -99,26 +113,25 @@ class Proxy(options: ProxyOptions, override fun registerHealthChecks() { super.registerHealthChecks() - healthCheckRegistry.register("grpc_service", grpcService.healthCheck) - healthCheckRegistry.register("scrape_response_map_check", - mapHealthCheck(scrapeRequestMap, - configVals.internal.scrapeRequestMapUnhealthySize)) healthCheckRegistry - .register("agent_scrape_request_queue", - object : HealthCheck() { - @Throws(Exception::class) - override fun check(): HealthCheck.Result { - val unhealthySize = configVals.internal.scrapeRequestQueueUnhealthySize - val vals = agentContextMap.entries - .filter { it.value.scrapeRequestQueueSize >= unhealthySize } - .map { "${it.value} ${it.value.scrapeRequestQueueSize}" } - .toList() - return if (vals.isEmpty()) - HealthCheck.Result.healthy() - else - HealthCheck.Result.unhealthy("Large scrapeRequestQueues: ${Joiner.on(", ").join(vals)}") - } - }) + .apply { + register("grpc_service", grpcService.healthCheck) + register("scrape_response_map_check", + newMapHealthCheck(scrapeRequestMap, configVals.internal.scrapeRequestMapUnhealthySize)) + register("agent_scrape_request_queue", + healthCheck { + val unhealthySize = configVals.internal.scrapeRequestQueueUnhealthySize + val vals = + agentContextMap.entries + .filter { it.value.scrapeRequestQueueSize >= unhealthySize } + .map { "${it.value} ${it.value.scrapeRequestQueueSize}" } + .toList() + if (vals.isEmpty()) + HealthCheck.Result.healthy() + else + HealthCheck.Result.unhealthy("Large scrapeRequestQueues: ${Joiner.on(", ").join(vals)}") + }) + } } fun addAgentContext(agentContext: AgentContext) = agentContextMap.put(agentContext.agentId, agentContext) @@ -126,20 +139,21 @@ class Proxy(options: ProxyOptions, fun getAgentContext(agentId: String) = agentContextMap[agentId] fun removeAgentContext(agentId: String?): AgentContext? { - if (agentId.isNullOrEmpty()) { + return if (agentId.isNullOrEmpty()) { logger.error("Missing agentId") - return null + null } - - val agentContext = agentContextMap.remove(agentId) - if (agentContext != null) { - logger.info("Removed $agentContext") - agentContext.markInvalid() + else { + val agentContext = agentContextMap.remove(agentId) + if (agentContext == null) { + logger.error("Missing AgentContext for agentId: $agentId") + } + else { + logger.info("Removed $agentContext") + agentContext.markInvalid() + } + agentContext } - else - logger.error("Missing AgentContext for agentId: $agentId") - - return agentContext } fun addToScrapeRequestMap(scrapeRequest: ScrapeRequestWrapper) = scrapeRequestMap.put(scrapeRequest.scrapeId, @@ -169,50 +183,55 @@ class Proxy(options: ProxyOptions, when { agentContext == null -> { val msg = "Unable to remove path /$path - path not found" - logger.info(msg) - responseBuilder.setValid(false).setReason(msg) + logger.error(msg) + responseBuilder + .apply { + valid = false + reason = msg + } } agentContext.agentId != agentId -> { val msg = "Unable to remove path /$path - invalid agentId: $agentId (owner is ${agentContext.agentId})" - logger.info(msg) - responseBuilder.setValid(false).setReason(msg) + logger.error(msg) + responseBuilder + .apply { + valid = false + reason = msg + } } - else - -> { + else -> { pathMap.remove(path) if (!isTestMode) logger.info("Removed path /$path for $agentContext") - responseBuilder.setValid(true).setReason("") + responseBuilder + .apply { + valid = true + reason = "" + } } } } } fun removePathByAgentId(agentId: String?) { - if (agentId.isNullOrEmpty()) { - logger.info("Missing agentId") - return - } - - synchronized(pathMap) { - pathMap.forEach { k, v -> - if (v.agentId == agentId) { - val agentContext = pathMap.remove(k) - if (agentContext != null) - logger.info("Removed path /$k for $agentContext") - else - logger.error("Missing path /$k for agentId: $agentId") + if (agentId.isNullOrEmpty()) + logger.error("Missing agentId") + else + synchronized(pathMap) { + pathMap.forEach { k, v -> + if (v.agentId == agentId) + pathMap.remove(k) + ?.let { logger.info("Removed path /$k for $it") } ?: logger.error("Missing path /$k for agentId: $agentId") } } - } } override fun toString() = - MoreObjects.toStringHelper(this) - .add("proxyPort", httpService.port) - .add("adminService", adminService ?: "Disabled") - .add("metricsService", metricsService ?: "Disabled") - .toString() + toStringElements { + add("proxyPort", httpService.port) + add("adminService", if (isAdminEnabled) adminService else "Disabled") + add("metricsService", if (isMetricsEnabled) metricsService else "Disabled") + } companion object { private val logger = LoggerFactory.getLogger(Proxy::class.java) @@ -224,11 +243,10 @@ class Proxy(options: ProxyOptions, fun main(argv: Array) { val options = ProxyOptions(argv) - logger.info(getBanner("banners/proxy.txt")) + logger.info(getBanner("banners/proxy.txt", logger)) logger.info(getVersionDesc(false)) - val proxy = Proxy(options = options, proxyPort = options.proxyPort!!) - proxy.startAsync() + Proxy(options = options) { startSync() } } } } diff --git a/src/main/java/io/prometheus/agent/AgentClientInterceptor.kt b/src/main/java/io/prometheus/agent/AgentClientInterceptor.kt index d541b92c..88c0cf89 100644 --- a/src/main/java/io/prometheus/agent/AgentClientInterceptor.kt +++ b/src/main/java/io/prometheus/agent/AgentClientInterceptor.kt @@ -28,27 +28,23 @@ class AgentClientInterceptor(private val agent: Agent) : ClientInterceptor { next: Channel): ClientCall = // final String methodName = method.getFullMethodName(); // logger.info("Intercepting {}", methodName); - object : ForwardingClientCall.SimpleForwardingClientCall(agent.channel!!.newCall(method, callOptions)) { - override fun start(responseListener: ClientCall.Listener, headers: Metadata) { + object : ForwardingClientCall.SimpleForwardingClientCall(agent.channel.newCall(method, callOptions)) { + override fun start(responseListener: ClientCall.Listener, metadata: Metadata) { super.start( object : ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) { override fun onHeaders(headers: Metadata?) { // Grab agent_id from headers if not already assigned if (agent.agentId.isEmpty()) { - val agentId = headers!!.get(Metadata.Key.of(Proxy.AGENT_ID, - Metadata.ASCII_STRING_MARSHALLER)) - if (agentId != null) { - agent.agentId = agentId - logger.info("Assigned agentId to $agent") - } - else { - logger.error("Headers missing AGENT_ID key") - } + headers!!.get(Metadata.Key.of(Proxy.AGENT_ID, Metadata.ASCII_STRING_MARSHALLER)) + ?.let { + agent.agentId = it + logger.info("Assigned agentId to $agent") + } ?: logger.error("Headers missing AGENT_ID key") } super.onHeaders(headers) } }, - headers) + metadata) } } diff --git a/src/main/java/io/prometheus/agent/AgentMetrics.kt b/src/main/java/io/prometheus/agent/AgentMetrics.kt index 1d5d32aa..827552e8 100644 --- a/src/main/java/io/prometheus/agent/AgentMetrics.kt +++ b/src/main/java/io/prometheus/agent/AgentMetrics.kt @@ -17,45 +17,44 @@ package io.prometheus.agent import io.prometheus.Agent -import io.prometheus.client.Collector import io.prometheus.client.Counter -import io.prometheus.client.Gauge import io.prometheus.client.Summary -import io.prometheus.common.SamplerGauge -import io.prometheus.common.SamplerGaugeData +import io.prometheus.common.SamplerGaugeCollector +import io.prometheus.dsl.MetricsDsl.counter +import io.prometheus.dsl.MetricsDsl.gauge +import io.prometheus.dsl.MetricsDsl.summary class AgentMetrics(agent: Agent) { val scrapeRequests: Counter = - Counter.build() - .name("agent_scrape_requests") - .help("Agent scrape requests") - .labelNames("type") - .register() + counter { + name("agent_scrape_requests") + help("Agent scrape requests") + labelNames("type") + } val connects: Counter = - Counter.build() - .name("agent_connect_count") - .help("Agent connect counts") - .labelNames("type") - .register() + counter { + name("agent_connect_count") + help("Agent connect counts") + labelNames("type") + } val scrapeRequestLatency: Summary = - Summary.build() - .name("agent_scrape_request_latency_seconds") - .help("Agent scrape request latency in seconds") - .labelNames("agent_name") - .register() + summary { + name("agent_scrape_request_latency_seconds") + help("Agent scrape request latency in seconds") + labelNames("agent_name") + } init { - Gauge.build() - .name("agent_start_time_seconds") - .help("Agent start time in seconds") - .register() - .setToCurrentTime() - - SamplerGauge("agent_scrape_queue_size", - "Agent scrape response queue size", - SamplerGaugeData { agent.scrapeResponseQueueSize.toDouble() }).register() + gauge { + name("agent_start_time_seconds") + help("Agent start time in seconds") + }.setToCurrentTime() + + SamplerGaugeCollector("agent_scrape_queue_size", + "Agent scrape response queue size", + data = { agent.scrapeResponseQueueSize.toDouble() }) } } diff --git a/src/main/java/io/prometheus/agent/AgentOptions.kt b/src/main/java/io/prometheus/agent/AgentOptions.kt index 69c340f7..5e09749c 100644 --- a/src/main/java/io/prometheus/agent/AgentOptions.kt +++ b/src/main/java/io/prometheus/agent/AgentOptions.kt @@ -24,17 +24,20 @@ import io.prometheus.common.EnvVars import io.prometheus.common.EnvVars.AGENT_CONFIG import io.prometheus.common.EnvVars.PROXY_HOSTNAME -class AgentOptions(argv: Array, exitOnMissingConfig: Boolean) : BaseOptions(Agent::class.java.name, argv, AGENT_CONFIG.name, exitOnMissingConfig) { +class AgentOptions(argv: Array, exitOnMissingConfig: Boolean) : BaseOptions(Agent::class.java.name, + argv, + AGENT_CONFIG.name, + exitOnMissingConfig) { constructor(args: List, exitOnMissingConfig: Boolean) : this(Iterables.toArray(args, String::class.java), exitOnMissingConfig) @Parameter(names = ["-p", "--proxy"], description = "Proxy hostname") - var proxyHostname: String? = null + var proxyHostname: String = "" private set @Parameter(names = ["-n", "--name"], description = "Agent name") - var agentName: String? = null + var agentName: String = "" private set init { @@ -42,16 +45,16 @@ class AgentOptions(argv: Array, exitOnMissingConfig: Boolean) : BaseOpti } override fun assignConfigVals() { - if (proxyHostname.isNullOrEmpty()) { + if (proxyHostname.isEmpty()) { val configHostname = configVals.agent.proxy.hostname proxyHostname = PROXY_HOSTNAME.getEnv(if (configHostname.contains(":")) - configHostname - else - "$configHostname:${configVals.agent.proxy.port}") + configHostname + else + "$configHostname:${configVals.agent.proxy.port}") ?: "" } - if (agentName.isNullOrEmpty()) - agentName = EnvVars.AGENT_NAME.getEnv(configVals.agent.name) + if (agentName.isEmpty()) + agentName = EnvVars.AGENT_NAME.getEnv(configVals.agent.name) ?: "" assignAdminEnabled(configVals.agent.admin.enabled) assignAdminPort(configVals.agent.admin.port) diff --git a/src/main/java/io/prometheus/agent/PathContext.kt b/src/main/java/io/prometheus/agent/PathContext.kt index 05a88be1..40d607fc 100644 --- a/src/main/java/io/prometheus/agent/PathContext.kt +++ b/src/main/java/io/prometheus/agent/PathContext.kt @@ -16,8 +16,8 @@ package io.prometheus.agent -import com.google.common.base.MoreObjects import com.google.common.net.HttpHeaders.ACCEPT +import io.prometheus.dsl.GuavaDsl.toStringElements import io.prometheus.grpc.ScrapeRequest import okhttp3.OkHttpClient import okhttp3.Request @@ -35,22 +35,25 @@ class PathContext(private val okHttpClient: OkHttpClient, fun fetchUrl(scrapeRequest: ScrapeRequest): Response = try { logger.debug("Fetching $this") - val builder = - if (!scrapeRequest.accept.isNullOrEmpty()) - request.header(ACCEPT, scrapeRequest.accept) - else - request - okHttpClient.newCall(builder.build()).execute() + val request = + request + .run { + if (!scrapeRequest.accept.isNullOrEmpty()) + header(ACCEPT, scrapeRequest.accept) + build() + } + + okHttpClient.newCall(request).execute() } catch (e: IOException) { logger.info("Failed HTTP request: $url [${e.javaClass.simpleName}: ${e.message}]") throw e } override fun toString() = - MoreObjects.toStringHelper(this) - .add("path", "/" + path) - .add("url", url) - .toString() + toStringElements { + add("path", "/" + path) + add("url", url) + } companion object { private val logger = LoggerFactory.getLogger(PathContext::class.java) diff --git a/src/main/java/io/prometheus/common/AdminConfig.kt b/src/main/java/io/prometheus/common/AdminConfig.kt index 422810bb..dcd5a4b4 100644 --- a/src/main/java/io/prometheus/common/AdminConfig.kt +++ b/src/main/java/io/prometheus/common/AdminConfig.kt @@ -24,7 +24,7 @@ data class AdminConfig(val enabled: Boolean, val threadDumpPath: String) { companion object { - fun create(enabled: Boolean, port: Int, admin: ConfigVals.Proxy2.Admin2) = + fun newAdminConfig(enabled: Boolean, port: Int, admin: ConfigVals.Proxy2.Admin2) = AdminConfig(enabled, port, admin.pingPath, @@ -32,7 +32,7 @@ data class AdminConfig(val enabled: Boolean, admin.healthCheckPath, admin.threadDumpPath) - fun create(enabled: Boolean, port: Int, admin: ConfigVals.Agent.Admin) = + fun newAdminConfig(enabled: Boolean, port: Int, admin: ConfigVals.Agent.Admin) = AdminConfig(enabled, port, admin.pingPath, diff --git a/src/main/java/io/prometheus/common/AdminService.kt b/src/main/java/io/prometheus/common/AdminService.kt index 5c7d5d66..788194fd 100644 --- a/src/main/java/io/prometheus/common/AdminService.kt +++ b/src/main/java/io/prometheus/common/AdminService.kt @@ -16,53 +16,61 @@ package io.prometheus.common +import com.codahale.metrics.health.HealthCheckRegistry 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 io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.dsl.SparkDsl.servletContextHandler +import io.prometheus.guava.GenericIdleService +import io.prometheus.guava.genericServiceListener import org.eclipse.jetty.server.Server -import org.eclipse.jetty.servlet.ServletContextHandler import org.eclipse.jetty.servlet.ServletHolder +import org.slf4j.LoggerFactory -class AdminService(service: GenericService, +class AdminService(healthCheckRegistry: HealthCheckRegistry, private val port: Int, private val pingPath: String, private val versionPath: String, private val healthCheckPath: String, - private val threadDumpPath: String) : AbstractIdleService() { - private val server: Server = Server(port) + private val threadDumpPath: String, + initBlock: (AdminService.() -> Unit)? = null) : GenericIdleService() { + private val server = + Server(port) + .apply { + handler = + servletContextHandler { + contextPath = "/" + if (pingPath.isNotBlank()) + addServlet(ServletHolder(PingServlet()), "/$pingPath") + if (versionPath.isNotBlank()) + addServlet(ServletHolder(VersionServlet()), "/$versionPath") + if (healthCheckPath.isNotBlank()) + addServlet(ServletHolder(HealthCheckServlet(healthCheckRegistry)), "/$healthCheckPath") + if (threadDumpPath.isNotBlank()) + addServlet(ServletHolder(ThreadDumpServlet()), "/$threadDumpPath") + } + } init { - val context = ServletContextHandler() - context.contextPath = "/" - server.handler = context + addListener(genericServiceListener(this, logger), MoreExecutors.directExecutor()) + initBlock?.invoke(this) + } - if (pingPath.isNotBlank()) - context.addServlet(ServletHolder(PingServlet()), "/$pingPath") - if (versionPath.isNotBlank()) - context.addServlet(ServletHolder(VersionServlet()), "/$versionPath") - if (healthCheckPath.isNotBlank()) - context.addServlet(ServletHolder(HealthCheckServlet(service.healthCheckRegistry)), "/$healthCheckPath") - if (threadDumpPath.isNotBlank()) - context.addServlet(ServletHolder(ThreadDumpServlet()), "/$threadDumpPath") + override fun startUp() = server.start() - addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) - } + override fun shutDown() = server.stop() - override fun startUp() { - server.start() - } + override fun toString() = + toStringElements { + add("ping", ":$port/$pingPath") + add("healthcheck", ":$port/$healthCheckPath") + add("threaddump", ":$port/$threadDumpPath") + } - override fun shutDown() { - server.stop() + companion object { + private val logger = LoggerFactory.getLogger(AdminService::class.java) } - override fun toString() = - MoreObjects.toStringHelper(this) - .add("ping", ":$port/$pingPath") - .add("healthcheck", ":$port/$healthCheckPath") - .add("threaddump", ":$port/$threadDumpPath") - .toString() } diff --git a/src/main/java/io/prometheus/common/AtomicDelegates.kt b/src/main/java/io/prometheus/common/AtomicDelegates.kt deleted file mode 100644 index bfdee2ce..00000000 --- a/src/main/java/io/prometheus/common/AtomicDelegates.kt +++ /dev/null @@ -1,86 +0,0 @@ -package io.prometheus.common - -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicLong -import java.util.concurrent.atomic.AtomicReference -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -object AtomicDelegates { - fun notNullReference(): ReadWriteProperty = NotNullAtomicReferenceDelegate() - - fun nullableReference(): NullableReadWriteProperty = NullableAtomicReferenceDelegate() - - fun boolean(initValue: Boolean = false): ReadWriteProperty = AtomicBooleanDelegate(initValue) - - fun long(initValue: Long = 0): ReadWriteProperty = AtomicLongDelegate(initValue) - - fun integer(initValue: Int = 0): ReadWriteProperty = AtomicIntDelegate(initValue) -} - -private class NotNullAtomicReferenceDelegate : ReadWriteProperty { - private val atomicVal = AtomicReference() - - override operator fun getValue(thisRef: Any?, property: KProperty<*>): T { - return atomicVal.get() ?: throw IllegalStateException("Property ${property.name} should be initialized before get.") - } - - override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - atomicVal.set(value) - } -} - -interface NullableReadWriteProperty { - operator fun getValue(thisRef: R, property: KProperty<*>): T? - - operator fun setValue(thisRef: R, property: KProperty<*>, value: T) -} - -private class NullableAtomicReferenceDelegate : NullableReadWriteProperty { - private val atomicVal = AtomicReference() - - override operator fun getValue(thisRef: Any?, property: KProperty<*>): T? { - return atomicVal.get() - } - - override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - atomicVal.set(value) - } -} - -private class AtomicBooleanDelegate(initValue: Boolean) : ReadWriteProperty { - private val atomicVal = AtomicBoolean(initValue) - - override operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return atomicVal.get() - } - - override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { - atomicVal.set(value) - } -} - -private class AtomicLongDelegate(initValue: Long) : ReadWriteProperty { - private val atomicVal = AtomicLong(initValue) - - override operator fun getValue(thisRef: Any?, property: KProperty<*>): Long { - return atomicVal.get() - } - - override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) { - atomicVal.set(value) - } -} - -private class AtomicIntDelegate(initValue: Int) : ReadWriteProperty { - private val atomicVal = AtomicInteger(initValue) - - override operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return atomicVal.get() - } - - override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { - atomicVal.set(value) - } -} \ No newline at end of file diff --git a/src/main/java/io/prometheus/common/BaseOptions.kt b/src/main/java/io/prometheus/common/BaseOptions.kt index d112f751..b8349cf2 100644 --- a/src/main/java/io/prometheus/common/BaseOptions.kt +++ b/src/main/java/io/prometheus/common/BaseOptions.kt @@ -31,17 +31,17 @@ import kotlin.properties.Delegates abstract class BaseOptions protected constructor(private val progName: String, private val argv: Array, private val envConfig: String, - private val exitOnMissingConfig: Boolean) { + private val exitOnMissingConfig: Boolean = false) { @Parameter(names = ["-c", "--conf", "--config"], description = "Configuration file or url") - private var configName: String? = null + private var configName: String = "" @Parameter(names = ["-r", "--admin"], description = "Admin servlets enabled") var adminEnabled: Boolean = false private set @Parameter(names = ["-i", "--admin_port"], description = "Admin servlets port") - var adminPort: Int? = null + var adminPort: Int = -1 private set @Parameter(names = ["-e", "--metrics"], description = "Metrics enabled") @@ -49,12 +49,12 @@ abstract class BaseOptions protected constructor(private val progName: String, private set @Parameter(names = ["-m", "--metrics_port"], description = "Metrics listen port") - var metricsPort: Int? = null + var metricsPort: Int = -1 private set @Parameter(names = ["-v", "--version"], description = "Print version info and exit", - validateWith = [(VersionValidator::class)]) + validateWith = [VersionValidator::class]) private var version = false @Parameter(names = ["-u", "--usage"], help = true) @@ -81,11 +81,12 @@ abstract class BaseOptions protected constructor(private val progName: String, private fun parseArgs(argv: Array?) { try { val jcom = - JCommander(this).apply { - programName = progName - setCaseSensitiveOptions(false) - parse(*argv ?: arrayOf()) - } + JCommander(this) + .apply { + programName = progName + setCaseSensitiveOptions(false) + parse(*argv ?: arrayOf()) + } if (usage) { jcom.usage() @@ -103,7 +104,7 @@ abstract class BaseOptions protected constructor(private val progName: String, } protected fun assignAdminPort(defaultVal: Int) { - if (adminPort == null) + if (adminPort == -1) adminPort = ADMIN_PORT.getEnv(defaultVal) } @@ -113,12 +114,12 @@ abstract class BaseOptions protected constructor(private val progName: String, } protected fun assignMetricsPort(defaultVal: Int) { - if (metricsPort == null) + if (metricsPort == -1) metricsPort = METRICS_PORT.getEnv(defaultVal) } private fun readConfig(envConfig: String, exitOnMissingConfig: Boolean) { - config = readConfig(configName, + config = readConfig(if (configName.isNotEmpty()) configName else System.getenv(envConfig) ?: "", envConfig, ConfigParseOptions.defaults().setAllowMissing(false), ConfigFactory.load().resolve(), @@ -136,24 +137,22 @@ abstract class BaseOptions protected constructor(private val progName: String, } } - private fun readConfig(cliConfig: String?, + private fun readConfig(configName: String, envConfig: String, configParseOptions: ConfigParseOptions, fallback: Config, exitOnMissingConfig: Boolean): Config { - val configName = cliConfig ?: System.getenv(envConfig) - when { - configName.isNullOrBlank() -> { + configName.isBlank() -> { if (exitOnMissingConfig) { - logger.error("A configuration file or url must be specified with --getConfig or \$$envConfig") + logger.error("A configuration file or url must be specified with --config or \$$envConfig") System.exit(1) } return fallback } - configName.isUrlPrefix() -> { + configName.isUrlPrefix() -> { try { val configSyntax = getConfigSyntax(configName) return ConfigFactory.parseURL(URL(configName), configParseOptions.setSyntax(configSyntax)) @@ -166,7 +165,7 @@ abstract class BaseOptions protected constructor(private val progName: String, } } - else -> { + else -> { try { return ConfigFactory.parseFileAnySyntax(File(configName), configParseOptions).withFallback(fallback) } catch (e: Exception) { diff --git a/src/main/java/io/prometheus/common/EnvVars.kt b/src/main/java/io/prometheus/common/EnvVars.kt index afb1321f..c2218900 100644 --- a/src/main/java/io/prometheus/common/EnvVars.kt +++ b/src/main/java/io/prometheus/common/EnvVars.kt @@ -36,11 +36,9 @@ enum class EnvVars { ADMIN_ENABLED, ADMIN_PORT; - private val env: String? by lazy { getenv(name) } + fun getEnv(defaultVal: String): String? = getenv(name) ?: defaultVal - fun getEnv(defaultVal: String): String? = env ?: defaultVal + fun getEnv(defaultVal: Boolean): Boolean = getenv(name)?.toBoolean() ?: defaultVal - fun getEnv(defaultVal: Boolean): Boolean = env?.toBoolean() ?: defaultVal - - fun getEnv(defaultVal: Int): Int = env?.toInt() ?: defaultVal + fun getEnv(defaultVal: Int): Int = getenv(name)?.toInt() ?: defaultVal } diff --git a/src/main/java/io/prometheus/common/GenericService.kt b/src/main/java/io/prometheus/common/GenericService.kt index c9c0c0cb..4de4488e 100644 --- a/src/main/java/io/prometheus/common/GenericService.kt +++ b/src/main/java/io/prometheus/common/GenericService.kt @@ -22,106 +22,128 @@ import com.codahale.metrics.health.HealthCheckRegistry import com.codahale.metrics.health.jvm.ThreadDeadlockHealthCheck import com.codahale.metrics.jmx.JmxReporter import com.google.common.base.Joiner -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 io.prometheus.dsl.GuavaDsl.serviceManager +import io.prometheus.dsl.GuavaDsl.serviceManagerListener +import io.prometheus.dsl.MetricsDsl.healthCheck +import io.prometheus.guava.GenericExecutionThreadService +import io.prometheus.guava.genericServiceListener import org.slf4j.LoggerFactory import java.io.Closeable import kotlin.properties.Delegates abstract class GenericService protected constructor(protected val genericConfigVals: ConfigVals, - adminConfig: AdminConfig, - metricsConfig: MetricsConfig, - zipkinConfig: ZipkinConfig, - val isTestMode: Boolean) : AbstractExecutionThreadService(), Closeable { + private val adminConfig: AdminConfig, + private val metricsConfig: MetricsConfig, + private val zipkinConfig: ZipkinConfig, + val isTestMode: Boolean) : GenericExecutionThreadService(), Closeable { + protected val healthCheckRegistry = HealthCheckRegistry() - private val metricRegistry = MetricRegistry() - val healthCheckRegistry = HealthCheckRegistry() + private val services = mutableListOf() - private val services = mutableListOf(this) - private val jmxReporter = JmxReporter.forRegistry(metricRegistry).build() - private var serviceManager: ServiceManager by Delegates.notNull() + private lateinit var serviceManager: ServiceManager - protected val adminService: AdminService? - protected val metricsService: MetricsService? + val isAdminEnabled: Boolean + get() = adminConfig.enabled - val zipkinReporterService: ZipkinReporterService? + val isMetricsEnabled: Boolean + get() = metricsConfig.enabled - val zipkinEnabled: Boolean - get() = zipkinReporterService != null + val isZipkinEnabled: Boolean + get() = zipkinConfig.enabled - val metricsEnabled: Boolean - get() = metricsService != null + private var jmxReporter: JmxReporter by Delegates.notNull() + var adminService: AdminService by Delegates.notNull() + var metricsService: MetricsService by Delegates.notNull() + var zipkinReporterService: ZipkinReporterService by Delegates.notNull() init { - if (adminConfig.enabled) { - adminService = AdminService(this, + if (isAdminEnabled) { + adminService = AdminService(healthCheckRegistry, adminConfig.port, adminConfig.pingPath, adminConfig.versionPath, adminConfig.healthCheckPath, - adminConfig.threadDumpPath) - addService(adminService) + adminConfig.threadDumpPath) { addService(this) } } else { logger.info("Admin service disabled") - adminService = null } - if (metricsConfig.enabled) { - metricsService = MetricsService(metricsConfig.port, metricsConfig.path) - addService(metricsService) + if (isMetricsEnabled) { + metricsService = MetricsService(metricsConfig.port, metricsConfig.path) { addService(this) } SystemMetrics.initialize(metricsConfig.standardExportsEnabled, metricsConfig.memoryPoolsExportsEnabled, metricsConfig.garbageCollectorExportsEnabled, metricsConfig.threadExportsEnabled, metricsConfig.classLoadingExportsEnabled, metricsConfig.versionInfoExportsEnabled) + jmxReporter = JmxReporter.forRegistry(MetricRegistry()).build() } else { logger.info("Metrics service disabled") - metricsService = null } - if (zipkinConfig.enabled) { + if (isZipkinEnabled) { val url = "http://${zipkinConfig.hostname}:${zipkinConfig.port}/${zipkinConfig.path}" - zipkinReporterService = ZipkinReporterService(url) - addService(zipkinReporterService) + zipkinReporterService = ZipkinReporterService(url) { addService(this) } } else { logger.info("Zipkin reporter service disabled") - zipkinReporterService = null } - - addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) } fun initService() { - serviceManager = ServiceManager(services) - serviceManager.addListener(newListener()) + addListener(genericServiceListener(this, logger), MoreExecutors.directExecutor()) + addService(this) + val clazzName = javaClass.simpleName + serviceManager = + serviceManager(services) { + addListener( + serviceManagerListener { + healthy { logger.info("All $clazzName services healthy") } + stopped { logger.info("All $clazzName services stopped") } + failure { service -> logger.info("$clazzName service failed: $service") } + }) + } registerHealthChecks() } override fun startUp() { super.startUp() - zipkinReporterService?.startAsync() - jmxReporter?.start() - metricsService?.startAsync() - adminService?.startAsync() + if (isZipkinEnabled) + zipkinReporterService.startSync() + + if (isMetricsEnabled) { + metricsService.startSync() + jmxReporter.start() + } + + if (isAdminEnabled) + adminService.startSync() + Runtime.getRuntime().addShutdownHook(shutDownHookAction(this)) } override fun shutDown() { - adminService?.stopAsync() - metricsService?.stopAsync() - jmxReporter?.stop() - zipkinReporterService?.stopAsync() + if (isAdminEnabled) + adminService.stopSync() + + if (isMetricsEnabled) { + metricsService.stopSync() + jmxReporter.stop() + } + + if (isZipkinEnabled) + zipkinReporterService.stopSync() + super.shutDown() } override fun close() { - stopAsync() + stopSync() } private fun addService(service: Service) { @@ -135,37 +157,29 @@ abstract class GenericService protected constructor(protected val genericConfigV } protected open fun registerHealthChecks() { - healthCheckRegistry.register("thread_deadlock", ThreadDeadlockHealthCheck()) - if (metricsEnabled) - healthCheckRegistry.register("metrics_service", metricsService!!.healthCheck) healthCheckRegistry - .register( - "all_services_healthy", - object : HealthCheck() { - @Throws(Exception::class) - override fun check(): HealthCheck.Result { - return if (serviceManager.isHealthy) + .apply { + register("thread_deadlock", ThreadDeadlockHealthCheck()) + if (isMetricsEnabled) + register("metrics_service", metricsService.healthCheck) + register( + "all_services_healthy", + healthCheck { + if (serviceManager.isHealthy) HealthCheck.Result.healthy() else { - val vals = serviceManager.servicesByState() - .entries() - .filter { it.key !== Service.State.RUNNING } - .onEach { logger.warn("Incorrect state - ${it.key}: ${it.value}") } - .map { "${it.key}: ${it.value}" } - .toList() + val vals = + serviceManager + .servicesByState() + .entries() + .filter { it.key !== Service.State.RUNNING } + .onEach { logger.warn("Incorrect state - ${it.key}: ${it.value}") } + .map { "${it.key}: ${it.value}" } + .toList() HealthCheck.Result.unhealthy("Incorrect state: ${Joiner.on(", ").join(vals)}") } - } - }) - } - - private fun newListener(): ServiceManager.Listener { - val serviceName = javaClass.simpleName - return object : ServiceManager.Listener() { - override fun healthy() = logger.info("All $serviceName services healthy") - override fun stopped() = logger.info("All $serviceName services stopped") - override fun failure(service: Service?) = logger.info("$serviceName service failed: $service") - } + }) + } } companion object { diff --git a/src/main/java/io/prometheus/common/GenericServiceListener.kt b/src/main/java/io/prometheus/common/GenericServiceListener.kt deleted file mode 100644 index aa9d9062..00000000 --- a/src/main/java/io/prometheus/common/GenericServiceListener.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.prometheus.common - -import com.google.common.util.concurrent.Service -import org.slf4j.LoggerFactory - -class GenericServiceListener(private val service: Service) : Service.Listener() { - - override fun starting() { - super.starting() - logger.info("Starting $service") - } - - override fun running() { - super.running() - logger.info("Running $service") - } - - override fun stopping(from: Service.State) { - super.stopping(from) - logger.info("Stopping $service") - } - - override fun terminated(from: Service.State) { - super.terminated(from) - logger.info("Terminated $service") - } - - override fun failed(from: Service.State, t: Throwable) { - super.failed(from, t) - logger.info("Failed on $from $service", t) - } - - companion object { - private val logger = LoggerFactory.getLogger(GenericServiceListener::class.java) - } -} diff --git a/src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt b/src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt index a95b2e5a..7db153a7 100644 --- a/src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt +++ b/src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt @@ -16,38 +16,27 @@ package io.prometheus.common -import com.google.common.base.Preconditions -import com.google.common.util.concurrent.ThreadFactoryBuilder -import io.prometheus.client.Counter -import io.prometheus.client.Gauge +import io.prometheus.dsl.MetricsDsl.counter +import io.prometheus.dsl.MetricsDsl.gauge import java.util.concurrent.ThreadFactory -class InstrumentedThreadFactory(delegate: ThreadFactory, name: String, help: String) : ThreadFactory { +class InstrumentedThreadFactory(private val delegate: ThreadFactory, name: String, help: String) : ThreadFactory { - private val delegate = Preconditions.checkNotNull(delegate) - private val created: Counter - private val running: Gauge - private val terminated: Counter - - init { - Preconditions.checkNotNull(name) - Preconditions.checkNotNull(help) - created = - Counter.build() - .name("${name}_threads_created") - .help("$help threads created") - .register() - running = - Gauge.build() - .name("${name}_threads_running") - .help("$help threads running") - .register() - terminated = - Counter.build() - .name("${name}_threads_terminated") - .help("$help threads terminated") - .register() - } + private val created = + counter { + name("${name}_threads_created") + help("$help threads created") + } + private val running = + gauge { + name("${name}_threads_running") + help("$help threads running") + } + private val terminated = + counter { + name("${name}_threads_terminated") + help("$help threads terminated") + } override fun newThread(runnable: Runnable): Thread { val wrappedRunnable = InstrumentedRunnable(runnable) @@ -68,11 +57,4 @@ class InstrumentedThreadFactory(delegate: ThreadFactory, name: String, help: Str } } - companion object { - fun newInstrumentedThreadFactory(name: String, help: String, daemon: Boolean): ThreadFactory { - val threadFactory = ThreadFactoryBuilder().setNameFormat(name + "-%d").setDaemon(daemon).build() - return InstrumentedThreadFactory(threadFactory, name, help) - } - } - } diff --git a/src/main/java/io/prometheus/common/MetricsConfig.kt b/src/main/java/io/prometheus/common/MetricsConfig.kt index ceb4b89a..e36f09e4 100644 --- a/src/main/java/io/prometheus/common/MetricsConfig.kt +++ b/src/main/java/io/prometheus/common/MetricsConfig.kt @@ -27,7 +27,7 @@ data class MetricsConfig(val enabled: Boolean, val versionInfoExportsEnabled: Boolean) { companion object { - fun create(enabled: Boolean, port: Int, metrics: ConfigVals.Proxy2.Metrics2) = + fun newMetricsConfig(enabled: Boolean, port: Int, metrics: ConfigVals.Proxy2.Metrics2) = MetricsConfig(enabled, port, metrics.path, @@ -38,7 +38,7 @@ data class MetricsConfig(val enabled: Boolean, metrics.classLoadingExportsEnabled, metrics.versionInfoExportsEnabled) - fun create(enabled: Boolean, port: Int, metrics: ConfigVals.Agent.Metrics) = + fun newMetricsConfig(enabled: Boolean, port: Int, metrics: ConfigVals.Agent.Metrics) = MetricsConfig(enabled, port, metrics.path, diff --git a/src/main/java/io/prometheus/common/MetricsService.kt b/src/main/java/io/prometheus/common/MetricsService.kt index 6def041e..989fca06 100644 --- a/src/main/java/io/prometheus/common/MetricsService.kt +++ b/src/main/java/io/prometheus/common/MetricsService.kt @@ -17,42 +17,52 @@ 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 io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.dsl.JettyDsl.server +import io.prometheus.dsl.JettyDsl.servletContextHandler +import io.prometheus.dsl.MetricsDsl.healthCheck +import io.prometheus.guava.GenericIdleService +import io.prometheus.guava.genericServiceListener import org.eclipse.jetty.servlet.ServletHolder +import org.slf4j.LoggerFactory -class MetricsService(private val port: Int, private val path: String) : AbstractIdleService() { - private val server: Server = Server(port) - val healthCheck: HealthCheck = object : HealthCheck() { - @Throws(Exception::class) - override fun check(): HealthCheck.Result { - return if (server.isRunning) HealthCheck.Result.healthy() else HealthCheck.Result.unhealthy("Jetty server not running") - } - } +class MetricsService(private val port: Int, + private val path: String, + initBlock: (MetricsService.() -> Unit)? = null) : GenericIdleService() { + private val server = + server(port) { + handler = + servletContextHandler { + contextPath = "/" + addServlet(ServletHolder(MetricsServlet()), "/$path") + } + } + val healthCheck = + healthCheck { + if (server.isRunning) + HealthCheck.Result.healthy() + else + HealthCheck.Result.unhealthy("Jetty server not running") + } - init { - val context = ServletContextHandler() - context.contextPath = "/" - server.handler = context - context.addServlet(ServletHolder(MetricsServlet()), "/$path") - addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + init { + addListener(genericServiceListener(this, logger), MoreExecutors.directExecutor()) + initBlock?.invoke(this) } - override fun startUp() { - server.start() - } + override fun startUp() = server.start() - override fun shutDown() { - server.stop() - } + override fun shutDown() = server.stop() override fun toString() = - MoreObjects.toStringHelper(this) - .add("url", "http://localhost:$port/$path") - .toString() + toStringElements { + add("url", "http://localhost:$port/$path") + } + + companion object { + private val logger = LoggerFactory.getLogger(MetricsService::class.java) + } } diff --git a/src/main/java/io/prometheus/common/SamplerGauge.kt b/src/main/java/io/prometheus/common/SamplerGaugeCollector.kt similarity index 55% rename from src/main/java/io/prometheus/common/SamplerGauge.kt rename to src/main/java/io/prometheus/common/SamplerGaugeCollector.kt index 641e69f2..2576ec11 100644 --- a/src/main/java/io/prometheus/common/SamplerGauge.kt +++ b/src/main/java/io/prometheus/common/SamplerGaugeCollector.kt @@ -18,18 +18,20 @@ package io.prometheus.common import io.prometheus.client.Collector -class SamplerGauge(private val name: String, - private val help: String, - private val samplerGaugeData: SamplerGaugeData) : Collector() { +class SamplerGaugeCollector(private val name: String, + private val help: String, + private val labelNames: List = emptyList(), + private val labelValues: List = emptyList(), + private val data: () -> Double) : Collector() { + init { + register() + } override fun collect(): List { val sample = MetricFamilySamples.Sample(name, - emptyList(), - emptyList(), - samplerGaugeData.value()) - return listOf(Collector.MetricFamilySamples(name, - Collector.Type.GAUGE, - help, - listOf(sample))) + labelNames, + labelValues, + data()) + return listOf(Collector.MetricFamilySamples(name, Collector.Type.GAUGE, help, listOf(sample))) } } diff --git a/src/main/java/io/prometheus/common/Utils.kt b/src/main/java/io/prometheus/common/Utils.kt index 66350dd6..1566bdcc 100644 --- a/src/main/java/io/prometheus/common/Utils.kt +++ b/src/main/java/io/prometheus/common/Utils.kt @@ -25,18 +25,14 @@ import com.google.common.base.Splitter import com.google.common.io.CharStreams import com.google.common.util.concurrent.Service import io.prometheus.Proxy +import io.prometheus.dsl.MetricsDsl.healthCheck import org.slf4j.Logger -import org.slf4j.LoggerFactory import java.io.InputStreamReader import java.net.InetAddress import java.net.UnknownHostException import java.util.* -object Utils { - val logger: Logger = LoggerFactory.getLogger(Utils::class.java) -} - -val hostName: String by lazy { +val localHostName: String by lazy { try { InetAddress.getLocalHost().hostName } catch (e: UnknownHostException) { @@ -44,9 +40,9 @@ val hostName: String by lazy { } } -fun getBanner(filename: String): String { +fun getBanner(filename: String, logger: Logger): String { try { - Utils.logger.javaClass.classLoader.getResourceAsStream(filename).use { + logger.javaClass.classLoader.getResourceAsStream(filename).use { val banner = CharStreams.toString(InputStreamReader(it, Charsets.UTF_8.name())) val lines: List = Splitter.on("\n").splitToList(banner) @@ -81,20 +77,20 @@ fun getBanner(filename: String): String { } } -fun queueHealthCheck(queue: Queue<*>, size: Int) = - object : HealthCheck() { - @Throws(Exception::class) - override fun check(): HealthCheck.Result { - return if (queue.size < size) HealthCheck.Result.healthy() else HealthCheck.Result.unhealthy("Large size: %d", queue.size) - } +fun newQueueHealthCheck(queue: Queue<*>, size: Int) = + healthCheck { + if (queue.size < size) + HealthCheck.Result.healthy() + else + HealthCheck.Result.unhealthy("Large size: %d", queue.size) } -fun mapHealthCheck(map: Map<*, *>, size: Int) = - object : HealthCheck() { - @Throws(Exception::class) - override fun check(): HealthCheck.Result { - return if (map.size < size) HealthCheck.Result.healthy() else HealthCheck.Result.unhealthy("Large size: %d", map.size) - } +fun newMapHealthCheck(map: Map<*, *>, size: Int) = + healthCheck { + if (map.size < size) + HealthCheck.Result.healthy() + else + HealthCheck.Result.unhealthy("Large size: %d", map.size) } fun sleepForMillis(millis: Long) = diff --git a/src/main/java/io/prometheus/common/ZipkinConfig.kt b/src/main/java/io/prometheus/common/ZipkinConfig.kt index 11984e31..8542a850 100644 --- a/src/main/java/io/prometheus/common/ZipkinConfig.kt +++ b/src/main/java/io/prometheus/common/ZipkinConfig.kt @@ -23,14 +23,14 @@ data class ZipkinConfig(val enabled: Boolean, val serviceName: String) { companion object { - fun create(zipkin: ConfigVals.Proxy2.Internal2.Zipkin2): ZipkinConfig = + fun newZipkinConfig(zipkin: ConfigVals.Proxy2.Internal2.Zipkin2): ZipkinConfig = ZipkinConfig(zipkin.enabled, zipkin.hostname, zipkin.port, zipkin.path, zipkin.serviceName) - fun create(zipkin: ConfigVals.Agent.Internal.Zipkin): ZipkinConfig = + fun newZipkinConfig(zipkin: ConfigVals.Agent.Internal.Zipkin): ZipkinConfig = ZipkinConfig(zipkin.enabled, zipkin.hostname, zipkin.port, diff --git a/src/main/java/io/prometheus/common/ZipkinReporterService.kt b/src/main/java/io/prometheus/common/ZipkinReporterService.kt index 947d3a8c..2dc32730 100644 --- a/src/main/java/io/prometheus/common/ZipkinReporterService.kt +++ b/src/main/java/io/prometheus/common/ZipkinReporterService.kt @@ -17,25 +17,29 @@ package io.prometheus.common import brave.Tracing -import com.google.common.base.MoreObjects -import com.google.common.util.concurrent.AbstractIdleService import com.google.common.util.concurrent.MoreExecutors +import io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.dsl.ZipkinDsl.tracing +import io.prometheus.guava.GenericIdleService +import io.prometheus.guava.genericServiceListener +import org.slf4j.LoggerFactory import zipkin2.reporter.AsyncReporter import zipkin2.reporter.okhttp3.OkHttpSender -class ZipkinReporterService(private val url: String) : AbstractIdleService() { +class ZipkinReporterService(private val url: String, protected val initBlock: (ZipkinReporterService.() -> Unit)? = null) : GenericIdleService() { private val sender = OkHttpSender.create(url) private val reporter = AsyncReporter.create(sender) init { - addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + addListener(genericServiceListener(this, logger), MoreExecutors.directExecutor()) + initBlock?.invoke(this) } fun newTracing(serviceName: String): Tracing = - Tracing.newBuilder() - .localServiceName(serviceName) - .spanReporter(reporter) - .build() + tracing { + localServiceName(serviceName) + spanReporter(reporter) + } override fun startUp() { // Empty @@ -46,8 +50,9 @@ class ZipkinReporterService(private val url: String) : AbstractIdleService() { sender.close() } - override fun toString() = - MoreObjects.toStringHelper(this) - .add("url", url) - .toString() + override fun toString() = toStringElements { add("url", url) } + + companion object { + private val logger = LoggerFactory.getLogger(ZipkinReporterService::class.java) + } } diff --git a/src/main/java/io/prometheus/delegate/AtomicDelegates.kt b/src/main/java/io/prometheus/delegate/AtomicDelegates.kt new file mode 100644 index 00000000..bff7e61e --- /dev/null +++ b/src/main/java/io/prometheus/delegate/AtomicDelegates.kt @@ -0,0 +1,163 @@ +package io.prometheus.delegate + +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.atomic.AtomicReference +import java.util.function.* +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +object AtomicDelegates { + fun notNullReference(initValue: T? = null): AtomicReferenceReadWriteProperty = NotNullAtomicReferenceDelegate(initValue) + fun nullableReference(initValue: T? = null): AtomicNullableReadWriteProperty = NullableAtomicReferenceDelegate(initValue) + fun boolean(initValue: Boolean = false): AtomicReadWriteProperty = AtomicBooleanDelegate(initValue) + fun integer(initValue: Int = 0): AtomicIntReadWriteProperty = AtomicIntDelegate(initValue) + fun long(initValue: Long = 0): AtomicLongReadWriteProperty = AtomicLongDelegate(initValue) +} + +interface AtomicReadWriteProperty : ReadWriteProperty { + fun lazySet(newValue: T) + fun getAndSet(newValue: T): T + fun compareAndSet(expect: T, update: T): Boolean + fun weakCompareAndSet(expect: T, update: T): Boolean +} + +interface AtomicReferenceReadWriteProperty : AtomicReadWriteProperty { + fun getAndUpdate(updateFunction: UnaryOperator): T + fun updateAndGet(updateFunction: UnaryOperator): T + fun getAndAccumulate(x: T, accumulatorFunction: BinaryOperator): T + fun accumulateAndGet(x: T, accumulatorFunction: BinaryOperator): T +} + +interface AtomicNullableReadWriteProperty { + operator fun getValue(thisRef: R, property: KProperty<*>): T? + operator fun setValue(thisRef: R, property: KProperty<*>, value: T) + + fun lazySet(newValue: T) + fun getAndSet(newValue: T): T + fun compareAndSet(expect: T, update: T): Boolean + fun weakCompareAndSet(expect: T, update: T): Boolean + + fun getAndUpdate(updateFunction: UnaryOperator): T + fun updateAndGet(updateFunction: UnaryOperator): T + fun getAndAccumulate(x: T, accumulatorFunction: BinaryOperator): T + fun accumulateAndGet(x: T, accumulatorFunction: BinaryOperator): T +} + +interface AtomicNumberReadWriteProperty : AtomicReadWriteProperty { + fun getAndIncrement(): T + fun getAndDecrement(): T + fun getAndAdd(delta: T): T + fun incrementAndGet(): T + fun decrementAndGet(): T + fun addAndGet(delta: T): T +} + +interface AtomicIntReadWriteProperty : AtomicNumberReadWriteProperty { + fun getAndUpdate(updateFunction: IntUnaryOperator): Int + fun updateAndGet(updateFunction: IntUnaryOperator): Int + fun getAndAccumulate(x: Int, accumulatorFunction: IntBinaryOperator): Int + fun accumulateAndGet(x: Int, accumulatorFunction: IntBinaryOperator): Int +} + +interface AtomicLongReadWriteProperty : AtomicNumberReadWriteProperty { + fun getAndUpdate(updateFunction: LongUnaryOperator): Long + fun updateAndGet(updateFunction: LongUnaryOperator): Long + fun getAndAccumulate(x: Long, accumulatorFunction: LongBinaryOperator): Long + fun accumulateAndGet(x: Long, accumulatorFunction: LongBinaryOperator): Long +} + +private class NotNullAtomicReferenceDelegate(initValue: T? = null) : AtomicReferenceReadWriteProperty { + private val atomicVal = AtomicReference(initValue) + + override operator fun getValue(thisRef: Any?, property: KProperty<*>) = atomicVal.get() ?: throw IllegalStateException("Property ${property.name} should be initialized before get.") + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = atomicVal.set(value) + + override fun lazySet(newValue: T) = atomicVal.lazySet(newValue) + override fun getAndSet(newValue: T): T = atomicVal.getAndSet(newValue) + override fun compareAndSet(expect: T, update: T) = atomicVal.compareAndSet(expect, update) + override fun weakCompareAndSet(expect: T, update: T) = atomicVal.weakCompareAndSet(expect, update) + + override fun getAndUpdate(updateFunction: UnaryOperator): T = atomicVal.getAndUpdate(updateFunction) + override fun updateAndGet(updateFunction: UnaryOperator): T = atomicVal.updateAndGet(updateFunction) + override fun getAndAccumulate(x: T, accumulatorFunction: BinaryOperator): T = atomicVal.getAndAccumulate(x, accumulatorFunction) + override fun accumulateAndGet(x: T, accumulatorFunction: BinaryOperator): T = atomicVal.accumulateAndGet(x, accumulatorFunction) +} + +private class NullableAtomicReferenceDelegate(initValue: T? = null) : AtomicNullableReadWriteProperty { + private val atomicVal = AtomicReference(initValue) + + override operator fun getValue(thisRef: Any?, property: KProperty<*>): T = atomicVal.get() + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = atomicVal.set(value) + + override fun lazySet(newValue: T) = atomicVal.lazySet(newValue) + override fun getAndSet(newValue: T): T = atomicVal.getAndSet(newValue) + override fun compareAndSet(expect: T, update: T) = atomicVal.compareAndSet(expect, update) + override fun weakCompareAndSet(expect: T, update: T) = atomicVal.weakCompareAndSet(expect, update) + + override fun getAndUpdate(updateFunction: UnaryOperator): T = atomicVal.getAndUpdate(updateFunction) + override fun updateAndGet(updateFunction: UnaryOperator): T = atomicVal.updateAndGet(updateFunction) + override fun getAndAccumulate(x: T, accumulatorFunction: BinaryOperator): T = atomicVal.getAndAccumulate(x, accumulatorFunction) + override fun accumulateAndGet(x: T, accumulatorFunction: BinaryOperator): T = atomicVal.accumulateAndGet(x, accumulatorFunction) +} + +private class AtomicBooleanDelegate(initValue: Boolean) : AtomicReadWriteProperty { + private val atomicVal = AtomicBoolean(initValue) + + override operator fun getValue(thisRef: Any?, property: KProperty<*>) = atomicVal.get() + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) = atomicVal.set(value) + + override fun lazySet(newValue: Boolean) = atomicVal.lazySet(newValue) + override fun getAndSet(newValue: Boolean) = atomicVal.getAndSet(newValue) + override fun compareAndSet(expect: Boolean, update: Boolean) = atomicVal.compareAndSet(expect, update) + override fun weakCompareAndSet(expect: Boolean, update: Boolean) = atomicVal.weakCompareAndSet(expect, update) +} + +private class AtomicIntDelegate(initValue: Int) : AtomicIntReadWriteProperty { + private val atomicVal = AtomicInteger(initValue) + + override operator fun getValue(thisRef: Any?, property: KProperty<*>) = atomicVal.get() + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) = atomicVal.set(value) + + override fun lazySet(newValue: Int) = atomicVal.lazySet(newValue) + override fun getAndSet(newValue: Int) = atomicVal.getAndSet(newValue) + override fun compareAndSet(expect: Int, update: Int) = atomicVal.compareAndSet(expect, update) + override fun weakCompareAndSet(expect: Int, update: Int) = atomicVal.weakCompareAndSet(expect, update) + + override fun getAndIncrement() = atomicVal.getAndIncrement() + override fun getAndDecrement() = atomicVal.getAndDecrement() + override fun getAndAdd(delta: Int) = atomicVal.getAndAdd(delta) + override fun incrementAndGet() = atomicVal.incrementAndGet() + override fun decrementAndGet() = atomicVal.decrementAndGet() + override fun addAndGet(delta: Int) = atomicVal.addAndGet(delta) + + override fun getAndUpdate(updateFunction: IntUnaryOperator) = atomicVal.getAndUpdate(updateFunction) + override fun updateAndGet(updateFunction: IntUnaryOperator) = atomicVal.updateAndGet(updateFunction) + override fun getAndAccumulate(x: Int, accumulatorFunction: IntBinaryOperator) = atomicVal.getAndAccumulate(x, accumulatorFunction) + override fun accumulateAndGet(x: Int, accumulatorFunction: IntBinaryOperator) = atomicVal.accumulateAndGet(x, accumulatorFunction) +} + +private class AtomicLongDelegate(initValue: Long) : AtomicLongReadWriteProperty { + private val atomicVal = AtomicLong(initValue) + + override operator fun getValue(thisRef: Any?, property: KProperty<*>): Long = atomicVal.get() + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) = atomicVal.set(value) + + override fun lazySet(newValue: Long) = atomicVal.lazySet(newValue) + override fun getAndSet(newValue: Long) = atomicVal.getAndSet(newValue) + override fun compareAndSet(expect: Long, update: Long) = atomicVal.compareAndSet(expect, update) + override fun weakCompareAndSet(expect: Long, update: Long) = atomicVal.weakCompareAndSet(expect, update) + + override fun getAndIncrement() = atomicVal.getAndIncrement() + override fun getAndDecrement() = atomicVal.getAndDecrement() + override fun getAndAdd(delta: Long) = atomicVal.getAndAdd(delta) + override fun incrementAndGet() = atomicVal.incrementAndGet() + override fun decrementAndGet() = atomicVal.decrementAndGet() + override fun addAndGet(delta: Long) = atomicVal.addAndGet(delta) + + override fun getAndUpdate(updateFunction: LongUnaryOperator) = atomicVal.getAndUpdate(updateFunction) + override fun updateAndGet(updateFunction: LongUnaryOperator) = atomicVal.updateAndGet(updateFunction) + override fun getAndAccumulate(x: Long, accumulatorFunction: LongBinaryOperator) = atomicVal.getAndAccumulate(x, accumulatorFunction) + override fun accumulateAndGet(x: Long, accumulatorFunction: LongBinaryOperator) = atomicVal.accumulateAndGet(x, accumulatorFunction) +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/delegate/DelegatesExtensions.kt b/src/main/java/io/prometheus/delegate/DelegatesExtensions.kt new file mode 100644 index 00000000..cbed820b --- /dev/null +++ b/src/main/java/io/prometheus/delegate/DelegatesExtensions.kt @@ -0,0 +1,29 @@ +package io.prometheus.delegate + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +object DelegatesExtensions { + + /** + * Returns a property delegate for a read/write property that can be assigned only once. Trying to assign the property + * a second time results in an exception. + */ + fun singleAssign(): ReadWriteProperty = SingleAssignVar() + + private class SingleAssignVar : ReadWriteProperty { + private var value: T? = null + + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return value + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + if (this.value != null) + throw IllegalStateException("Property ${property.name} cannot be assigned more than once.") + this.value = value + } + } +} + + diff --git a/src/main/java/io/prometheus/dsl/GrpcDsl.kt b/src/main/java/io/prometheus/dsl/GrpcDsl.kt new file mode 100644 index 00000000..cff4b119 --- /dev/null +++ b/src/main/java/io/prometheus/dsl/GrpcDsl.kt @@ -0,0 +1,81 @@ +package io.prometheus.dsl + +import io.grpc.Attributes +import io.grpc.ManagedChannel +import io.grpc.Server +import io.grpc.ServerBuilder +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.internal.AbstractManagedChannelImplBuilder +import io.grpc.netty.NettyChannelBuilder +import io.grpc.stub.StreamObserver +import io.prometheus.delegate.DelegatesExtensions.singleAssign + +object GrpcDsl { + fun channel(inProcessServerName: String = "", + hostName: String = "", + port: Int = -1, + block: AbstractManagedChannelImplBuilder<*>.() -> Unit): ManagedChannel { + return (if (inProcessServerName.isEmpty()) + NettyChannelBuilder.forAddress(hostName, port) + else + InProcessChannelBuilder.forName(inProcessServerName)) + .run { + block(this) + build() + } + } + + fun server(inProcessServerName: String = "", port: Int = -1, block: ServerBuilder<*>.() -> Unit): Server { + return (if (inProcessServerName.isEmpty()) + ServerBuilder.forPort(port) + else + InProcessServerBuilder.forName(inProcessServerName)) + .run { + block(this) + build() + } + } + + fun attributes(block: Attributes.Builder.() -> Unit): Attributes { + return Attributes.newBuilder() + .run { + block(this) + build() + } + } + + fun streamObserver(init: StreamObserverHelper.() -> Unit): StreamObserver { + return StreamObserverHelper().apply { init() } + } + + class StreamObserverHelper : StreamObserver { + private var onNextBlock: ((T) -> Unit)? by singleAssign() + private var onErrorBlock: ((Throwable) -> Unit)? by singleAssign() + private var completedBlock: (() -> Unit)? by singleAssign() + + override fun onNext(response: T) { + onNextBlock?.invoke(response) + } + + override fun onError(t: Throwable) { + onErrorBlock?.invoke(t) + } + + override fun onCompleted() { + completedBlock?.invoke() + } + + fun onNext(block: (T) -> Unit) { + onNextBlock = block + } + + fun onError(block: (Throwable) -> Unit) { + onErrorBlock = block + } + + fun onCompleted(block: () -> Unit) { + completedBlock = block + } + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/dsl/GuavaDsl.kt b/src/main/java/io/prometheus/dsl/GuavaDsl.kt new file mode 100644 index 00000000..3eb30f80 --- /dev/null +++ b/src/main/java/io/prometheus/dsl/GuavaDsl.kt @@ -0,0 +1,115 @@ +package io.prometheus.dsl + +import com.google.common.base.MoreObjects +import com.google.common.util.concurrent.Service +import com.google.common.util.concurrent.ServiceManager +import io.prometheus.delegate.DelegatesExtensions.singleAssign + +object GuavaDsl { + fun Any.toStringElements(block: MoreObjects.ToStringHelper.() -> Unit): String { + return MoreObjects.toStringHelper(this) + .run { + block(this) + toString() + } + } + + fun serviceManager(services: List, block: ServiceManager.() -> Unit): ServiceManager { + return ServiceManager(services).apply { block.invoke(this) } + } + + fun serviceManagerListener(init: ServiceManagerListenerHelper.() -> Unit): ServiceManager.Listener { + return ServiceManagerListenerHelper().apply { init() } + } + + class ServiceManagerListenerHelper : ServiceManager.Listener() { + private var healthyBlock: (() -> Unit)? by singleAssign() + private var stoppedBlock: (() -> Unit)? by singleAssign() + private var failureBlock: ((Service) -> Unit)? by singleAssign() + + override fun healthy() { + super.healthy() + healthyBlock?.invoke() + } + + override fun stopped() { + super.stopped() + stoppedBlock?.invoke() + } + + override fun failure(service: Service) { + super.failure(service) + failureBlock?.invoke(service) + } + + fun healthy(block: () -> Unit) { + healthyBlock = block + } + + fun stopped(block: () -> Unit) { + stoppedBlock = block + } + + fun failure(block: (Service) -> Unit) { + failureBlock = block + } + } + + fun serviceListener(init: ServiceListenerHelper.() -> Unit): Service.Listener { + return ServiceListenerHelper().apply { init() } + } + + class ServiceListenerHelper : Service.Listener() { + private var startingBlock: (() -> Unit)? by singleAssign() + private var runningBlock: (() -> Unit)? by singleAssign() + private var stoppingBlock: ((Service.State) -> Unit)? by singleAssign() + private var terminatedBlock: ((Service.State) -> Unit)? by singleAssign() + private var failedBlock: ((Service.State, Throwable) -> Unit)? by singleAssign() + + override fun starting() { + super.starting() + startingBlock?.invoke() + } + + override fun running() { + super.running() + runningBlock?.invoke() + } + + override fun stopping(from: Service.State) { + super.stopping(from) + stoppingBlock?.invoke(from) + } + + override fun terminated(from: Service.State) { + super.terminated(from) + terminatedBlock?.invoke(from) + } + + override fun failed(from: Service.State, failure: Throwable) { + super.failed(from, failure) + failedBlock?.invoke(from, failure) + } + + fun starting(block: (() -> Unit)?) { + startingBlock = block + } + + fun running(block: () -> Unit) { + runningBlock = block + } + + fun stopping(block: (Service.State) -> Unit) { + stoppingBlock = block + } + + fun terminated(block: (Service.State) -> Unit) { + terminatedBlock = block + } + + fun failed(block: (Service.State, Throwable) -> Unit) { + failedBlock = block + } + } +} + diff --git a/src/main/java/io/prometheus/dsl/JettyDsl.kt b/src/main/java/io/prometheus/dsl/JettyDsl.kt new file mode 100644 index 00000000..7d86747c --- /dev/null +++ b/src/main/java/io/prometheus/dsl/JettyDsl.kt @@ -0,0 +1,14 @@ +package io.prometheus.dsl + +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.servlet.ServletContextHandler + +object JettyDsl { + fun server(port: Int, block: Server.() -> Unit): Server { + return Server(port).apply { block(this) } + } + + fun servletContextHandler(block: ServletContextHandler.() -> Unit): ServletContextHandler { + return ServletContextHandler().apply { block(this) } + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/dsl/MetricsDsl.kt b/src/main/java/io/prometheus/dsl/MetricsDsl.kt new file mode 100644 index 00000000..32d37913 --- /dev/null +++ b/src/main/java/io/prometheus/dsl/MetricsDsl.kt @@ -0,0 +1,41 @@ +package io.prometheus.dsl + +import com.codahale.metrics.health.HealthCheck +import io.prometheus.client.Counter +import io.prometheus.client.Gauge +import io.prometheus.client.Summary + +object MetricsDsl { + fun counter(builder: Counter.Builder.() -> Unit): Counter { + return Counter.build() + .run { + builder(this) + register() + } + } + + fun summary(builder: Summary.Builder.() -> Unit): Summary { + return Summary.build() + .run { + builder(this) + register() + } + } + + fun gauge(builder: Gauge.Builder.() -> Unit): Gauge { + return Gauge.build() + .run { + builder(this) + register() + } + } + + fun healthCheck(block: HealthCheck.() -> HealthCheck.Result): HealthCheck { + return object : HealthCheck() { + @Throws(Exception::class) + override fun check(): Result { + return block.invoke(this) + } + } + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/dsl/OkHttpDsl.kt b/src/main/java/io/prometheus/dsl/OkHttpDsl.kt new file mode 100644 index 00000000..b1b3e8bb --- /dev/null +++ b/src/main/java/io/prometheus/dsl/OkHttpDsl.kt @@ -0,0 +1,16 @@ +package io.prometheus.dsl + +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response + +object OkHttpDsl { + val OK_HTTP_CLIENT = OkHttpClient() + + inline fun String.get(block: (Response) -> Unit) { + OK_HTTP_CLIENT + .newCall(Request.Builder().url(this).build()) + .execute() + .use(block) + } +} diff --git a/src/main/java/io/prometheus/dsl/SparkDsl.kt b/src/main/java/io/prometheus/dsl/SparkDsl.kt new file mode 100644 index 00000000..77200b75 --- /dev/null +++ b/src/main/java/io/prometheus/dsl/SparkDsl.kt @@ -0,0 +1,14 @@ +package io.prometheus.dsl + +import org.eclipse.jetty.servlet.ServletContextHandler +import spark.Service + +object SparkDsl { + inline fun httpServer(block: Service.() -> Unit): Service { + return Service.ignite().apply { block.invoke(this) } + } + + inline fun servletContextHandler(block: ServletContextHandler.() -> Unit): ServletContextHandler { + return ServletContextHandler().apply { block.invoke(this) } + } +} diff --git a/src/main/java/io/prometheus/dsl/ThreadDsl.kt b/src/main/java/io/prometheus/dsl/ThreadDsl.kt new file mode 100644 index 00000000..1c6b4710 --- /dev/null +++ b/src/main/java/io/prometheus/dsl/ThreadDsl.kt @@ -0,0 +1,14 @@ +package io.prometheus.dsl + +import com.google.common.util.concurrent.ThreadFactoryBuilder +import java.util.concurrent.ThreadFactory + +object ThreadDsl { + fun threadFactory(block: ThreadFactoryBuilder.() -> Unit): ThreadFactory { + return ThreadFactoryBuilder() + .run { + block(this) + build() + } + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/dsl/ZipkinDsl.kt b/src/main/java/io/prometheus/dsl/ZipkinDsl.kt new file mode 100644 index 00000000..c6d0eb72 --- /dev/null +++ b/src/main/java/io/prometheus/dsl/ZipkinDsl.kt @@ -0,0 +1,13 @@ +package io.prometheus.dsl + +import brave.Tracing + +object ZipkinDsl { + fun tracing(block: Tracing.Builder.() -> Unit): Tracing { + return Tracing.newBuilder() + .run { + block(this) + build() + } + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/guava/GenericExecutionThreadService.kt b/src/main/java/io/prometheus/guava/GenericExecutionThreadService.kt new file mode 100644 index 00000000..208f27ad --- /dev/null +++ b/src/main/java/io/prometheus/guava/GenericExecutionThreadService.kt @@ -0,0 +1,17 @@ +package io.prometheus.guava + +import com.google.common.util.concurrent.AbstractExecutionThreadService +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeUnit.SECONDS + +abstract class GenericExecutionThreadService : AbstractExecutionThreadService() { + fun startSync(maxWaitSecs: Long = 15, timeUnit: TimeUnit = SECONDS) { + startAsync() + awaitRunning(maxWaitSecs, timeUnit) + } + + fun stopSync(maxWaitSecs: Long = 15, timeUnit: TimeUnit = SECONDS) { + stopAsync() + awaitTerminated(maxWaitSecs, timeUnit) + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/guava/GenericIdleService.kt b/src/main/java/io/prometheus/guava/GenericIdleService.kt new file mode 100644 index 00000000..a981fd5f --- /dev/null +++ b/src/main/java/io/prometheus/guava/GenericIdleService.kt @@ -0,0 +1,17 @@ +package io.prometheus.guava + +import com.google.common.util.concurrent.AbstractIdleService +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeUnit.SECONDS + +abstract class GenericIdleService : AbstractIdleService() { + fun startSync(maxWaitSecs: Long = 15, timeUnit: TimeUnit = SECONDS) { + startAsync() + awaitRunning(maxWaitSecs, timeUnit) + } + + fun stopSync(maxWaitSecs: Long = 15, timeUnit: TimeUnit = SECONDS) { + stopAsync() + awaitTerminated(maxWaitSecs, timeUnit) + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/common/SamplerGaugeData.java b/src/main/java/io/prometheus/guava/GenericServiceListener.kt similarity index 53% rename from src/main/java/io/prometheus/common/SamplerGaugeData.java rename to src/main/java/io/prometheus/guava/GenericServiceListener.kt index 637950f1..efc6c16a 100644 --- a/src/main/java/io/prometheus/common/SamplerGaugeData.java +++ b/src/main/java/io/prometheus/guava/GenericServiceListener.kt @@ -14,9 +14,18 @@ * limitations under the License. */ -package io.prometheus.common; +package io.prometheus.guava -@FunctionalInterface -public interface SamplerGaugeData { - double value(); +import com.google.common.util.concurrent.Service +import io.prometheus.dsl.GuavaDsl.serviceListener +import org.slf4j.Logger + +fun genericServiceListener(service: Service, logger: Logger): Service.Listener { + return serviceListener { + starting { logger.info("Starting $service") } + running { logger.info("Running $service") } + stopping { logger.info("Stopping $service") } + terminated { logger.info("Terminated $service") } + failed { from, t -> logger.info("Failed on $from $service", t) } + } } diff --git a/src/main/java/io/prometheus/package-info.java b/src/main/java/io/prometheus/package-info.java index f66686a4..01555919 100644 --- a/src/main/java/io/prometheus/package-info.java +++ b/src/main/java/io/prometheus/package-info.java @@ -14,7 +14,7 @@ * limitations under the License. */ -@VersionAnnotation(version = "1.3.1", date = "12/29/17") +@VersionAnnotation(version = "1.3.2", date = "1/3/18") package io.prometheus; import io.prometheus.common.VersionAnnotation; \ No newline at end of file diff --git a/src/main/java/io/prometheus/proxy/AgentContext.kt b/src/main/java/io/prometheus/proxy/AgentContext.kt index ba4bc638..59d68c7c 100644 --- a/src/main/java/io/prometheus/proxy/AgentContext.kt +++ b/src/main/java/io/prometheus/proxy/AgentContext.kt @@ -16,10 +16,10 @@ package io.prometheus.proxy -import com.google.common.base.MoreObjects import io.prometheus.Proxy -import io.prometheus.common.AtomicDelegates import io.prometheus.common.toSecs +import io.prometheus.delegate.AtomicDelegates +import io.prometheus.dsl.GuavaDsl.toStringElements import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicLong @@ -31,7 +31,7 @@ class AgentContext(proxy: Proxy, private val remoteAddr: String) { private val waitMillis = proxy.configVals.internal.scrapeRequestQueueCheckMillis.toLong() private var lastActivityTime: Long by AtomicDelegates.long() - var valid: Boolean by AtomicDelegates.boolean(true) + var isValid: Boolean by AtomicDelegates.boolean(true) var hostName: String by AtomicDelegates.notNullReference() var agentName: String by AtomicDelegates.notNullReference() @@ -57,7 +57,7 @@ class AgentContext(proxy: Proxy, private val remoteAddr: String) { } fun markInvalid() { - valid = false + isValid = false } fun markActivity() { @@ -65,14 +65,14 @@ class AgentContext(proxy: Proxy, private val remoteAddr: String) { } override fun toString() = - MoreObjects.toStringHelper(this) - .add("agentId", agentId) - .add("valid", valid) - .add("remoteAddr", remoteAddr) - .add("agentName", agentName) - .add("hostName", hostName) - .add("inactivitySecs", inactivitySecs) - .toString() + toStringElements { + add("agentId", agentId) + add("valid", isValid) + add("remoteAddr", remoteAddr) + add("agentName", agentName) + add("hostName", hostName) + add("inactivitySecs", inactivitySecs) + } companion object { private val AGENT_ID_GENERATOR = AtomicLong(0) diff --git a/src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt b/src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt index 298f183b..f9ddf8da 100644 --- a/src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt +++ b/src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt @@ -16,18 +16,19 @@ 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.sleepForSecs +import io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.guava.GenericExecutionThreadService +import io.prometheus.guava.genericServiceListener import org.slf4j.LoggerFactory -class AgentContextCleanupService(private val proxy: Proxy) : AbstractExecutionThreadService() { +class AgentContextCleanupService(private val proxy: Proxy, initBlock: (AgentContextCleanupService.() -> Unit)? = null) : GenericExecutionThreadService() { init { - addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + addListener(genericServiceListener(this, logger), MoreExecutors.directExecutor()) + initBlock?.invoke(this) } @Throws(Exception::class) @@ -41,8 +42,8 @@ class AgentContextCleanupService(private val proxy: Proxy) : AbstractExecutionTh if (inactivitySecs > maxInactivitySecs) { logger.info("Evicting agent after $inactivitySecs secs of inactivty $agentContext") proxy.removeAgentContext(agentId) - if (proxy.metricsEnabled) - proxy.metrics!!.agentEvictions.inc() + if (proxy.isMetricsEnabled) + proxy.metrics.agentEvictions.inc() } } sleepForSecs(threadPauseSecs) @@ -50,10 +51,10 @@ class AgentContextCleanupService(private val proxy: Proxy) : AbstractExecutionTh } override fun toString() = - MoreObjects.toStringHelper(this) - .add("max inactivity secs", proxy.configVals.internal.maxAgentInactivitySecs) - .add("pause secs", proxy.configVals.internal.staleAgentCheckPauseSecs) - .toString() + toStringElements { + add("max inactivity secs", proxy.configVals.internal.maxAgentInactivitySecs) + add("pause secs", proxy.configVals.internal.staleAgentCheckPauseSecs) + } companion object { private val logger = LoggerFactory.getLogger(AgentContextCleanupService::class.java) diff --git a/src/main/java/io/prometheus/proxy/ProxyGrpcService.kt b/src/main/java/io/prometheus/proxy/ProxyGrpcService.kt index 0b40db4d..95fecd6f 100644 --- a/src/main/java/io/prometheus/proxy/ProxyGrpcService.kt +++ b/src/main/java/io/prometheus/proxy/ProxyGrpcService.kt @@ -19,67 +19,55 @@ package io.prometheus.proxy import brave.Tracing import brave.grpc.GrpcTracing 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 com.salesforce.grpc.contrib.Servers import io.grpc.Server -import io.grpc.ServerBuilder import io.grpc.ServerInterceptor import io.grpc.ServerInterceptors -import io.grpc.inprocess.InProcessServerBuilder import io.prometheus.Proxy -import io.prometheus.common.GenericServiceListener +import io.prometheus.dsl.GrpcDsl.server +import io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.dsl.MetricsDsl.healthCheck +import io.prometheus.guava.GenericIdleService +import io.prometheus.guava.genericServiceListener import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException +import kotlin.properties.Delegates -class ProxyGrpcService private constructor(proxy: Proxy, +class ProxyGrpcService private constructor(private val proxy: Proxy, private val port: Int = -1, - private val inProcessServerName: String = "") : AbstractIdleService() { - val healthCheck: HealthCheck - get() = object : HealthCheck() { - @Throws(Exception::class) - override fun check(): HealthCheck.Result { - return if (grpcServer.isShutdown || grpcServer.isShutdown) + private val inProcessServerName: String = "") : GenericIdleService() { + val healthCheck = + healthCheck { + if (grpcServer.isShutdown || grpcServer.isShutdown) HealthCheck.Result.unhealthy("gRPC server is not running") else HealthCheck.Result.healthy() } - } + private var tracing: Tracing by Delegates.notNull() + private var grpcTracing: GrpcTracing by Delegates.notNull() private val grpcServer: Server - private val tracing: Tracing? - private val grpcTracing: GrpcTracing? init { - if (proxy.zipkinEnabled) { - tracing = proxy.zipkinReporterService!!.newTracing("grpc_server") + if (proxy.isZipkinEnabled) { + tracing = proxy.zipkinReporterService.newTracing("grpc_server") grpcTracing = GrpcTracing.create(tracing) } - else { - tracing = null - grpcTracing = null - } - - val serverBuilder = - if (inProcessServerName.isNotEmpty()) - InProcessServerBuilder.forName(inProcessServerName) - else - ServerBuilder.forPort(port) - - val proxyService = ProxyServiceImpl(proxy) - val interceptors = mutableListOf(ProxyInterceptor()) - if (proxy.zipkinEnabled) - interceptors.add(grpcTracing!!.newServerInterceptor()) - val serviceDef = ServerInterceptors.intercept(proxyService.bindService(), interceptors) grpcServer = - serverBuilder - .addService(serviceDef) - .addTransportFilter(ProxyTransportFilter(proxy)) - .build() + server(inProcessServerName = inProcessServerName, port = port) { + val proxyService = ProxyServiceImpl(proxy) + val interceptors = mutableListOf(ProxyInterceptor()) + if (proxy.isZipkinEnabled) + interceptors.add(grpcTracing.newServerInterceptor()) + addService(ServerInterceptors.intercept(proxyService.bindService(), interceptors)) + addTransportFilter(ProxyTransportFilter(proxy)) + } + Servers.shutdownWithJvm(grpcServer, 2000) - addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + addListener(genericServiceListener(this, logger), MoreExecutors.directExecutor()) } @Throws(IOException::class) @@ -88,12 +76,13 @@ class ProxyGrpcService private constructor(proxy: Proxy, } override fun shutDown() { - tracing?.close() - grpcServer.shutdown() + if (proxy.isZipkinEnabled) + tracing.close() + Servers.shutdownGracefully(grpcServer, 2000) } override fun toString() = - with(MoreObjects.toStringHelper(this)) { + toStringElements { if (inProcessServerName.isNotEmpty()) { add("serverType", "InProcess") add("serverName", inProcessServerName) @@ -102,14 +91,12 @@ class ProxyGrpcService private constructor(proxy: Proxy, add("serverType", "Netty") add("port", port) } - toString() } companion object { val logger: Logger = LoggerFactory.getLogger(ProxyGrpcService::class.java) - fun create(proxy: Proxy, grpcPort: Int) = ProxyGrpcService(proxy = proxy, port = grpcPort) - - fun create(proxy: Proxy, serverName: String) = ProxyGrpcService(proxy = proxy, inProcessServerName = serverName) + fun newProxyGrpcService(proxy: Proxy, port: Int) = ProxyGrpcService(proxy = proxy, port = port) + fun newProxyGrpcService(proxy: Proxy, serverName: String) = ProxyGrpcService(proxy = proxy, inProcessServerName = serverName) } } diff --git a/src/main/java/io/prometheus/proxy/ProxyHttpService.kt b/src/main/java/io/prometheus/proxy/ProxyHttpService.kt index bbf38558..aaddd6c1 100644 --- a/src/main/java/io/prometheus/proxy/ProxyHttpService.kt +++ b/src/main/java/io/prometheus/proxy/ProxyHttpService.kt @@ -16,33 +16,44 @@ package io.prometheus.proxy +import brave.Tracing import brave.sparkjava.SparkTracing -import com.google.common.base.MoreObjects import com.google.common.net.HttpHeaders.* -import com.google.common.util.concurrent.AbstractIdleService import com.google.common.util.concurrent.MoreExecutors import io.prometheus.Proxy -import io.prometheus.common.GenericServiceListener +import io.prometheus.common.sleepForSecs +import io.prometheus.dsl.GuavaDsl.toStringElements +import io.prometheus.dsl.SparkDsl.httpServer +import io.prometheus.guava.GenericIdleService +import io.prometheus.guava.genericServiceListener import org.slf4j.LoggerFactory -import spark.* - -class ProxyHttpService(private val proxy: Proxy, val port: Int) : AbstractIdleService() { +import spark.Request +import spark.Response +import spark.Route +import spark.Spark +import java.net.BindException +import kotlin.properties.Delegates + +class ProxyHttpService(private val proxy: Proxy, val port: Int) : GenericIdleService() { private val configVals = proxy.configVals - private val tracing = proxy.zipkinReporterService?.newTracing("proxy-http") - private val http: Service = - Service.ignite().apply { + private var tracing: Tracing by Delegates.notNull() + private val httpServer = + httpServer { + initExceptionHandler { e -> sparkExceptionHandler(e, port) } port(port) - threadPool(proxy.configVals.http.maxThreads, - proxy.configVals.http.minThreads, - proxy.configVals.http.idleTimeoutMillis) + threadPool(configVals.http.maxThreads, + configVals.http.minThreads, + configVals.http.idleTimeoutMillis) } init { - addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + if (proxy.isZipkinEnabled) + tracing = proxy.zipkinReporterService.newTracing("proxy-http") + addListener(genericServiceListener(this, logger), MoreExecutors.directExecutor()) } override fun startUp() { - if (proxy.zipkinEnabled) { + if (proxy.isZipkinEnabled) { val sparkTracing = SparkTracing.create(tracing) Spark.before(sparkTracing.before()) Spark.exception(Exception::class.java, @@ -50,57 +61,65 @@ class ProxyHttpService(private val proxy: Proxy, val port: Int) : AbstractIdleSe Spark.afterAfter(sparkTracing.afterAfter()) } - http.get("/*", - Route { req, res -> - res.header("cache-control", "must-revalidate,no-cache,no-store") - - if (!proxy.isRunning) { - logger.error("Proxy stopped") - res.status(503) - updateScrapeRequests("proxy_stopped") - return@Route null - } - - val vals = req.splat() - - if (vals == null || vals.isEmpty()) { - logger.info("Request missing path") - res.status(404) - updateScrapeRequests("missing_path") - return@Route null - } - - val path = vals[0] - - if (configVals.internal.blitz.enabled && path == configVals.internal.blitz.path) { - res.status(200) - res.type("text/plain") - return@Route "42" - } - - val agentContext = proxy.getAgentContextByPath(path) - - if (agentContext == null) { - logger.debug("Invalid path request /\${path") - res.status(404) - updateScrapeRequests("invalid_path") - return@Route null - } - - if (!agentContext.valid) { - logger.error("Invalid AgentContext") - res.status(404) - updateScrapeRequests("invalid_agent_context") - return@Route null - } - - return@Route submitScrapeRequest(req, res, agentContext, path) - }) + httpServer + .apply { + get("/*", + Route { req, res -> + res.header("cache-control", "must-revalidate,no-cache,no-store") + + if (!proxy.isRunning) { + logger.error("Proxy stopped") + res.status(503) + updateScrapeRequests("proxy_stopped") + return@Route null + } + + val vals = req.splat() + + if (vals == null || vals.isEmpty()) { + logger.info("Request missing path") + res.status(404) + updateScrapeRequests("missing_path") + return@Route null + } + + val path = vals[0] + + if (configVals.internal.blitz.enabled && path == configVals.internal.blitz.path) { + res.status(200) + res.type("text/plain") + return@Route "42" + } + + val agentContext = proxy.getAgentContextByPath(path) + + if (agentContext == null) { + logger.debug("Invalid path request /\${path") + res.status(404) + updateScrapeRequests("invalid_path") + return@Route null + } + + if (!agentContext.isValid) { + logger.error("Invalid AgentContext") + res.status(404) + updateScrapeRequests("invalid_agent_context") + return@Route null + } + + return@Route submitScrapeRequest(req, res, agentContext, path) + }) + awaitInitialization() + } + } override fun shutDown() { - tracing?.close() - http.stop() + if (proxy.isZipkinEnabled) + tracing.close() + + httpServer.stop() + sleepForSecs(3) } private fun submitScrapeRequest(req: Request, @@ -120,16 +139,14 @@ class ProxyHttpService(private val proxy: Proxy, val port: Int) : AbstractIdleSe break // Check if agent is disconnected or agent is hung - if (scrapeRequest.ageInSecs() >= timeoutSecs || !scrapeRequest.agentContext.valid || !proxy.isRunning) { + if (scrapeRequest.ageInSecs() >= timeoutSecs || !scrapeRequest.agentContext.isValid || !proxy.isRunning) { res.status(503) updateScrapeRequests("time_out") return null } } } finally { - val prev = proxy.removeFromScrapeRequestMap(scrapeRequest.scrapeId) - if (prev == null) - logger.error("Scrape request ${scrapeRequest.scrapeId} missing in map") + proxy.removeFromScrapeRequestMap(scrapeRequest.scrapeId) ?: logger.error("Scrape request ${scrapeRequest.scrapeId} missing in map") } logger.debug("Results returned from $agentContext for $scrapeRequest") @@ -154,16 +171,22 @@ class ProxyHttpService(private val proxy: Proxy, val port: Int) : AbstractIdleSe } private fun updateScrapeRequests(type: String) { - if (proxy.metricsEnabled) - proxy.metrics!!.scrapeRequests.labels(type).inc() + if (proxy.isMetricsEnabled) + proxy.metrics.scrapeRequests.labels(type).inc() } - override fun toString() = - MoreObjects.toStringHelper(this) - .add("port", port) - .toString() + override fun toString() = toStringElements { add("port", port) } companion object { private val logger = LoggerFactory.getLogger(ProxyHttpService::class.java) + + fun sparkExceptionHandler(e: Exception, port: Int) { + if (e is BindException) + logger.error("ignite failed to bind to port $port", e) + else + logger.error("ignite failed", e) + System.exit(100) + } + } } diff --git a/src/main/java/io/prometheus/proxy/ProxyInterceptor.kt b/src/main/java/io/prometheus/proxy/ProxyInterceptor.kt index f132ea6e..f7f5e7ee 100644 --- a/src/main/java/io/prometheus/proxy/ProxyInterceptor.kt +++ b/src/main/java/io/prometheus/proxy/ProxyInterceptor.kt @@ -33,9 +33,7 @@ class ProxyInterceptor : ServerInterceptor { object : ForwardingServerCall.SimpleForwardingServerCall(call) { override fun sendHeaders(headers: Metadata) { // agent_id was assigned in ServerTransportFilter - val agentId = attributes.get(Proxy.ATTRIB_AGENT_ID) - if (agentId != null) - headers.put(META_AGENT_ID, agentId) + attributes.get(Proxy.ATTRIB_AGENT_ID)?.let { headers.put(META_AGENT_ID, it) } super.sendHeaders(headers) } }, diff --git a/src/main/java/io/prometheus/proxy/ProxyMetrics.kt b/src/main/java/io/prometheus/proxy/ProxyMetrics.kt index bfa595b2..568ca572 100644 --- a/src/main/java/io/prometheus/proxy/ProxyMetrics.kt +++ b/src/main/java/io/prometheus/proxy/ProxyMetrics.kt @@ -17,67 +17,66 @@ package io.prometheus.proxy import io.prometheus.Proxy -import io.prometheus.client.Collector import io.prometheus.client.Counter -import io.prometheus.client.Gauge import io.prometheus.client.Summary -import io.prometheus.common.SamplerGauge -import io.prometheus.common.SamplerGaugeData +import io.prometheus.common.SamplerGaugeCollector +import io.prometheus.dsl.MetricsDsl.counter +import io.prometheus.dsl.MetricsDsl.gauge +import io.prometheus.dsl.MetricsDsl.summary class ProxyMetrics(proxy: Proxy) { val scrapeRequests: Counter = - Counter.build() - .name("proxy_scrape_requests") - .help("Proxy scrape requests") - .labelNames("type") - .register() + counter { + name("proxy_scrape_requests") + help("Proxy scrape requests") + labelNames("type") + } val connects: Counter = - Counter.build() - .name("proxy_connect_count") - .help("Proxy connect count") - .register() + counter { + name("proxy_connect_count") + help("Proxy connect count") + } val agentEvictions: Counter = - Counter.build() - .name("proxy_eviction_count") - .help("Proxy eviction count") - .register() + counter { + name("proxy_eviction_count") + help("Proxy eviction count") + } val heartbeats: Counter = - Counter.build() - .name("proxy_heartbeat_count") - .help("Proxy heartbeat count") - .register() + counter { + name("proxy_heartbeat_count") + help("Proxy heartbeat count") + } val scrapeRequestLatency: Summary = - Summary.build() - .name("proxy_scrape_request_latency_seconds") - .help("Proxy scrape request latency in seconds") - .register() + summary { + name("proxy_scrape_request_latency_seconds") + help("Proxy scrape request latency in seconds") + } init { - Gauge.build() - .name("proxy_start_time_seconds") - .help("Proxy start time in seconds") - .register() - .setToCurrentTime() + gauge { + name("proxy_start_time_seconds") + help("Proxy start time in seconds") + }.setToCurrentTime() - SamplerGauge("proxy_agent_map_size", - "Proxy connected agents", - SamplerGaugeData { proxy.agentContextSize.toDouble() }).register() + SamplerGaugeCollector(name = "proxy_agent_map_size", + help = "Proxy connected agents", + data = { proxy.agentContextSize.toDouble() }) - SamplerGauge("proxy_path_map_size", - "Proxy path map size", - SamplerGaugeData { proxy.pathMapSize.toDouble() }).register() + SamplerGaugeCollector(name = "proxy_path_map_size", + help = "Proxy path map size", + data = { proxy.pathMapSize.toDouble() }) - SamplerGauge("proxy_scrape_map_size", - "Proxy scrape map size", - SamplerGaugeData { proxy.scrapeMapSize.toDouble() }).register() + SamplerGaugeCollector(name = "proxy_scrape_map_size", + help = "Proxy scrape map size", + data = { proxy.scrapeMapSize.toDouble() }) - SamplerGauge("proxy_cummulative_agent_queue_size", - "Proxy cummulative agent queue size", - SamplerGaugeData { proxy.totalAgentRequestQueueSize.toDouble() }).register() + SamplerGaugeCollector(name = "proxy_cummulative_agent_queue_size", + help = "Proxy cummulative agent queue size", + data = { proxy.totalAgentRequestQueueSize.toDouble() }) } } diff --git a/src/main/java/io/prometheus/proxy/ProxyOptions.kt b/src/main/java/io/prometheus/proxy/ProxyOptions.kt index 5f8f9a1f..44cae823 100644 --- a/src/main/java/io/prometheus/proxy/ProxyOptions.kt +++ b/src/main/java/io/prometheus/proxy/ProxyOptions.kt @@ -22,15 +22,15 @@ import io.prometheus.Proxy import io.prometheus.common.BaseOptions import io.prometheus.common.EnvVars.* -class ProxyOptions(argv: Array) : BaseOptions(Proxy::class.java.simpleName, argv, PROXY_CONFIG.name, false) { +class ProxyOptions(argv: Array) : BaseOptions(Proxy::class.java.simpleName, argv, PROXY_CONFIG.name) { constructor(args: List) : this(Iterables.toArray(args, String::class.java)) @Parameter(names = ["-p", "--port"], description = "Listen port for Prometheus") - var proxyPort: Int? = null + var proxyPort: Int = -1 private set @Parameter(names = ["-a", "--agent_port"], description = "Listen port for agents") - var agentPort: Int? = null + var agentPort: Int = -1 private set init { @@ -38,10 +38,10 @@ class ProxyOptions(argv: Array) : BaseOptions(Proxy::class.java.simpleNa } override fun assignConfigVals() { - if (proxyPort == null) + if (proxyPort == -1) proxyPort = PROXY_PORT.getEnv(configVals.proxy.http.port) - if (agentPort == null) + if (agentPort == -1) agentPort = AGENT_PORT.getEnv(configVals.proxy.agent.port) assignAdminEnabled(configVals.proxy.admin.enabled) diff --git a/src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt b/src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt index d8166714..6757e85d 100644 --- a/src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt +++ b/src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt @@ -21,6 +21,7 @@ import io.grpc.Status import io.grpc.StatusRuntimeException import io.grpc.stub.StreamObserver import io.prometheus.Proxy +import io.prometheus.dsl.GrpcDsl.streamObserver import io.prometheus.grpc.* import org.slf4j.LoggerFactory import java.util.concurrent.atomic.AtomicLong @@ -28,37 +29,39 @@ import java.util.concurrent.atomic.AtomicLong internal class ProxyServiceImpl(private val proxy: Proxy) : ProxyServiceGrpc.ProxyServiceImplBase() { override fun connectAgent(request: Empty, responseObserver: StreamObserver) { - proxy.metrics?.connects?.inc() - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() - } + if (proxy.isMetricsEnabled) + proxy.metrics.connects.inc() + responseObserver + .apply { + onNext(Empty.getDefaultInstance()) + onCompleted() + } } override fun registerAgent(request: RegisterAgentRequest, responseObserver: StreamObserver) { val agentId = request.agentId - val agentContext = proxy.getAgentContext(agentId) - if (agentContext == null) - logger.info("registerAgent() missing AgentContext agentId: $agentId") - else - agentContext.apply { - agentName = request.agentName - hostName = request.hostName - markActivity() - } - - responseObserver.apply { - val response = - with(RegisterAgentResponse.newBuilder()) { - valid = agentContext != null - reason = "Invalid agentId: $agentId" - this.agentId = agentId - build() - } - onNext(response) - onCompleted() - } + var valid = false + proxy.getAgentContext(agentId) + ?.apply { + valid = true + agentName = request.agentName + hostName = request.hostName + markActivity() + } ?: logger.info("registerAgent() missing AgentContext agentId: $agentId") + + responseObserver + .apply { + onNext( + RegisterAgentResponse.newBuilder() + .run { + reason = "Invalid agentId: $agentId" + this.valid = valid + this.agentId = agentId + build() + }) + onCompleted() + } } override fun registerPath(request: RegisterPathRequest, @@ -68,27 +71,28 @@ internal class ProxyServiceImpl(private val proxy: Proxy) : ProxyServiceGrpc.Pro logger.info("Overwriting path /$path") val agentId = request.agentId - val agentContext = proxy.getAgentContext(agentId) - val response = - with(RegisterPathResponse.newBuilder()) { - valid = agentContext != null - reason = "Invalid agentId: $agentId" - pathCount = proxy.pathMapSize() - pathId = if (agentContext != null) PATH_ID_GENERATOR.getAndIncrement() else -1 - build() + var valid = false + + proxy.getAgentContext(agentId) + ?.apply { + valid = true + proxy.addPath(path, this) + markActivity() + } ?: logger.error("Missing AgentContext for agentId: $agentId") + + responseObserver + .apply { + onNext( + RegisterPathResponse.newBuilder() + .run { + this.valid = valid + reason = "Invalid agentId: $agentId" + pathCount = proxy.pathMapSize() + pathId = if (valid) PATH_ID_GENERATOR.getAndIncrement() else -1 + build() + }) + onCompleted() } - if (agentContext == null) { - logger.error("Missing AgentContext for agentId: $agentId") - } - else { - proxy.addPath(path, agentContext) - agentContext.markActivity() - } - - responseObserver.apply { - onNext(response) - onCompleted() - } } override fun unregisterPath(request: UnregisterPathRequest, @@ -96,103 +100,107 @@ internal class ProxyServiceImpl(private val proxy: Proxy) : ProxyServiceGrpc.Pro val path = request.path val agentId = request.agentId val agentContext = proxy.getAgentContext(agentId) - val responseBuilder = UnregisterPathResponse.newBuilder() if (agentContext == null) { logger.error("Missing AgentContext for agentId: $agentId") - responseBuilder.apply { - valid = false - reason = "Invalid agentId: $agentId" - } + responseBuilder + .apply { + valid = false + reason = "Invalid agentId: $agentId" + } } else { proxy.removePath(path, agentId, responseBuilder) agentContext.markActivity() } - responseObserver.apply { - onNext(responseBuilder.build()) - onCompleted() - } + responseObserver + .apply { + onNext(responseBuilder.build()) + onCompleted() + } } override fun pathMapSize(request: PathMapSizeRequest, responseObserver: StreamObserver) { - responseObserver.apply { - val response = with(PathMapSizeResponse.newBuilder()) { - pathCount = proxy.pathMapSize() - build() - } - onNext(response) - onCompleted() - } + responseObserver + .apply { + onNext( + PathMapSizeResponse.newBuilder() + .run { + pathCount = proxy.pathMapSize() + build() + }) + onCompleted() + } } override fun sendHeartBeat(request: HeartBeatRequest, responseObserver: StreamObserver) { - proxy.metrics?.heartbeats?.inc() + if (proxy.isZipkinEnabled) + proxy.metrics.heartbeats.inc() val agentContext = proxy.getAgentContext(request.agentId) agentContext?.markActivity() ?: logger.info("sendHeartBeat() missing AgentContext agentId: ${request.agentId}") - responseObserver.apply { - val response = - with(HeartBeatResponse.newBuilder()) { - valid = agentContext != null - reason = "Invalid agentId: ${request.agentId}" - build() - } - onNext(response) - onCompleted() - } + responseObserver + .apply { + onNext( + HeartBeatResponse.newBuilder() + .run { + valid = agentContext != null + reason = "Invalid agentId: ${request.agentId}" + build() + }) + onCompleted() + } } override fun readRequestsFromProxy(agentInfo: AgentInfo, responseObserver: StreamObserver) { - val agentId = agentInfo.agentId - val agentContext = proxy.getAgentContext(agentId) - if (agentContext != null) { - while (proxy.isRunning && agentContext.valid) { - val scrapeRequest = agentContext.pollScrapeRequestQueue() - if (scrapeRequest != null) { - responseObserver.onNext(scrapeRequest.scrapeRequest) + responseObserver + .run { + proxy.getAgentContext(agentInfo.agentId) + ?.let { + while (proxy.isRunning && it.isValid) + it.pollScrapeRequestQueue()?.apply { onNext(scrapeRequest) } + } + onCompleted() } - } - } - responseObserver.onCompleted() } override fun writeResponsesToProxy(responseObserver: StreamObserver): StreamObserver { - return object : StreamObserver { - override fun onNext(response: ScrapeResponse) { - val scrapeRequest = proxy.getFromScrapeRequestMap(response.scrapeId) - if (scrapeRequest == null) - logger.error("Missing ScrapeRequestWrapper for scrape_id: ${response.scrapeId}") - else - scrapeRequest.apply { - scrapeResponse = response - markComplete() - agentContext.markActivity() - } + return streamObserver { + onNext { response -> + proxy.getFromScrapeRequestMap(response.scrapeId) + ?.apply { + scrapeResponse = response + markComplete() + agentContext.markActivity() + } ?: logger.error("Missing ScrapeRequestWrapper for scrape_id: ${response.scrapeId}") } - override fun onError(t: Throwable) { - val status = Status.fromThrowable(t) - if (status !== Status.CANCELLED) - logger.info("Error in writeResponsesToProxy(): $status") + onError { t -> + Status.fromThrowable(t) + .let { + if (it !== Status.CANCELLED) + logger.info("Error in writeResponsesToProxy(): $it") + } + try { - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() - } + responseObserver + .apply { + onNext(Empty.getDefaultInstance()) + onCompleted() + } } catch (e: StatusRuntimeException) { // logger.warn("StatusRuntimeException", e); // Ignore } - } - override fun onCompleted() { - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() - } + onCompleted { + responseObserver + .apply { + onNext(Empty.getDefaultInstance()) + onCompleted() + } } } } diff --git a/src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt b/src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt index 085f8b81..d6e0350a 100644 --- a/src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt +++ b/src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt @@ -19,24 +19,26 @@ package io.prometheus.proxy import io.grpc.Attributes import io.grpc.ServerTransportFilter import io.prometheus.Proxy +import io.prometheus.dsl.GrpcDsl.attributes import org.slf4j.LoggerFactory class ProxyTransportFilter(private val proxy: Proxy) : ServerTransportFilter() { private fun getRemoteAddr(attributes: Attributes): String { - val key = attributes.keys().first { "remote-addr" == it.toString() } - return if (key == null) "Unknown" else attributes.get(key)?.toString() ?: "Unknown" + return attributes.keys().first { "remote-addr" == it.toString() } + ?.let { + attributes.get(it)?.toString() ?: "Unknown" + } ?: "Unknown" } override fun transportReady(attributes: Attributes): Attributes { - val remoteAddr = getRemoteAddr(attributes) - val agentContext = AgentContext(proxy, remoteAddr) + val agentContext = AgentContext(proxy, getRemoteAddr(attributes)) proxy.addAgentContext(agentContext) logger.info("Connected to $agentContext") - return Attributes.newBuilder() - .set(Proxy.ATTRIB_AGENT_ID, agentContext.agentId) - .setAll(attributes) - .build() + return attributes { + set(Proxy.ATTRIB_AGENT_ID, agentContext.agentId) + setAll(attributes) + } } override fun transportTerminated(attributes: Attributes?) { diff --git a/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt b/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt index 46b4bd40..39584ec2 100644 --- a/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt +++ b/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt @@ -17,10 +17,10 @@ package io.prometheus.proxy -import com.google.common.base.MoreObjects import com.google.common.base.Preconditions import io.prometheus.Proxy -import io.prometheus.common.AtomicDelegates +import io.prometheus.delegate.AtomicDelegates +import io.prometheus.dsl.GuavaDsl.toStringElements import io.prometheus.grpc.ScrapeRequest import io.prometheus.grpc.ScrapeResponse import java.util.concurrent.CountDownLatch @@ -34,21 +34,22 @@ class ScrapeRequestWrapper(proxy: Proxy, private val createTime = System.currentTimeMillis() private val complete = CountDownLatch(1) - private val requestTimer = proxy.metrics?.scrapeRequestLatency?.startTimer() + private val requestTimer = if (proxy.isMetricsEnabled) proxy.metrics.scrapeRequestLatency.startTimer() else null val agentContext: AgentContext = Preconditions.checkNotNull(agentContext) var scrapeResponse: ScrapeResponse by AtomicDelegates.notNullReference() val scrapeRequest: ScrapeRequest = - with(ScrapeRequest.newBuilder()) { - agentId = agentContext.agentId - scrapeId = SCRAPE_ID_GENERATOR.getAndIncrement() - this.path = path - if (!accept.isNullOrBlank()) - this.accept = accept - build() - } + ScrapeRequest.newBuilder() + .run { + agentId = agentContext.agentId + scrapeId = SCRAPE_ID_GENERATOR.getAndIncrement() + this.path = path + if (!accept.isNullOrBlank()) + this.accept = accept + build() + } val scrapeId: Long get() = scrapeRequest.scrapeId @@ -71,10 +72,10 @@ class ScrapeRequestWrapper(proxy: Proxy, } override fun toString() = - MoreObjects.toStringHelper(this) - .add("scrapeId", scrapeRequest.scrapeId) - .add("path", scrapeRequest.path) - .toString() + toStringElements { + add("scrapeId", scrapeRequest.scrapeId) + add("path", scrapeRequest.path) + } companion object { private val SCRAPE_ID_GENERATOR = AtomicLong(0) diff --git a/src/test/java/io/prometheus/AdminDefaultPathTest.kt b/src/test/java/io/prometheus/AdminDefaultPathTest.kt index 4de49b58..fc16fc6a 100644 --- a/src/test/java/io/prometheus/AdminDefaultPathTest.kt +++ b/src/test/java/io/prometheus/AdminDefaultPathTest.kt @@ -16,11 +16,10 @@ package io.prometheus -import io.prometheus.ConstantsTest.OK_HTTP_CLIENT import io.prometheus.TestUtils.startAgent import io.prometheus.TestUtils.startProxy import io.prometheus.client.CollectorRegistry -import okhttp3.Request +import io.prometheus.dsl.OkHttpDsl.get import org.assertj.core.api.Assertions.assertThat import org.junit.AfterClass import org.junit.BeforeClass @@ -29,86 +28,77 @@ import org.slf4j.LoggerFactory import java.io.IOException import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeoutException -import kotlin.properties.Delegates class AdminDefaultPathTest { @Test fun proxyPingPathTest() { - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.pingPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string()).startsWith("pong") - } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.pingPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).startsWith("pong") + } } @Test fun agentPingPathTest() { - val url = "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.pingPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string()).startsWith("pong") - } + "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.pingPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).startsWith("pong") + } } @Test fun proxyVersionPathTest() { - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.versionPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string()).contains("Version") - } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.versionPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).contains("Version") + } } @Test fun agentVersionPathTest() { - val url = "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.versionPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string()).contains("Version") - } + "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.versionPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).contains("Version") + } } @Test fun proxyHealthCheckPathTest() { - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.healthCheckPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string().length).isGreaterThan(10) - } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.healthCheckPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string().length).isGreaterThan(10) + } } @Test fun agentHealthCheckPathTest() { - val url = "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.healthCheckPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.healthCheckPath}" + .get { assertThat(it.body()!!.string().length).isGreaterThan(10) } } @Test fun proxyThreadDumpPathTest() { - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.threadDumpPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.threadDumpPath}" + .get { assertThat(it.body()!!.string().length).isGreaterThan(10) } } @Test fun agentThreadDumpPathTest() { - val url = "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.threadDumpPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + "http://localhost:${AGENT.configVals.admin.port}/${AGENT.configVals.admin.threadDumpPath}" + .get { assertThat(it.body()!!.string().length).isGreaterThan(10) } } companion object { private val logger = LoggerFactory.getLogger(AdminDefaultPathTest::class.java) - private var PROXY: Proxy by Delegates.notNull() - private var AGENT: Agent by Delegates.notNull() + private lateinit var PROXY: Proxy + private lateinit var AGENT: Agent @JvmStatic @BeforeClass @@ -126,10 +116,8 @@ class AdminDefaultPathTest { @Throws(InterruptedException::class, TimeoutException::class) fun takeDown() { logger.info("Stopping Proxy and Agent") - PROXY.stopAsync() - PROXY.awaitTerminated(5, SECONDS) - AGENT.stopAsync() - AGENT.awaitTerminated(5, SECONDS) + PROXY.stopSync() + AGENT.stopSync() } } } diff --git a/src/test/java/io/prometheus/AdminEmptyPathTest.kt b/src/test/java/io/prometheus/AdminEmptyPathTest.kt index 7ddf6fe0..3d1dd09d 100644 --- a/src/test/java/io/prometheus/AdminEmptyPathTest.kt +++ b/src/test/java/io/prometheus/AdminEmptyPathTest.kt @@ -16,11 +16,10 @@ package io.prometheus -import io.prometheus.ConstantsTest.OK_HTTP_CLIENT import io.prometheus.TestUtils.startAgent import io.prometheus.TestUtils.startProxy import io.prometheus.client.CollectorRegistry -import okhttp3.Request +import io.prometheus.dsl.OkHttpDsl.get import org.assertj.core.api.Assertions.assertThat import org.junit.AfterClass import org.junit.BeforeClass @@ -29,7 +28,6 @@ import org.slf4j.LoggerFactory import java.io.IOException import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeoutException -import kotlin.properties.Delegates class AdminEmptyPathTest { @@ -37,41 +35,37 @@ class AdminEmptyPathTest { fun proxyPingPathTest() { assertThat(PROXY.configVals.admin.port).isEqualTo(8098) assertThat(PROXY.configVals.admin.pingPath).isEqualTo("") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.pingPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.pingPath}" + .get { assertThat(it.code()).isEqualTo(404) } } @Test fun proxyVersionPathTest() { assertThat(PROXY.configVals.admin.port).isEqualTo(8098) assertThat(PROXY.configVals.admin.versionPath).isEqualTo("") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.versionPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.versionPath}" + .get { assertThat(it.code()).isEqualTo(404) } } @Test fun proxyHealthCheckPathTest() { assertThat(PROXY.configVals.admin.healthCheckPath).isEqualTo("") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.healthCheckPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.healthCheckPath}" + .get { assertThat(it.code()).isEqualTo(404) } } @Test fun proxyThreadDumpPathTest() { assertThat(PROXY.configVals.admin.threadDumpPath).isEqualTo("") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.threadDumpPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.threadDumpPath}" + .get { assertThat(it.code()).isEqualTo(404) } } companion object { private val logger = LoggerFactory.getLogger(AdminEmptyPathTest::class.java) - private var PROXY: Proxy by Delegates.notNull() - private var AGENT: Agent by Delegates.notNull() + private lateinit var PROXY: Proxy + private lateinit var AGENT: Agent @JvmStatic @BeforeClass @@ -94,10 +88,8 @@ class AdminEmptyPathTest { @Throws(InterruptedException::class, TimeoutException::class) fun takeDown() { logger.info("Stopping Proxy and Agent") - PROXY.stopAsync() - PROXY.awaitTerminated(5, SECONDS) - AGENT.stopAsync() - AGENT.awaitTerminated(5, SECONDS) + PROXY.stopSync() + AGENT.stopSync() } } } diff --git a/src/test/java/io/prometheus/AdminNonDefaultPathTest.kt b/src/test/java/io/prometheus/AdminNonDefaultPathTest.kt index 79fa1e72..7786cdf5 100644 --- a/src/test/java/io/prometheus/AdminNonDefaultPathTest.kt +++ b/src/test/java/io/prometheus/AdminNonDefaultPathTest.kt @@ -16,11 +16,10 @@ package io.prometheus -import io.prometheus.ConstantsTest.OK_HTTP_CLIENT import io.prometheus.TestUtils.startAgent import io.prometheus.TestUtils.startProxy import io.prometheus.client.CollectorRegistry -import okhttp3.Request +import io.prometheus.dsl.OkHttpDsl.get import org.assertj.core.api.Assertions.assertThat import org.junit.AfterClass import org.junit.BeforeClass @@ -29,7 +28,6 @@ import org.slf4j.LoggerFactory import java.io.IOException import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeoutException -import kotlin.properties.Delegates class AdminNonDefaultPathTest { @@ -37,50 +35,46 @@ class AdminNonDefaultPathTest { fun proxyPingPathTest() { assertThat(PROXY.configVals.admin.port).isEqualTo(8099) assertThat(PROXY.configVals.admin.pingPath).isEqualTo("pingPath2") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.pingPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string()).startsWith("pong") - } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.pingPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).startsWith("pong") + } } @Test fun proxyVersionPathTest() { assertThat(PROXY.configVals.admin.port).isEqualTo(8099) assertThat(PROXY.configVals.admin.versionPath).isEqualTo("versionPath2") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.versionPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string()).contains("Version") - } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.versionPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).contains("Version") + } } @Test fun proxyHealthCheckPathTest() { assertThat(PROXY.configVals.admin.healthCheckPath).isEqualTo("healthCheckPath2") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.healthCheckPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { - assertThat(it.code()).isEqualTo(200) - assertThat(it.body()!!.string().length).isGreaterThan(10) - } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.healthCheckPath}" + .get { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string().length).isGreaterThan(10) + } } @Test fun proxyThreadDumpPathTest() { assertThat(PROXY.configVals.admin.threadDumpPath).isEqualTo("threadDumpPath2") - val url = "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.threadDumpPath}" - val request = Request.Builder().url(url) - OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + "http://localhost:${PROXY.configVals.admin.port}/${PROXY.configVals.admin.threadDumpPath}" + .get { assertThat(it.body()!!.string().length).isGreaterThan(10) } } companion object { private val logger = LoggerFactory.getLogger(AdminNonDefaultPathTest::class.java) - private var PROXY: Proxy by Delegates.notNull() - private var AGENT: Agent by Delegates.notNull() + private lateinit var PROXY: Proxy + private lateinit var AGENT: Agent @JvmStatic @BeforeClass @@ -104,10 +98,8 @@ class AdminNonDefaultPathTest { @Throws(InterruptedException::class, TimeoutException::class) fun takeDown() { logger.info("Stopping Proxy and Agent") - PROXY.stopAsync() - PROXY.awaitTerminated(5, SECONDS) - AGENT.stopAsync() - AGENT.awaitTerminated(5, SECONDS) + PROXY.stopSync() + AGENT.stopSync() } } } diff --git a/src/test/java/io/prometheus/MiscTests.kt b/src/test/java/io/prometheus/CommonTests.kt similarity index 67% rename from src/test/java/io/prometheus/MiscTests.kt rename to src/test/java/io/prometheus/CommonTests.kt index 1a3af12d..ac1d331a 100644 --- a/src/test/java/io/prometheus/MiscTests.kt +++ b/src/test/java/io/prometheus/CommonTests.kt @@ -16,37 +16,35 @@ package io.prometheus -import com.google.common.collect.Maps -import io.prometheus.ConstantsTest.PROXY_PORT +import com.google.common.collect.Maps.newConcurrentMap +import io.prometheus.TestConstants.PROXY_PORT import io.prometheus.agent.RequestFailureException import io.prometheus.common.sleepForMillis import io.prometheus.common.sleepForSecs -import okhttp3.Request +import io.prometheus.dsl.OkHttpDsl.get +import io.prometheus.dsl.SparkDsl.httpServer +import io.prometheus.proxy.ProxyHttpService.Companion.sparkExceptionHandler import org.assertj.core.api.Assertions.assertThat import org.slf4j.LoggerFactory import spark.Service import java.lang.Math.abs import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeUnit.MINUTES import java.util.concurrent.atomic.AtomicInteger import java.util.stream.IntStream -object MiscTests { +object CommonTests { - private val logger = LoggerFactory.getLogger(MiscTests::class.java) + private val logger = LoggerFactory.getLogger(CommonTests::class.java) fun missingPathTest(caller: String) { logger.info("Calling missingPathTest() from $caller") - val url = "http://localhost:$PROXY_PORT/" - val request = Request.Builder().url(url) - ConstantsTest.OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + "http://localhost:$PROXY_PORT/".get { assertThat(it.code()).isEqualTo(404) } } fun invalidPathTest(caller: String) { logger.info("Calling invalidPathTest() from $caller") - val url = "http://localhost:$PROXY_PORT/invalid_path" - val request = Request.Builder().url(url) - ConstantsTest.OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + "http://localhost:$PROXY_PORT/invalid_path".get { assertThat(it.code()).isEqualTo(404) } } fun addRemovePathsTest(agent: Agent, caller: String) { @@ -56,7 +54,7 @@ object MiscTests { val originalSize = agent.pathMapSize() var cnt = 0 - IntStream.range(0, ConstantsTest.REPS) + IntStream.range(0, TestConstants.REPS) .forEach { val path = "test-$it" agent.registerPath(path, "http://localhost:$PROXY_PORT/$path") @@ -72,36 +70,40 @@ object MiscTests { logger.info("Calling threadedAddRemovePathsTest() from $caller") val paths = mutableListOf() val cnt = AtomicInteger(0) - val latch1 = CountDownLatch(ConstantsTest.REPS) - val latch2 = CountDownLatch(ConstantsTest.REPS) + val latch1 = CountDownLatch(TestConstants.REPS) + val latch2 = CountDownLatch(TestConstants.REPS) // Take into account pre-existing paths already registered val originalSize = agent.pathMapSize() - IntStream.range(0, ConstantsTest.REPS) + IntStream.range(0, TestConstants.REPS) .forEach { - ConstantsTest.EXECUTOR_SERVICE.submit( - { - val path = "test-${cnt.getAndIncrement()}" - synchronized(paths) { - paths.add(path) - } - try { - val url = "http://localhost:$PROXY_PORT/$path" - agent.registerPath(path, url) - latch1.countDown() - } catch (e: RequestFailureException) { - e.printStackTrace() - } - }) + TestConstants + .EXECUTOR_SERVICE + .submit( + { + val path = "test-${cnt.getAndIncrement()}" + + synchronized(paths) { + paths.add(path) + } + + try { + val url = "http://localhost:$PROXY_PORT/$path" + agent.registerPath(path, url) + latch1.countDown() + } catch (e: RequestFailureException) { + e.printStackTrace() + } + }) } - assertThat(latch1.await(5, SECONDS)).isTrue() - assertThat(paths.size).isEqualTo(ConstantsTest.REPS) - assertThat(agent.pathMapSize()).isEqualTo(originalSize + ConstantsTest.REPS) + assertThat(latch1.await(1, MINUTES)).isTrue() + assertThat(paths.size).isEqualTo(TestConstants.REPS) + assertThat(agent.pathMapSize()).isEqualTo(originalSize + TestConstants.REPS) paths.forEach { - ConstantsTest + TestConstants .EXECUTOR_SERVICE .submit({ try { @@ -114,22 +116,15 @@ object MiscTests { } // Wait for all unregistrations to complete - assertThat(latch2.await(5, SECONDS)).isTrue() + assertThat(latch2.await(1, MINUTES)).isTrue() assertThat(agent.pathMapSize()).isEqualTo(originalSize) } fun invalidAgentUrlTest(agent: Agent, badPath: String = "badPath", caller: String) { - logger.info("Calling invalidAgentUrlTest() from $caller") agent.registerPath(badPath, "http://localhost:33/metrics") - - val url = "http://localhost:$PROXY_PORT/$badPath" - val request = Request.Builder().url(url) - ConstantsTest - .OK_HTTP_CLIENT - .newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } - + "http://localhost:$PROXY_PORT/$badPath".get { assertThat(it.code()).isEqualTo(404) } agent.unregisterPath(badPath) } @@ -140,34 +135,25 @@ object MiscTests { caller: String) { logger.info("Calling timeoutTest() from $caller") - val http = - Service.ignite().apply { + + val httpServer = + httpServer { + initExceptionHandler { e -> sparkExceptionHandler(e, agentPort) } port(agentPort) get("/$agentPath") { _, res -> res.type("text/plain") sleepForSecs(10) "I timed out" } + awaitInitialization() } - // Give http server chance to start - sleepForSecs(5) - - val agentUrl = "http://localhost:$agentPort/$agentPath" - agent.registerPath("/$proxyPath", agentUrl) - - val proxyUrl = "http://localhost:$PROXY_PORT/$proxyPath" - val request = Request.Builder().url(proxyUrl) - ConstantsTest - .OK_HTTP_CLIENT - .newCall(request.build()) - .execute() - .use { - assertThat(it.code()).isEqualTo(404) - } - + agent.registerPath("/$proxyPath", "http://localhost:$agentPort/$agentPath") + "http://localhost:$PROXY_PORT/$proxyPath".get { assertThat(it.code()).isEqualTo(404) } agent.unregisterPath("/$proxyPath") - http.stop() + + httpServer.stop() + sleepForSecs(5) } fun proxyCallTest(agent: Agent, @@ -181,7 +167,7 @@ object MiscTests { logger.info("Calling proxyCallTest() from $caller") val httpServers = mutableListOf() - val pathMap = Maps.newConcurrentMap() + val pathMap = newConcurrentMap() // Take into account pre-existing paths already registered val originalSize = agent.pathMapSize() @@ -189,25 +175,24 @@ object MiscTests { // Create the endpoints IntStream.range(0, httpServerCount) .forEach { i -> - val http = - Service.ignite().apply { - port(startingPort + i) + val port = startingPort + i + httpServers.add( + httpServer { + initExceptionHandler { e -> sparkExceptionHandler(e, port) } + port(port) threadPool(30, 10, 1000) get("/agent-$i") { _, res -> res.type("text/plain") "value: $i" } - } - httpServers.add(http) + awaitInitialization() + }) } - // Give http server chance to start - sleepForSecs(5) - // Create the paths IntStream.range(0, pathCount) .forEach { - val index = abs(ConstantsTest.RANDOM.nextInt()) % httpServers.size + val index = abs(TestConstants.RANDOM.nextInt()) % httpServers.size agent.registerPath("proxy-$it", "http://localhost:${startingPort + index}/agent-$index") pathMap.put(it, index) } @@ -225,7 +210,8 @@ object MiscTests { val latch = CountDownLatch(parallelQueryCount) IntStream.range(0, parallelQueryCount) .forEach { - ConstantsTest.EXECUTOR_SERVICE + TestConstants + .EXECUTOR_SERVICE .submit { try { callProxy(pathMap, "Parallel $it") @@ -236,7 +222,7 @@ object MiscTests { } } - assertThat(latch.await(10, SECONDS)).isTrue() + assertThat(latch.await(1, MINUTES)).isTrue() val errorCnt = AtomicInteger() pathMap.forEach { @@ -251,19 +237,16 @@ object MiscTests { assertThat(agent.pathMapSize()).isEqualTo(originalSize) httpServers.forEach(Service::stop) + sleepForSecs(5) } private fun callProxy(pathMap: Map, msg: String) { //logger.info("Calling proxy for ${msg}") // Choose one of the pathMap values - val index = abs(ConstantsTest.RANDOM.nextInt() % pathMap.size) + val index = abs(TestConstants.RANDOM.nextInt() % pathMap.size) val httpVal = pathMap[index] - val url = "http://localhost:$PROXY_PORT/proxy-$index" - val request = Request.Builder().url(url) - ConstantsTest.OK_HTTP_CLIENT - .newCall(request.build()) - .execute() - .use { + "http://localhost:$PROXY_PORT/proxy-$index" + .get { if (it.code() != 200) logger.error("Proxy failed on $msg") assertThat(it.code()).isEqualTo(200) diff --git a/src/test/java/io/prometheus/DataClassTest.kt b/src/test/java/io/prometheus/DataClassTest.kt index 4e43eabc..9c8d4820 100644 --- a/src/test/java/io/prometheus/DataClassTest.kt +++ b/src/test/java/io/prometheus/DataClassTest.kt @@ -19,10 +19,10 @@ package io.prometheus import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigSyntax -import io.prometheus.common.AdminConfig +import io.prometheus.common.AdminConfig.Companion.newAdminConfig import io.prometheus.common.ConfigVals -import io.prometheus.common.MetricsConfig -import io.prometheus.common.ZipkinConfig +import io.prometheus.common.MetricsConfig.Companion.newMetricsConfig +import io.prometheus.common.ZipkinConfig.Companion.newZipkinConfig import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -36,72 +36,112 @@ class DataClassTest { @Test fun adminConfigTest() { var vals = configVals("agent.admin.enabled=true") - var c = AdminConfig.create(vals.agent.admin.enabled, -1, vals.agent.admin) - assertThat(c.enabled).isTrue() + newAdminConfig(vals.agent.admin.enabled, -1, vals.agent.admin) + .let { + assertThat(it.enabled).isTrue() + } 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) - - c = AdminConfig.create(true, 444, configVals("agent.admin.pingPath=a pingpath val").agent.admin) - assertThat(c.pingPath).isEqualTo("a pingpath val") - - c = AdminConfig.create(true, 444, configVals("agent.admin.versionPath=a versionpath val").agent.admin) - assertThat(c.versionPath).isEqualTo("a versionpath val") - - c = AdminConfig.create(true, 444, configVals("agent.admin.healthCheckPath=a healthCheckPath val").agent.admin) - assertThat(c.healthCheckPath).isEqualTo("a healthCheckPath val") - - c = AdminConfig.create(true, 444, configVals("agent.admin.threadDumpPath=a threadDumpPath val").agent.admin) - assertThat(c.threadDumpPath).isEqualTo("a threadDumpPath val") + newAdminConfig(vals.agent.admin.enabled, vals.agent.admin.port, vals.agent.admin) + .let { + assertThat(it.enabled).isFalse() + assertThat(it.port).isEqualTo(888) + } + + newAdminConfig(true, 444, configVals("agent.admin.pingPath=a pingpath val").agent.admin) + .let { + assertThat(it.pingPath).isEqualTo("a pingpath val") + } + + newAdminConfig(true, 444, configVals("agent.admin.versionPath=a versionpath val").agent.admin) + .let { + assertThat(it.versionPath).isEqualTo("a versionpath val") + } + + newAdminConfig(true, 444, configVals("agent.admin.healthCheckPath=a healthCheckPath val").agent.admin) + .let { + assertThat(it.healthCheckPath).isEqualTo("a healthCheckPath val") + } + + newAdminConfig(true, 444, configVals("agent.admin.threadDumpPath=a threadDumpPath val").agent.admin) + .let { + assertThat(it.threadDumpPath).isEqualTo("a threadDumpPath val") + } } @Test fun metricsConfigTest() { - var c = MetricsConfig.create(true, 555, configVals("agent.metrics.enabled=true").agent.metrics) - assertThat(c.enabled).isTrue() - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.hostname=testval").agent.metrics) - assertThat(c.port).isEqualTo(555) - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.path=a path val").agent.metrics) - assertThat(c.path).isEqualTo("a path val") - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.standardExportsEnabled=true").agent.metrics) - assertThat(c.standardExportsEnabled).isTrue() - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.memoryPoolsExportsEnabled=true").agent.metrics) - assertThat(c.memoryPoolsExportsEnabled).isTrue() - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.garbageCollectorExportsEnabled=true").agent.metrics) - assertThat(c.garbageCollectorExportsEnabled).isTrue() - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.threadExportsEnabled=true").agent.metrics) - assertThat(c.threadExportsEnabled).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() + newMetricsConfig(true, 555, configVals("agent.metrics.enabled=true").agent.metrics) + .let { + assertThat(it.enabled).isTrue() + } + + newMetricsConfig(true, 555, configVals("agent.metrics.hostname=testval").agent.metrics) + .let { + assertThat(it.port).isEqualTo(555) + } + + newMetricsConfig(true, 555, configVals("agent.metrics.path=a path val").agent.metrics) + .let { + assertThat(it.path).isEqualTo("a path val") + } + + newMetricsConfig(true, 555, configVals("agent.metrics.standardExportsEnabled=true").agent.metrics) + .let { + assertThat(it.standardExportsEnabled).isTrue() + } + + newMetricsConfig(true, 555, configVals("agent.metrics.memoryPoolsExportsEnabled=true").agent.metrics) + .let { + assertThat(it.memoryPoolsExportsEnabled).isTrue() + } + + newMetricsConfig(true, 555, configVals("agent.metrics.garbageCollectorExportsEnabled=true").agent.metrics) + .let { + assertThat(it.garbageCollectorExportsEnabled).isTrue() + } + + newMetricsConfig(true, 555, configVals("agent.metrics.threadExportsEnabled=true").agent.metrics) + .let { + assertThat(it.threadExportsEnabled).isTrue() + } + + newMetricsConfig(true, 555, configVals("agent.metrics.classLoadingExportsEnabled=true").agent.metrics) + .let { + assertThat(it.classLoadingExportsEnabled).isTrue() + } + + newMetricsConfig(true, 555, configVals("agent.metrics.versionInfoExportsEnabled=true").agent.metrics) + .let { + assertThat(it.versionInfoExportsEnabled).isTrue() + } } @Test fun zipkinConfigTest() { - var 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") + newZipkinConfig(configVals("agent.internal.zipkin.enabled=true").agent.internal.zipkin) + .let { + assertThat(it.enabled).isTrue() + } + + newZipkinConfig(configVals("agent.internal.zipkin.hostname=testval").agent.internal.zipkin) + .let { + assertThat(it.hostname).isEqualTo("testval") + } + + newZipkinConfig(configVals("agent.internal.zipkin.port=999").agent.internal.zipkin) + .let { + assertThat(it.port).isEqualTo(999) + } + + newZipkinConfig(configVals("agent.internal.zipkin.path=a path val").agent.internal.zipkin) + .let { + assertThat(it.path).isEqualTo("a path val") + } + + newZipkinConfig(configVals("agent.internal.zipkin.serviceName=a service name").agent.internal.zipkin) + .let { + assertThat(it.serviceName).isEqualTo("a service name") + } } } diff --git a/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt b/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt index 90eca988..9f3d4189 100644 --- a/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt +++ b/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt @@ -24,56 +24,55 @@ import org.junit.BeforeClass import org.junit.Test import org.slf4j.LoggerFactory import java.util.concurrent.TimeUnit.SECONDS -import kotlin.properties.Delegates class InProcessTestNoAdminMetricsTest { @Test fun missingPathTest() { - MiscTests.missingPathTest(this.javaClass.simpleName) + CommonTests.missingPathTest(javaClass.simpleName) } @Test fun invalidPathTest() { - MiscTests.invalidPathTest(this.javaClass.simpleName) + CommonTests.invalidPathTest(javaClass.simpleName) } @Test fun addRemovePathsTest() { - MiscTests.addRemovePathsTest(AGENT, this.javaClass.simpleName) + CommonTests.addRemovePathsTest(AGENT, javaClass.simpleName) } @Test fun threadedAddRemovePathsTest() { - MiscTests.threadedAddRemovePathsTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.threadedAddRemovePathsTest(AGENT, caller = javaClass.simpleName) } @Test fun invalidAgentUrlTest() { - MiscTests.invalidAgentUrlTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.invalidAgentUrlTest(AGENT, caller = javaClass.simpleName) } @Test fun timeoutTest() { - MiscTests.timeoutTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.timeoutTest(AGENT, caller = javaClass.simpleName) } @Test fun proxyCallTest() { - MiscTests.proxyCallTest(AGENT, - httpServerCount = 25, - pathCount = 50, - sequentialQueryCount = 500, - sequentialPauseMillis = 25, - parallelQueryCount = 100, - caller = this.javaClass.simpleName) + CommonTests.proxyCallTest(AGENT, + httpServerCount = 10, + pathCount = 25, + sequentialQueryCount = 100, + sequentialPauseMillis = 25, + parallelQueryCount = 25, + caller = javaClass.simpleName) } companion object { private val logger = LoggerFactory.getLogger(InProcessTestNoAdminMetricsTest::class.java) - private var PROXY: Proxy by Delegates.notNull() - private var AGENT: Agent by Delegates.notNull() + private lateinit var PROXY: Proxy + private lateinit var AGENT: Agent @JvmStatic @BeforeClass @@ -89,11 +88,8 @@ class InProcessTestNoAdminMetricsTest { @AfterClass fun takeDown() { logger.info("Stopping Proxy and Agent") - PROXY.stopAsync() - PROXY.awaitTerminated(5, SECONDS) - AGENT.stopAsync() - AGENT.awaitTerminated(5, SECONDS) + PROXY.stopSync() + AGENT.stopSync() } } - -} +} \ No newline at end of file diff --git a/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt b/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt index 6f9efedc..53bf2bb3 100644 --- a/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt +++ b/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt @@ -24,45 +24,55 @@ import org.junit.BeforeClass import org.junit.Test import org.slf4j.LoggerFactory import java.util.concurrent.TimeUnit.SECONDS -import kotlin.properties.Delegates class InProcessTestWithAdminMetricsTest { @Test fun missingPathTest() { - MiscTests.missingPathTest(this.javaClass.simpleName) + CommonTests.missingPathTest(javaClass.simpleName) } @Test fun invalidPathTest() { - MiscTests.invalidPathTest(this.javaClass.simpleName) + CommonTests.invalidPathTest(javaClass.simpleName) } @Test fun addRemovePathsTest() { - MiscTests.addRemovePathsTest(AGENT, this.javaClass.simpleName) + CommonTests.addRemovePathsTest(AGENT, javaClass.simpleName) } @Test fun threadedAddRemovePathsTest() { - MiscTests.threadedAddRemovePathsTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.threadedAddRemovePathsTest(AGENT, caller = javaClass.simpleName) } @Test fun invalidAgentUrlTest() { - MiscTests.invalidAgentUrlTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.invalidAgentUrlTest(AGENT, caller = javaClass.simpleName) } @Test fun timeoutTest() { - MiscTests.timeoutTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.timeoutTest(AGENT, caller = javaClass.simpleName) + } + + @Test + fun proxyCallTest() { + CommonTests.proxyCallTest(AGENT, + httpServerCount = 25, + pathCount = 50, + sequentialQueryCount = 500, + sequentialPauseMillis = 25, + parallelQueryCount = 100, + caller = javaClass.simpleName) } companion object { private val logger = LoggerFactory.getLogger(InProcessTestWithAdminMetricsTest::class.java) - private var PROXY: Proxy by Delegates.notNull() - private var AGENT: Agent by Delegates.notNull() + private lateinit var PROXY: Proxy + private lateinit var AGENT: Agent @JvmStatic @BeforeClass @@ -78,12 +88,8 @@ class InProcessTestWithAdminMetricsTest { @AfterClass fun takeDown() { logger.info("Stopping Proxy and Agent") - PROXY.stopAsync() - PROXY.awaitTerminated(5, SECONDS) - AGENT.stopAsync() - AGENT.awaitTerminated(5, SECONDS) + PROXY.stopSync() + AGENT.stopSync() } } - - // proxyCallTest() called in InProcess tests } diff --git a/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt b/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt index 2ed5f1a8..53ad606c 100644 --- a/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt +++ b/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt @@ -25,45 +25,55 @@ import org.junit.Test import org.slf4j.LoggerFactory import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeoutException -import kotlin.properties.Delegates class NettyTestNoAdminMetricsTest { @Test fun missingPathTest() { - MiscTests.missingPathTest(this.javaClass.simpleName) + CommonTests.missingPathTest(javaClass.simpleName) } @Test fun invalidPathTest() { - MiscTests.invalidPathTest(this.javaClass.simpleName) + CommonTests.invalidPathTest(javaClass.simpleName) } @Test fun addRemovePathsTest() { - MiscTests.addRemovePathsTest(AGENT, this.javaClass.simpleName) + CommonTests.addRemovePathsTest(AGENT, javaClass.simpleName) } @Test fun threadedAddRemovePathsTest() { - MiscTests.threadedAddRemovePathsTest(AGENT, this.javaClass.simpleName) + CommonTests.threadedAddRemovePathsTest(AGENT, javaClass.simpleName) } @Test fun invalidAgentUrlTest() { - MiscTests.invalidAgentUrlTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.invalidAgentUrlTest(AGENT, caller = javaClass.simpleName) } @Test fun timeoutTest() { - MiscTests.timeoutTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.timeoutTest(AGENT, caller = javaClass.simpleName) + } + + @Test + fun proxyCallTest() { + CommonTests.proxyCallTest(AGENT, + httpServerCount = 25, + pathCount = 50, + sequentialQueryCount = 500, + sequentialPauseMillis = 25, + parallelQueryCount = 100, + caller = javaClass.simpleName) } companion object { private val logger = LoggerFactory.getLogger(NettyTestNoAdminMetricsTest::class.java) - private var PROXY: Proxy by Delegates.notNull() - private var AGENT: Agent by Delegates.notNull() + private lateinit var PROXY: Proxy + private lateinit var AGENT: Agent @JvmStatic @BeforeClass @@ -80,12 +90,8 @@ class NettyTestNoAdminMetricsTest { @Throws(InterruptedException::class, TimeoutException::class) fun takeDown() { logger.info("Stopping Proxy and Agent") - PROXY.stopAsync() - PROXY.awaitTerminated(5, SECONDS) - AGENT.stopAsync() - AGENT.awaitTerminated(5, SECONDS) + PROXY.stopSync() + AGENT.stopSync() } } - - // proxyCallTest() called in InProcess tests } diff --git a/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt b/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt index 1af65615..3bd5ce8c 100644 --- a/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt +++ b/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt @@ -26,45 +26,55 @@ import org.junit.Test import org.slf4j.LoggerFactory import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeoutException -import kotlin.properties.Delegates class NettyTestWithAdminMetricsTest { @Test fun missingPathTest() { - MiscTests.missingPathTest(this.javaClass.simpleName) + CommonTests.missingPathTest(javaClass.simpleName) } @Test fun invalidPathTest() { - MiscTests.invalidPathTest(this.javaClass.simpleName) + CommonTests.invalidPathTest(javaClass.simpleName) } @Test fun addRemovePathsTest() { - MiscTests.addRemovePathsTest(AGENT, this.javaClass.simpleName) + CommonTests.addRemovePathsTest(AGENT, javaClass.simpleName) } @Test fun threadedAddRemovePathsTest() { - MiscTests.threadedAddRemovePathsTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.threadedAddRemovePathsTest(AGENT, caller = javaClass.simpleName) } @Test fun invalidAgentUrlTest() { - MiscTests.invalidAgentUrlTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.invalidAgentUrlTest(AGENT, caller = javaClass.simpleName) } @Test fun timeoutTest() { - MiscTests.timeoutTest(AGENT, caller = this.javaClass.simpleName) + CommonTests.timeoutTest(AGENT, caller = javaClass.simpleName) + } + + @Test + fun proxyCallTest() { + CommonTests.proxyCallTest(AGENT, + httpServerCount = 10, + pathCount = 25, + sequentialQueryCount = 100, + sequentialPauseMillis = 25, + parallelQueryCount = 25, + caller = javaClass.simpleName) } companion object { private val logger = LoggerFactory.getLogger(NettyTestWithAdminMetricsTest::class.java) - private var PROXY: Proxy by Delegates.notNull() - private var AGENT: Agent by Delegates.notNull() + private lateinit var PROXY: Proxy + private lateinit var AGENT: Agent @JvmStatic @BeforeClass @@ -84,12 +94,8 @@ class NettyTestWithAdminMetricsTest { @Throws(InterruptedException::class, TimeoutException::class) fun takeDown() { logger.info("Stopping Proxy and Agent") - PROXY.stopAsync() - PROXY.awaitTerminated(5, SECONDS) - AGENT.stopAsync() - AGENT.awaitTerminated(5, SECONDS) + PROXY.stopSync() + AGENT.stopSync() } } - - // proxyCallTest() called in InProcess tests -} +} \ No newline at end of file diff --git a/src/test/java/io/prometheus/ConstantsTest.kt b/src/test/java/io/prometheus/TestConstants.kt similarity index 81% rename from src/test/java/io/prometheus/ConstantsTest.kt rename to src/test/java/io/prometheus/TestConstants.kt index 3114523b..43395735 100644 --- a/src/test/java/io/prometheus/ConstantsTest.kt +++ b/src/test/java/io/prometheus/TestConstants.kt @@ -16,13 +16,11 @@ package io.prometheus -import okhttp3.OkHttpClient import java.util.* -import java.util.concurrent.Executors +import java.util.concurrent.Executors.newCachedThreadPool -object ConstantsTest { - internal val EXECUTOR_SERVICE = Executors.newCachedThreadPool() - internal val OK_HTTP_CLIENT = OkHttpClient() +object TestConstants { + internal val EXECUTOR_SERVICE = newCachedThreadPool() internal val RANDOM = Random() internal val REPS = 1000 internal val PROXY_PORT = 9505 diff --git a/src/test/java/io/prometheus/TestUtils.kt b/src/test/java/io/prometheus/TestUtils.kt index 0e403ae5..5f4d1c79 100644 --- a/src/test/java/io/prometheus/TestUtils.kt +++ b/src/test/java/io/prometheus/TestUtils.kt @@ -16,14 +16,13 @@ package io.prometheus -import io.prometheus.ConstantsTest.PROXY_PORT +import io.prometheus.TestConstants.PROXY_PORT import io.prometheus.agent.AgentOptions import io.prometheus.common.getBanner import io.prometheus.common.getVersionDesc import io.prometheus.proxy.ProxyOptions import org.slf4j.LoggerFactory import java.io.IOException -import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException object TestUtils { @@ -35,22 +34,22 @@ object TestUtils { adminEnabled: Boolean = false, metricsEnabled: Boolean = false, argv: List = emptyList()): Proxy { - val args = - mutableListOf().apply { - addAll(ConstantsTest.args) - addAll(argv) - add("-Dproxy.admin.enabled=$adminEnabled") - add("-Dproxy.metrics.enabled=$metricsEnabled") - } - val options = ProxyOptions(args) - logger.info(getBanner("banners/proxy.txt")) + logger.info(getBanner("banners/proxy.txt", logger)) logger.info(getVersionDesc(false)) - val proxy = Proxy(options = options, proxyPort = PROXY_PORT, inProcessServerName = serverName, testMode = true) - proxy.startAsync() - proxy.awaitRunning(5, TimeUnit.SECONDS) - return proxy + return Proxy(options = + ProxyOptions( + mutableListOf() + .apply { + addAll(TestConstants.args) + addAll(argv) + add("-Dproxy.admin.enabled=$adminEnabled") + add("-Dproxy.metrics.enabled=$metricsEnabled") + }), + proxyPort = PROXY_PORT, + inProcessServerName = serverName, + testMode = true) { startSync() } } @Throws(IOException::class, TimeoutException::class) @@ -58,21 +57,21 @@ object TestUtils { adminEnabled: Boolean = false, metricsEnabled: Boolean = false, argv: List = emptyList()): Agent { - val args = - mutableListOf().apply { - addAll(ConstantsTest.args) - addAll(argv) - add("-Dagent.admin.enabled=$adminEnabled") - add("-Dagent.metrics.enabled=$metricsEnabled") - } - val options = AgentOptions(args, false) - logger.info(getBanner("banners/agent.txt")) + logger.info(getBanner("banners/agent.txt", logger)) logger.info(getVersionDesc(false)) - return Agent(options = options, inProcessServerName = serverName, testMode = true).apply { - startAsync() - awaitRunning(5, TimeUnit.SECONDS) - } + return Agent(options = + AgentOptions( + mutableListOf() + .apply { + addAll(TestConstants.args) + addAll(argv) + add("-Dagent.admin.enabled=$adminEnabled") + add("-Dagent.metrics.enabled=$metricsEnabled") + }, + false), + inProcessServerName = serverName, + testMode = true) { startSync() } } }