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

com.azure.cosmos.spark.plugins.CosmosMetricsApplicationInsightsPlugin.scala Maven / Gradle / Ivy

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.cosmos.spark.plugins

import com.azure.cosmos.implementation.Strings
import com.azure.cosmos.spark.{CosmosClientMetrics, CosmosConfigNames, CosmosConstants}
import com.azure.cosmos.spark.diagnostics.BasicLoggingTrait
import com.microsoft.applicationinsights.TelemetryConfiguration
import io.micrometer.azuremonitor.{AzureMonitorConfig, AzureMonitorMeterRegistry}
import io.micrometer.core.instrument.{Clock, MeterRegistry}
import org.apache.spark.SparkContext
import org.apache.spark.api.plugin.{DriverPlugin, ExecutorPlugin, PluginContext, SparkPlugin}

import java.time.Duration
import java.util

// scalastyle:off underscore.import
import scala.collection.JavaConverters._
// scalastyle:on underscore.import

class CosmosMetricsApplicationInsightsPlugin extends SparkPlugin with BasicLoggingTrait {
  override def driverPlugin(): DriverPlugin = new CosmosMetricsApplicationInsightsDriverPlugin()

  override def executorPlugin(): ExecutorPlugin = new CosmosMetricsApplicationInsightsExecutorPlugin()

  private[this] def createAndAddRegistry(connectionString: String, metricsCollectionIntervalInSeconds: Long): MeterRegistry = {
    val config = new DefaultAzureMonitorConfig(metricsCollectionIntervalInSeconds)

    val telemetryConfig = TelemetryConfiguration
      .createDefault()
    telemetryConfig.setConnectionString(connectionString)

    val azureMonitorRegistry = AzureMonitorMeterRegistry
      .builder(config)
      .clock(Clock.SYSTEM)
      .telemetryConfiguration(telemetryConfig)
      .build()

    CosmosClientMetrics.addMeterRegistry(azureMonitorRegistry)

    azureMonitorRegistry
  }

  private[this] def cleanupMeterRegistry(meterRegistry: Option[MeterRegistry]): Unit = {
    meterRegistry match {
      case Some(existingRegistry) => CosmosClientMetrics.removeMeterRegistry(existingRegistry)
      case None =>
    }
  }

  private class CosmosMetricsApplicationInsightsDriverPlugin
    extends DriverPlugin
      with BasicLoggingTrait {

    private[this] var connectionString: String = _
    private[this] var metricsCollectionIntervalInSeconds = CosmosConstants.defaultMetricsIntervalInSeconds
    private[this] var meterRegistry: Option[MeterRegistry] = None

    override def init
    (
      sc: SparkContext,
      pluginContext: PluginContext
    ): java.util.Map[String, String] = {
      val originalConfig = super.init(sc, pluginContext).asScala

      metricsCollectionIntervalInSeconds = sc
        .getConf
        .getInt(CosmosConfigNames.MetricsIntervalInSeconds, CosmosConstants.defaultMetricsIntervalInSeconds)

      connectionString = sc
        .getConf
        .get(CosmosConfigNames.MetricsAzureMonitorConnectionString, null)

      if (Strings.isNullOrWhiteSpace(connectionString)) {
        throw new IllegalArgumentException(
          s"Azure monitor instrumentation key provided in '" +
            s"${CosmosConfigNames.MetricsAzureMonitorConnectionString}' is null or only " +
            s"contains whitespaces.'"
        )
      }

      logInfo(s"CosmosMetricsApplicationInsightsDriverPlugin initialized - metrics interval " +
        s"(seconds): $metricsCollectionIntervalInSeconds)")

      val newConfig = originalConfig +
        (CosmosConfigNames.MetricsAzureMonitorConnectionString -> connectionString) +
        (CosmosConfigNames.MetricsIntervalInSeconds -> metricsCollectionIntervalInSeconds.toString)

      newConfig.toMap.asJava
    }

    override def registerMetrics(appId: String, pluginContext: PluginContext): Unit = {
      super.registerMetrics(appId, pluginContext)

      this.meterRegistry = Some(createAndAddRegistry(connectionString, metricsCollectionIntervalInSeconds))

      logInfo(s"CosmosMetricsApplicationInsightsDriverPlugin metrics for application $appId " +
        s"registered (metrics interval (seconds): $metricsCollectionIntervalInSeconds)")
    }

    override def shutdown(): Unit = {
      super.shutdown()

      cleanupMeterRegistry(this.meterRegistry)

      logInfo("CosmosMetricsApplicationInsightsDriverPlugin shutdown initiated")
    }
  }

  private class CosmosMetricsApplicationInsightsExecutorPlugin
    extends ExecutorPlugin
      with BasicLoggingTrait {

    private[this] var connectionString: String = _
    private[this] var metricsCollectionIntervalInSeconds = CosmosConstants.defaultMetricsIntervalInSeconds
    private[this] var meterRegistry: Option[MeterRegistry] = None

    override def init(ctx: PluginContext, extraConf: util.Map[String, String]): Unit = {
      super.init(ctx, extraConf)

      if (Option(extraConf).isDefined &&
        extraConf.containsKey(CosmosConfigNames.MetricsAzureMonitorConnectionString)) {

        connectionString = extraConf.get(CosmosConfigNames.MetricsAzureMonitorConnectionString)
      }

      if (Option(extraConf).isDefined &&
        extraConf.containsKey(CosmosConfigNames.MetricsIntervalInSeconds)) {

        metricsCollectionIntervalInSeconds = extraConf.get(CosmosConfigNames.MetricsIntervalInSeconds).toInt
      }

      this.meterRegistry = Some(
        createAndAddRegistry(connectionString, metricsCollectionIntervalInSeconds))

      logInfo(s"CosmosMetricsApplicationInsightsExecutorPlugin metrics " +
        s"registered (metrics interval (seconds): $metricsCollectionIntervalInSeconds)")
    }

    override def shutdown(): Unit = {
      super.shutdown()

      cleanupMeterRegistry(this.meterRegistry)
      logInfo("CosmosMetricsApplicationInsightsExecutorPlugin shutdown initiated")
    }
  }

  private class DefaultAzureMonitorConfig
  (
    val intervalInSeconds: Long
  ) extends AzureMonitorConfig {

    override def get(s: String): String = {
      null
    }

    override def step(): Duration = {
      Duration.ofSeconds(intervalInSeconds)
    }

    override def instrumentationKey(): String = {
      null
    }

    override def enabled(): Boolean = {
      true
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy