aboutsummaryrefslogtreecommitdiff
path: root/kamon-core/src/main/scala
diff options
context:
space:
mode:
Diffstat (limited to 'kamon-core/src/main/scala')
-rw-r--r--kamon-core/src/main/scala/kamon/Kamon.scala2
-rw-r--r--kamon-core/src/main/scala/kamon/Status.scala40
-rw-r--r--kamon-core/src/main/scala/kamon/StatusPage.scala75
-rw-r--r--kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala134
-rw-r--r--kamon-core/src/main/scala/kamon/status/Status.scala12
-rw-r--r--kamon-core/src/main/scala/kamon/status/StatusPageServer.scala113
6 files changed, 45 insertions, 331 deletions
diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala
index cfeea19e..8b2b64e6 100644
--- a/kamon-core/src/main/scala/kamon/Kamon.scala
+++ b/kamon-core/src/main/scala/kamon/Kamon.scala
@@ -23,7 +23,7 @@ object Kamon extends ClassLoading
with ModuleLoading
with ContextPropagation
with ContextStorage
- with StatusPage {
+ with Status {
@volatile private var _environment = Environment.fromConfig(config())
diff --git a/kamon-core/src/main/scala/kamon/Status.scala b/kamon-core/src/main/scala/kamon/Status.scala
new file mode 100644
index 00000000..a5cfe868
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/Status.scala
@@ -0,0 +1,40 @@
+/* =========================================================================================
+ * Copyright © 2013-2019 the kamon project <https://kamon.io/>
+ *
+ * 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 kamon
+
+/**
+ * Exposes access to the Kamon's current status. The status information contains details about the internal state of
+ * several Kamon components and is exposed for the sole purpose of troubleshooting and debugging issues that might be
+ * related to Kamon.
+ *
+ * The Status APIs might change between minor versions.
+ */
+trait Status { self: ModuleLoading with Metrics with Configuration =>
+ private val _status = new kamon.status.Status(self._moduleRegistry, self._metricsRegistry, self)
+
+ /**
+ * Returns an accessor to Kamon's current status. The current status information is split into four main sections:
+ * - Settings: which include the Kamon version, environment and configuration being used.
+ * - Module Registry: Lists all modules that have been detected on the classpath and their current state.
+ * - Metric Registry: Lists all metrics currently registered in Kamon and all instruments belonging to them.
+ * - Instrumentation: Lists all instrumentation modules that have been detected and their current state.
+ *
+ * All information exposed by the Status API represents an immutable snapshot of the state at the moment the status
+ * was requested.
+ */
+ def status(): kamon.status.Status =
+ _status
+}
diff --git a/kamon-core/src/main/scala/kamon/StatusPage.scala b/kamon-core/src/main/scala/kamon/StatusPage.scala
deleted file mode 100644
index 1ac60c1b..00000000
--- a/kamon-core/src/main/scala/kamon/StatusPage.scala
+++ /dev/null
@@ -1,75 +0,0 @@
-package kamon
-
-import com.typesafe.config.Config
-import kamon.status.{Status, StatusPageServer}
-import org.slf4j.LoggerFactory
-
-import scala.util.{Failure, Success, Try}
-
-trait StatusPage { self: Configuration with ClassLoading with ModuleLoading with Metrics with Configuration =>
- private val _log = LoggerFactory.getLogger(classOf[StatusPage])
- @volatile private var _statusPageServer: Option[StatusPageServer] = None
- private val _status = new Status(self._moduleRegistry, self._metricsRegistry, self)
-
- // Initial configuration and reconfigures
- init(self.config())
- self.onReconfigure(newConfig => self.init(newConfig))
-
-
- /**
- * Allows to access internal Kamon status for troubleshooting and displaying purposes. All information returned
- * by the Status instance is a immutable snapshot of the current state of a given component.
- */
- def status(): Status =
- _status
-
-
- private def init(config: Config): Unit = synchronized {
- val isStatusPageEnabled = config.getBoolean("kamon.status.enabled")
-
- if(isStatusPageEnabled) {
- val hostname = config.getString("kamon.status.listen.hostname")
- val port = config.getInt("kamon.status.listen.port")
-
- _statusPageServer.fold {
- // Starting a new server on the configured hostname/port
- startServer(hostname, port, self.classLoader())
-
- }(existentServer => {
- // If the configuration has changed we will stop the previous version
- // and start a new one with the new hostname/port.
-
- if(existentServer.getHostname != hostname || existentServer.getListeningPort != port) {
- stopServer()
- startServer(hostname, port, self.classLoader())
- }
- })
-
- } else {
- _statusPageServer.foreach(_ => stopServer())
- }
- }
-
- private def startServer(hostname: String, port: Int, resourceLoader: ClassLoader): Unit = {
- Try {
-
- val server = new StatusPageServer(hostname, port, resourceLoader, _status)
- server.start()
- server
-
- } match {
- case Success(server) =>
- _log.info(s"Status page started on http://$hostname:$port/")
- _statusPageServer = Some(server)
-
- case Failure(t) =>
- _log.error("Failed to start the status page embedded server", t)
- }
- }
-
- private def stopServer(): Unit = {
- _statusPageServer.foreach(_.stop())
- _statusPageServer = None
- }
-
-}
diff --git a/kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala b/kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala
deleted file mode 100644
index 5ab0eb9f..00000000
--- a/kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala
+++ /dev/null
@@ -1,134 +0,0 @@
-package kamon.status
-
-import com.grack.nanojson.JsonWriter
-import java.lang.{StringBuilder => JavaStringBuilder}
-
-import com.typesafe.config.ConfigRenderOptions
-import kamon.module.Module
-
-import scala.collection.JavaConverters.{iterableAsScalaIterableConverter, mapAsScalaMapConverter}
-
-
-trait JsonMarshalling[T] {
-
- /**
- * Implementations should append a Json object or array that describes the given instance members and any
- * additional information that is expected to be shown in the status mini site.
- */
- def toJson(instance: T, builder: JavaStringBuilder): Unit
-}
-
-object JsonMarshalling {
-
- implicit object ModuleRegistryStatusJsonMarshalling extends JsonMarshalling[Status.ModuleRegistry] {
- override def toJson(instance: Status.ModuleRegistry, builder: JavaStringBuilder): Unit = {
- def moduleKindString(moduleKind: Module.Kind): String = moduleKind match {
- case Module.Kind.Combined => "combined"
- case Module.Kind.Metric => "metric"
- case Module.Kind.Span => "span"
- case Module.Kind.Plain => "plain"
- }
-
- val array = JsonWriter.on(builder)
- .`object`()
- .array("modules")
-
- instance.modules.foreach(m => {
- array.`object`()
- .value("name", m.name)
- .value("description", m.description)
- .value("clazz", m.clazz)
- .value("kind", moduleKindString(m.kind))
- .value("programmaticallyRegistered", m.programmaticallyRegistered)
- .value("enabled", m.enabled)
- .value("started", m.started)
- .end()
- })
-
- array.end().end().done()
- }
- }
-
- implicit object BaseInfoJsonMarshalling extends JsonMarshalling[Status.Settings] {
- override def toJson(instance: Status.Settings, builder: JavaStringBuilder): Unit = {
- val baseConfigJson = JsonWriter.on(builder)
- .`object`()
- .value("version", instance.version)
- .value("config", instance.config.root().render(ConfigRenderOptions.concise()))
-
- baseConfigJson.`object`("environment")
- .value("service", instance.environment.service)
- .value("host", instance.environment.host)
- .value("instance", instance.environment.instance)
- .`object`("tags")
-
- instance.environment.tags.foreach {
- case (key, value) => baseConfigJson.value(key, value)
- }
-
- baseConfigJson
- .end() // ends tags
- .end() // ends environment
- .end() // ends base config
- .done()
- }
- }
-
- implicit object MetricRegistryStatusJsonMarshalling extends JsonMarshalling[Status.MetricRegistry] {
- override def toJson(instance: Status.MetricRegistry, builder: JavaStringBuilder): Unit = {
- val metricsObject = JsonWriter.on(builder)
- .`object`
- .array("metrics")
-
- instance.metrics.foreach(metric => {
- metricsObject
- .`object`()
- .value("name", metric.name)
- .value("type", metric.instrumentType.name)
- .value("unitDimension", metric.unit.dimension.name)
- .value("unitMagnitude", metric.unit.magnitude.name)
- .`object`("tags")
-
- metric.tags.foreach { case (tag, value) => metricsObject.value(tag, value) }
-
- metricsObject
- .end() // tags
- .end() // metric info
- })
-
- metricsObject
- .end() // metrics array
- .end() // object
- .done()
- }
- }
-
- implicit object InstrumentationStatusJsonMarshalling extends JsonMarshalling[Status.Instrumentation] {
- override def toJson(instance: Status.Instrumentation, builder: JavaStringBuilder): Unit = {
- val instrumentationObject = JsonWriter.on(builder)
- .`object`()
- .value("active", instance.active)
- .`object`("modules")
-
- instance.modules.asScala.foreach {
- case (moduleName, moduleDescription) => instrumentationObject.value(moduleName, moduleDescription)
- }
-
- instrumentationObject
- .end() // end modules
- .`object`("errors")
-
- instance.errors.asScala.foreach {
- case (moduleName, errors) =>
- instrumentationObject.array(moduleName)
- errors.asScala.foreach(t => instrumentationObject.value(t.toString))
- instrumentationObject.end()
- }
-
- instrumentationObject
- .end() // errors
- .end() // object
- .done()
- }
- }
-} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/status/Status.scala b/kamon-core/src/main/scala/kamon/status/Status.scala
index ef5bb8eb..5c6d7cb0 100644
--- a/kamon-core/src/main/scala/kamon/status/Status.scala
+++ b/kamon-core/src/main/scala/kamon/status/Status.scala
@@ -22,7 +22,7 @@ class Status(_moduleRegistry: ModuleRegistry, _metricRegistry: MetricRegistry, c
/**
* Status of the module registry. Describes what modules have been detected and registered, either from the classpath
- * or programatically and their current status.
+ * or programmatically and their current status.
*/
def moduleRegistry(): Status.ModuleRegistry =
_moduleRegistry.status()
@@ -35,14 +35,10 @@ class Status(_moduleRegistry: ModuleRegistry, _metricRegistry: MetricRegistry, c
/**
- * PRIVATE API.
- *
- * Status of instrumentation modules that have been detected and/or loaded into the current JVM. This
- * API is not meant to be used by the general public.
- *
- * Read the [[Status.Instrumentation]] companion object's docs for more information.
+ * Status of instrumentation modules that have been detected and/or loaded into the current JVM. Read the
+ * [[Status.Instrumentation]] companion object's docs for more information.
*/
- private[kamon] def instrumentation(): Status.Instrumentation = {
+ def instrumentation(): Status.Instrumentation = {
import Status.Instrumentation._
Status.Instrumentation(
diff --git a/kamon-core/src/main/scala/kamon/status/StatusPageServer.scala b/kamon-core/src/main/scala/kamon/status/StatusPageServer.scala
deleted file mode 100644
index b2c7ff74..00000000
--- a/kamon-core/src/main/scala/kamon/status/StatusPageServer.scala
+++ /dev/null
@@ -1,113 +0,0 @@
-package kamon.status
-
-import java.io.InputStream
-
-import fi.iki.elonen.NanoHTTPD
-import fi.iki.elonen.NanoHTTPD.Response.{Status => StatusCode}
-import java.util.Collections
-import java.util.concurrent.{ExecutorService, Executors}
-
-import scala.collection.JavaConverters.asScalaBufferConverter
-
-class StatusPageServer(hostname: String, port: Int, resourceLoader: ClassLoader, status: Status)
- extends NanoHTTPD(hostname, port) {
-
- private val RootResourceDirectory = "status"
- private val ResourceExtensionRegex = ".*\\.([a-zA-Z0-9]*)".r
-
- override def serve(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response = {
- if(session.getMethod() == NanoHTTPD.Method.GET) {
- if(session.getUri().startsWith("/status")) {
-
- // Serve the current status data on Json.
- session.getUri() match {
- case "/status/settings" => json(status.settings())
- case "/status/modules" => json(status.moduleRegistry())
- case "/status/metrics" => json(status.metricRegistry())
- case "/status/instrumentation" => json(status.instrumentation())
- case _ => NotFound
- }
-
- } else {
-
- // Serve resources from the status page folder.
- val requestedResource = if (session.getUri() == "/") "/index.html" else session.getUri()
- val resourcePath = RootResourceDirectory + requestedResource
- val resourceStream = resourceLoader.getResourceAsStream(resourcePath)
-
- if (resourceStream == null) NotFound else resource(requestedResource, resourceStream)
- }
-
- } else NotAllowed
- }
-
- override def start(): Unit = {
- setAsyncRunner(new ThreadPoolRunner(Executors.newFixedThreadPool(2)))
- start(NanoHTTPD.SOCKET_READ_TIMEOUT, false)
- }
-
- private def mimeType(resource: String): String = {
- val ResourceExtensionRegex(resourceExtension) = resource
- resourceExtension match {
- case "css" => "text/css"
- case "js" => "application/javascript"
- case "ico" => "image/x-icon"
- case "svg" => "image/svg+xml"
- case "html" => "text/html"
- case "woff2" => "font/woff2"
- case _ => "text/plain"
- }
- }
-
- private def json[T](instance: T)(implicit marshalling: JsonMarshalling[T]) = {
- val builder = new java.lang.StringBuilder()
- marshalling.toJson(instance, builder)
-
- val response = NanoHTTPD.newFixedLengthResponse(StatusCode.OK, "application/json", builder.toString())
- response.closeConnection(true)
- response
- }
-
- private def resource(name: String, stream: InputStream) = {
- val response = NanoHTTPD.newChunkedResponse(StatusCode.OK, mimeType(name), stream)
- response.closeConnection(true)
- response
- }
-
- private val NotAllowed = NanoHTTPD.newFixedLengthResponse(
- StatusCode.METHOD_NOT_ALLOWED,
- NanoHTTPD.MIME_PLAINTEXT,
- "Only GET requests are allowed."
- )
-
- private val NotFound = NanoHTTPD.newFixedLengthResponse(
- StatusCode.NOT_FOUND,
- NanoHTTPD.MIME_PLAINTEXT,
- "The requested resource was not found."
- )
-
- // Closing the connections will ensure that the thread pool will not be exhausted by keep alive
- // connections from the browsers.
- NotAllowed.closeConnection(true)
- NotFound.closeConnection(true)
-
-
- /**
- * AsyncRunner that uses a thread pool for handling requests rather than spawning a new thread for each request (as
- * the default runner does).
- */
- private class ThreadPoolRunner(executorService: ExecutorService) extends NanoHTTPD.AsyncRunner {
- final private val _openRequests = Collections.synchronizedList(new java.util.LinkedList[NanoHTTPD#ClientHandler]())
-
- override def closeAll(): Unit =
- _openRequests.asScala.foreach(_.close())
-
- override def closed(clientHandler: NanoHTTPD#ClientHandler): Unit =
- _openRequests.remove(clientHandler)
-
- override def exec(clientHandler: NanoHTTPD#ClientHandler): Unit = {
- executorService.submit(clientHandler)
- _openRequests.add(clientHandler)
- }
- }
-} \ No newline at end of file