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

com.netflix.atlas.eval.graph.DefaultSettings.scala Maven / Gradle / Ivy

/*
 * Copyright 2014-2024 Netflix, Inc.
 *
 * 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 com.netflix.atlas.eval.graph

import java.util.concurrent.TimeUnit
import java.util.regex.Pattern

import org.apache.pekko.http.scaladsl.model.ContentType
import com.netflix.atlas.chart.GraphEngine
import com.netflix.atlas.core.model.CustomVocabulary
import com.netflix.atlas.core.stacklang.Interpreter
import com.netflix.atlas.core.stacklang.Vocabulary
import com.netflix.atlas.core.util.Strings
import com.netflix.atlas.eval.util.HostRewriter
import com.typesafe.config.Config

import java.awt.Color

/**
  * Default settings to use when rendering a graph image.
  *
  * @param root
  *     The full config object for the app. Primarily used for constructing
  *     any custom vocabulary that is needed for the interpreter.
  * @param config
  *     The specific config block for graph settings. This is typically under
  *     `atlas.eval.graph`.
  */
case class DefaultSettings(root: Config, config: Config) {

  /**
    * Default step size to use for the chart. This should typically match the primary step
    * size of the underlying storage.
    */
  val stepSize: Long = config.getDuration("step", TimeUnit.MILLISECONDS)

  /**
    * Default start time for the chart. This value should typically be relative to the
    * end time.
    */
  val startTime: String = config.getString("start-time")

  /** Default end time for the chart. This value should typically be relative to `now`. */
  val endTime: String = config.getString("end-time")

  /** Default time zone for the chart. */
  val timezone: String = config.getString("timezone")

  /** Default width for the chart. */
  val width: Int = config.getInt("width")

  /** Default height for the chart. */
  val height: Int = config.getInt("height")

  /** Default theme to use for the chart. */
  val theme: String = config.getString("theme")

  private def themeConfig(theme: String): Config = {
    if (config.hasPath(theme))
      config.getConfig(theme)
    else
      throw new IllegalArgumentException(s"invalid theme: $theme")
  }

  /** Default palette name to use. */
  def primaryPalette(theme: String): String = themeConfig(theme).getString("palette.primary")

  /** Default palette name to use for lines with an offset. */
  def offsetPalette(theme: String): String = themeConfig(theme).getString(s"palette.offset")

  /** Resolve color for a given theme. */
  def resolveColor(theme: String, color: String): Color = {
    val k = s"$theme.named-colors.$color"
    Strings.parseColor(if (config.hasPath(k)) config.getString(k) else color)
  }

  /** Should the uri and other graph metadata be encoded as text fields in the image? */
  val metadataEnabled: Boolean = config.getBoolean("png-metadata-enabled")

  /** Pattern to use for detecting if a user-agent is a web-browser. */
  val browserAgentPattern: Pattern = {
    Pattern.compile(config.getString("browser-agent-pattern"), Pattern.CASE_INSENSITIVE)
  }

  /** Should the system try to generate a simplified legend? */
  val simpleLegendsEnabled: Boolean = config.getBoolean("simple-legends-enabled")

  /** Maximum number of datapoints allowed for a line in a chart. */
  val maxDatapoints: Int = config.getInt("max-datapoints")

  /** Available engines for rendering a chart. */
  val engines: Map[String, GraphEngine] = {
    import scala.jdk.CollectionConverters.*
    config
      .getStringList("engines")
      .asScala
      .toList
      .map { cname =>
        val e = newInstance[GraphEngine](cname)
        e.name -> e
      }
      .toMap
  }

  /** Content types for the various rendering options. */
  val contentTypes: Map[String, ContentType] = engines.map {
    case (k, e) =>
      k -> ContentType.parse(e.contentType).toOption.get
  }

  /** Vocabulary to use in the interpreter when evaluating a graph expression. */
  val graphVocabulary: Vocabulary = {
    config.getString("vocabulary") match {
      case "default" => new CustomVocabulary(root)
      case cls       => newInstance[Vocabulary](cls)
    }
  }

  /** Interpreter for the graph expressions. */
  val interpreter: Interpreter = Interpreter(graphVocabulary.allWords)

  private def newInstance[T](cls: String): T = {
    Class.forName(cls).getDeclaredConstructor().newInstance().asInstanceOf[T]
  }

  /** Host rewriter for restricting the expressions based on how the service was accessed. */
  val hostRewriter = new HostRewriter(root.getConfig("atlas.eval.host-rewrite"))
}

object DefaultSettings {

  def apply(root: Config): DefaultSettings = {
    DefaultSettings(root, root.getConfig("atlas.eval.graph"))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy