All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kamon.status.Environment.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013-2021 The Kamon Project 
 *
 * 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
package status

import java.net.InetAddress
import java.util.concurrent.ThreadLocalRandom
import com.typesafe.config.{Config, ConfigUtil}
import kamon.tag.TagSet
import kamon.util.HexCodec
import org.slf4j.LoggerFactory

/**
  * Describes the conditions on which the instrumented application is running. This information is typically used by
  * reporter modules to enrich the data before it is sent to external systems. Kamon will always try to create an
  * appropriate Environment instance based on the details found on the "kamon.environment" configuration settings and
  * then exposed through an instance of this class.
  *
  * Starting from a service name, Kamon will try to figure out how to create the Environment instance using:
  *
  *   - The "kamon.environment.host" setting as the hostname where this application is running. If the value "auto"
  *     (default) is present, Kamon will try to resolve the hostname automatically and use it.
  *
  *   - The "kamon.environment.instance" setting as the name of the this instance. Typically there will be several JVMs
  *     running the exact same service code on different hosts and even though they should share the same "service"
  *     name, each one should have its own identifier. If the value "auto" (default) is present, Kamon will generate an
  *     instance name with the pattern "service@host".
  *
  *   - The "kamon.environment.tags" are additional key/value pairs with information about the conditions on which this
  *     service is running. They will typically be used to include things like the Datacenter name or region, software
  *     versions and so on.
  *
  * Finally, the incarnation is a randomly generated string that uniquely identifies this JVM. The incarnation value
  * remains the same even after Kamon gets reconfigured and its host, service, instance or tags are changed.
  *
  */
case class Environment(host: String, service: String, instance: String, incarnation: String, tags: TagSet)

object Environment {

  private val _logger = LoggerFactory.getLogger(classOf[Environment])
  private val _incarnation = HexCodec.toLowerHex(ThreadLocalRandom.current().nextLong())

  /**
    * Creates an Environment instance from the provided configuration. All environment settings will be looked up on the
    * "kamon.environment" configuration path.
    */
  def from(config: Config): Environment = {
    val environmentConfig = config.getConfig("kamon.environment")
    val service = environmentConfig.getString("service")
    val tagsConfig = environmentConfig.getConfig("tags")
    val tags = flattenedTags(tagsConfig)

    val host = readValueOrGenerate(environmentConfig.getString("host"), generateHostname())
    val instance = readValueOrGenerate(environmentConfig.getString("instance"), s"$service@$host")

    Environment(host, service, instance, _incarnation, tags)
  }

  /**
    * Flattens all the configuration keys in the configuration so that we can have namespaced tag names
    * like `k8s.namespace.name` or nested configurations and they will still generate a flat `x.y.z=value`
    * set of tags.
    */
  private def flattenedTags(tagsConfig: Config): TagSet = {
    import scala.collection.JavaConverters._

    TagSet.from(
      tagsConfig.entrySet()
        .iterator()
        .asScala
        .map { e => ConfigUtil.splitPath(e.getKey).asScala.mkString(".") -> e.getValue.unwrapped().toString }
        .toMap
    )
  }

  private def generateHostname(): String = {
    try InetAddress.getLocalHost.getHostName()
    catch {
      case t: Throwable =>
        _logger.warn("Could not automatically resolve a host name for this instance, falling back to 'localhost'", t)
        "localhost"
    }
  }

  private def readValueOrGenerate(configuredValue: String, generator: => String): String =
    if (configuredValue == "auto") generator else configuredValue
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy