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

akka.monitor.instrumentation.ActorMonitor.scala Maven / Gradle / Ivy

The newest version!
/*
 * =========================================================================================
 * Copyright © 2017,2018 Workday, Inc.
 * Copyright © 2013-2017 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 akka.monitor.instrumentation

import java.util.concurrent.TimeUnit

import org.aspectj.lang.ProceedingJoinPoint
import org.slf4j.LoggerFactory

import akka.actor.{ActorRef, ActorSystem, Cell}
import akka.monitor.instrumentation.ActorMonitors.{TrackedActor, TrackedRoutee}
import io.kontainers.micrometer.akka._

trait ActorMonitor {
  def captureEnvelopeContext(): EnvelopeContext
  def processMessage(pjp: ProceedingJoinPoint, envelopeContext: EnvelopeContext): AnyRef
  def processFailure(failure: Throwable): Unit
  def cleanup(): Unit
}

object ActorMonitor {

  def createActorMonitor(cell: Cell, system: ActorSystem, ref: ActorRef, parent: ActorRef, actorCellCreation: Boolean): ActorMonitor = {
    val cellInfo = CellInfo.cellInfoFor(cell, system, ref, parent, actorCellCreation)

    if (cellInfo.isRouter)
      ActorMonitors.ContextPropagationOnly
    else {
      if (cellInfo.isRoutee && cellInfo.isTracked)
        createRouteeMonitor(cellInfo)
      else
        createRegularActorMonitor(cellInfo)
    }
  }

  def createRegularActorMonitor(cellInfo: CellInfo): ActorMonitor = {
    val actorMetrics = if (cellInfo.isTracked) ActorMetrics.metricsFor(cellInfo.entity) else None
    new TrackedActor(cellInfo.entity, cellInfo.actorSystemName, actorMetrics, cellInfo.trackingGroups, cellInfo.actorCellCreation)
  }

  def createRouteeMonitor(cellInfo: CellInfo): ActorMonitor = {
    RouterMetrics.metricsFor(cellInfo.entity) match {
      case Some(rm) =>
        new TrackedRoutee(cellInfo.entity, cellInfo.actorSystemName, rm, cellInfo.trackingGroups, cellInfo.actorCellCreation)
      case _ =>
        new TrackedActor(cellInfo.entity, cellInfo.actorSystemName, None, cellInfo.trackingGroups, cellInfo.actorCellCreation)
    }
  }
}

object ActorMonitors {

  val logger = LoggerFactory.getLogger(ActorMonitors.getClass)

  val ContextPropagationOnly = new ActorMonitor {
    def captureEnvelopeContext(): EnvelopeContext = EnvelopeContext()

    def processMessage(pjp: ProceedingJoinPoint, envelopeContext: EnvelopeContext): AnyRef = {
      pjp.proceed()
    }

    def processFailure(failure: Throwable): Unit = {}
    def cleanup(): Unit = {}
  }

  class TrackedActor(val entity: Entity, actorSystemName: String, actorMetrics: Option[ActorMetrics],
      trackingGroups: List[String], actorCellCreation: Boolean)
      extends GroupMetricsTrackingActor(entity, actorSystemName, trackingGroups, actorCellCreation) {

    if (logger.isDebugEnabled()) {
      logger.debug(s"tracking ${entity.name} actor: ${actorMetrics.isDefined} actor-group: ${trackingGroups}")
    }

    override def captureEnvelopeContext(): EnvelopeContext = {
      actorMetrics.foreach { am =>
        am.mailboxSize.increment()
        am.messages.increment()
      }
      super.captureEnvelopeContext()
    }

    def processMessage(pjp: ProceedingJoinPoint, envelopeContext: EnvelopeContext): AnyRef = {
      val timeInMailbox: Long = System.nanoTime() - envelopeContext.nanoTime

      val actorProcessingTimers = actorMetrics.map { am =>
        am.processingTime.startTimer()
      }
      val actorGroupProcessingTimers = trackingGroups.map { group =>
        ActorGroupMetrics.processingTime(group).startTimer()
      }

      try {
        pjp.proceed()
      } finally {
        actorProcessingTimers.foreach { _.close() }
        actorGroupProcessingTimers.foreach { _.close() }

        actorMetrics.foreach { am =>
          am.timeInMailbox.timer.record(timeInMailbox, TimeUnit.NANOSECONDS)
          am.mailboxSize.decrement()
        }
        recordGroupMetrics(timeInMailbox)
      }
    }

    override def processFailure(failure: Throwable): Unit = {
      actorMetrics.foreach { am =>
        am.errors.increment()
      }
      super.processFailure(failure)
    }
  }

  class TrackedRoutee(val entity: Entity, actorSystemName: String, routerMetrics: RouterMetrics,
      trackingGroups: List[String], actorCellCreation: Boolean)
      extends GroupMetricsTrackingActor(entity, actorSystemName, trackingGroups, actorCellCreation) {

    if (logger.isDebugEnabled()) {
      logger.debug(s"tracking ${entity.name} router: true actor-group: ${trackingGroups} actorCellCreation: ${actorCellCreation}")
    }

    override def captureEnvelopeContext(): EnvelopeContext = {
      routerMetrics.messages.increment()
      super.captureEnvelopeContext()
    }

    def processMessage(pjp: ProceedingJoinPoint, envelopeContext: EnvelopeContext): AnyRef = {
      val timeInMailbox: Long = System.nanoTime() - envelopeContext.nanoTime

      val processingTimer = routerMetrics.processingTime.startTimer()
      val actorGroupProcessingTimers = trackingGroups.map { group =>
        ActorGroupMetrics.processingTime(group).startTimer()
      }

      try {
        pjp.proceed()
      } finally {
        processingTimer.close()
        actorGroupProcessingTimers.foreach { _.close() }
        routerMetrics.timeInMailbox.timer.record(timeInMailbox, TimeUnit.NANOSECONDS)
        recordGroupMetrics(timeInMailbox)
      }
    }

    override def processFailure(failure: Throwable): Unit = {
      routerMetrics.errors.increment()
      super.processFailure(failure)
    }
  }

  abstract class GroupMetricsTrackingActor(entity: Entity, actorSystemName: String,
      trackingGroups: List[String], actorCellCreation: Boolean) extends ActorMonitor {
    if (actorCellCreation) {
      ActorSystemMetrics.actorCount(actorSystemName).increment()
      trackingGroups.foreach { group =>
        ActorGroupMetrics.actorCount(group).increment()
      }
    }

    def captureEnvelopeContext(): EnvelopeContext = {
      trackingGroups.foreach { group =>
        ActorGroupMetrics.mailboxSize(group).increment()
        ActorGroupMetrics.messages(group).increment()
      }
      EnvelopeContext()
    }

    protected def recordGroupMetrics(timeInMailbox: Long): Unit = {
      trackingGroups.foreach { group =>
        ActorGroupMetrics.timeInMailbox(group).timer.record(timeInMailbox, TimeUnit.NANOSECONDS)
        ActorGroupMetrics.mailboxSize(group).decrement()
      }
    }

    def processFailure(failure: Throwable): Unit = {
      trackingGroups.foreach { group =>
        ActorGroupMetrics.errors(group).increment()
      }
    }

    def cleanup(): Unit = {
      if (actorCellCreation) {
        ActorSystemMetrics.actorCount(actorSystemName).decrement()
        trackingGroups.foreach { group =>
          ActorGroupMetrics.actorCount(group).decrement()
        }
      }
    }

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy