diff options
Diffstat (limited to 'kamon-core/src/main/scala')
-rw-r--r-- | kamon-core/src/main/scala/kamon/Kamon.scala | 2 | ||||
-rw-r--r-- | kamon-core/src/main/scala/kamon/Status.scala | 40 | ||||
-rw-r--r-- | kamon-core/src/main/scala/kamon/StatusPage.scala | 75 | ||||
-rw-r--r-- | kamon-core/src/main/scala/kamon/status/JsonMarshalling.scala | 134 | ||||
-rw-r--r-- | kamon-core/src/main/scala/kamon/status/Status.scala | 12 | ||||
-rw-r--r-- | kamon-core/src/main/scala/kamon/status/StatusPageServer.scala | 113 |
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 |