Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configurable endpoint for Prometheus scraping #938

Merged
merged 4 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ kamon.prometheus {
# Hostname and port used by the embedded web server to publish the scraping enpoint.
hostname = "0.0.0.0"
port = 9095
metrics_path = "/metrics"

# embedded server impl
# choose between
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ class PrometheusReporter(configPath: String = DefaultConfigPath, initialConfig:
private def startEmbeddedServerIfEnabled(): Unit = {
if (_reporterSettings.startEmbeddedServer) {
val server = _reporterSettings.createEmbeddedHttpServerClass()
.getConstructor(Array[Class[_]](classOf[String], classOf[Int], classOf[ScrapeSource], classOf[Config]): _*)
.newInstance(_reporterSettings.embeddedServerHostname, _reporterSettings.embeddedServerPort:Integer, this, _config)
_logger.info(s"Started the embedded HTTP server on http://${_reporterSettings.embeddedServerHostname}:${_reporterSettings.embeddedServerPort}")
.getConstructor(Array[Class[_]](classOf[String], classOf[Int], classOf[String], classOf[ScrapeSource], classOf[Config]): _*)
.newInstance(_reporterSettings.embeddedServerHostname,
_reporterSettings.embeddedServerPort:Integer,
_reporterSettings.embeddedServerPath,
this, _config)
_logger.info(s"Started the embedded HTTP server on http://${_reporterSettings.embeddedServerHostname}:${_reporterSettings.embeddedServerPort}${_reporterSettings.embeddedServerPath}")
_embeddedHttpServer = Some(server)
}
}
Expand All @@ -101,6 +104,7 @@ object PrometheusReporter {
startEmbeddedServer: Boolean,
embeddedServerHostname: String,
embeddedServerPort: Int,
embeddedServerPath: String,
embeddedServerImpl: String,
generic: PrometheusSettings.Generic
) {
Expand All @@ -120,6 +124,7 @@ object PrometheusReporter {
startEmbeddedServer = prometheusConfig.getBoolean("start-embedded-http-server"),
embeddedServerHostname = prometheusConfig.getString("embedded-server.hostname"),
embeddedServerPort = prometheusConfig.getInt("embedded-server.port"),
embeddedServerPath = prometheusConfig.getString("embedded-server.metrics_path"),
embeddedServerImpl = prometheusConfig.getString("embedded-server.impl"),
generic = PrometheusSettings.readSettings(prometheusConfig)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ import kamon.prometheus.ScrapeSource

abstract class EmbeddedHttpServer(hostname: String, port: Int, scrapeSource: ScrapeSource, config: Config) {
def stop(): Unit
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import java.util.zip.GZIPOutputStream
import scala.collection.JavaConverters._
import scala.collection.mutable

class SunEmbeddedHttpServer(hostname: String, port: Int, scrapeSource: ScrapeSource, config: Config) extends EmbeddedHttpServer(hostname, port, scrapeSource, config) {
class SunEmbeddedHttpServer(hostname: String, port: Int, path: String, scrapeSource: ScrapeSource, config: Config) extends EmbeddedHttpServer(hostname, port, scrapeSource, config) {
private val server = {
val s = HttpServer.create(new InetSocketAddress(InetAddress.getByName(hostname), port), 0)
s.setExecutor(null)
val handler = new HttpHandler {
override def handle(httpExchange: HttpExchange): Unit = {
if (httpExchange.getRequestURI.getPath == "/metrics") {
if (httpExchange.getRequestURI.getPath == path) {
val data = scrapeSource.scrapeData()
val bytes = data.getBytes(StandardCharsets.UTF_8)
var os: OutputStream = null
Expand All @@ -55,7 +55,7 @@ class SunEmbeddedHttpServer(hostname: String, port: Int, scrapeSource: ScrapeSou
}
}

s.createContext("/metrics", handler)
s.createContext(path, handler)
s.start()
s
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package kamon.prometheus
import java.io.FileNotFoundException
import java.net.URL
import com.typesafe.config.{Config, ConfigFactory}
import org.scalatest.concurrent.Eventually
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpec}

import java.util.zip.GZIPInputStream
import scala.concurrent.duration.DurationInt

class SunHttpServerSpecSuite extends EmbeddedHttpServerSpecSuite {
override def testConfig: Config = ConfigFactory.load()
Expand All @@ -22,7 +24,11 @@ class SunHttpServerSpecSuite extends EmbeddedHttpServerSpecSuite {
// override def port = 9096
//}

abstract class EmbeddedHttpServerSpecSuite extends WordSpec with Matchers with BeforeAndAfterAll with KamonTestSnapshotSupport {
abstract class EmbeddedHttpServerSpecSuite extends WordSpec
with Matchers
with BeforeAndAfterAll
with KamonTestSnapshotSupport
with Eventually {
protected def testConfig: Config

protected def port: Int = 9095
Expand Down Expand Up @@ -69,15 +75,6 @@ abstract class EmbeddedHttpServerSpecSuite extends WordSpec with Matchers with B
metrics shouldBe "# TYPE jvm_mem_total counter\njvm_mem_total 2.0\n"
}

"provide the metrics strictly on /metrics endpoint" in {
assertThrows[FileNotFoundException] {
httpGetMetrics("")
}
assertThrows[FileNotFoundException] {
httpGetMetrics("/metricsss")
}
}

"respect gzip Content-Encoding headers" in {
//arrange
testee.reportPeriodSnapshot(counter("jvm.mem"))
Expand All @@ -87,6 +84,20 @@ abstract class EmbeddedHttpServerSpecSuite extends WordSpec with Matchers with B
//assert
metrics.length should be > gzippedMetrics.length
}

"respect the path configuration" in {
httpGetMetrics("/metrics") should not be empty
assertThrows[FileNotFoundException] {
httpGetMetrics("/new-metrics")
}

testee.reconfigure(changeEndpoint("/new-metrics"))
httpGetMetrics("/new-metrics") should not be empty

assertThrows[FileNotFoundException] {
httpGetMetrics("/metrics")
}
}
}

private def httpGetMetrics(endpoint: String): String = {
Expand All @@ -106,4 +117,9 @@ abstract class EmbeddedHttpServerSpecSuite extends WordSpec with Matchers with B
try src.getLines.mkString
finally gzipStream.close()
}

private def changeEndpoint(path: String): Config = {
ConfigFactory.parseString(
s"""kamon.prometheus.embedded-server.metrics_path = ${path}""").withFallback(testConfig)
}
}