aboutsummaryrefslogtreecommitdiff
path: root/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelic.scala
blob: 2ee7ada071ddefd7add28e5c06fc720d08809dd9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package kamon.newrelic

import akka.actor._
import scala.collection.mutable
import kamon.Kamon
import kamon.trace.{UowTrace, Trace}
import kamon.newrelic.NewRelicMetric.{MetricBatch, FlushMetrics}
import scala.concurrent.duration._

class NewRelic extends ExtensionId[NewRelicExtension] {
  def createExtension(system: ExtendedActorSystem): NewRelicExtension = new NewRelicExtension(system)
}

class NewRelicExtension(system: ExtendedActorSystem) extends Kamon.Extension {
  val manager: ActorRef = system.actorOf(Props[NewRelicManager], "kamon-newrelic")
}

class NewRelicManager extends Actor with ActorLogging {
  log.info("Registering the Kamon(NewRelic) extension")

  Kamon(Trace)(context.system) ! Trace.Register



  val webTransactionMetrics = context.actorOf(Props[WebTransactionMetrics], "web-transaction-metrics")
  val agent = context.actorOf(Props[Agent], "agent")

  import context.dispatcher
  context.system.scheduler.schedule(1 minute, 1 minute) {
    webTransactionMetrics.tell(FlushMetrics, agent)
  }

  def receive = {
    case trace: UowTrace => webTransactionMetrics ! trace
  }
}

object NewRelicMetric {
  case class ID(name: String, scope: Option[String])
  case class Data(var total: Double, var totalExclusive: Double, var min: Double, var max: Double, var sumOfSquares: Double, var callCount: Long) {
    def record(value: Double): Unit = {
      if(value > max) max = value
      if(value < min) min = value

      total += value
      totalExclusive += value
      sumOfSquares += value * value
      callCount += 1
    }
  }

  object Data {
    def apply(): Data = Data(0, 0, 0, 0, 0, 0)
  }

  case object FlushMetrics
  case class MetricBatch(metrics: List[(ID, Data)])
}


class WebTransactionMetrics extends Actor with ActorLogging {
  val apdexT = 1500000000
  var metrics = mutable.Map.empty[NewRelicMetric.ID, NewRelicMetric.Data]
  var apdex = NewRelicMetric.Data(0, 0, 0, apdexT, apdexT, 0)

  def receive = {
    case trace: UowTrace  => updateStats(trace)
    case FlushMetrics     => flush
  }

  def flush: Unit = {
    sender ! MetricBatch(metrics.toList :+ (NewRelicMetric.ID("Apdex", None), apdex))
    apdex = NewRelicMetric.Data(0, 0, 0, apdexT, apdexT, 0)
    metrics = mutable.Map.empty[NewRelicMetric.ID, NewRelicMetric.Data]
  }

  def recordValue(metricID: NewRelicMetric.ID, value: Double): Unit = {
    metrics.getOrElseUpdate(metricID, NewRelicMetric.Data()).record(value)
  }

  def recordApdex(time: Double): Unit = {
    if(time <= apdexT)
      apdex.total += 1
    else
      if(time > apdexT && time <= (4 * apdexT))
        apdex.totalExclusive += 1
      else
        apdex.min += 1

  }

  def updateStats(trace: UowTrace): Unit = {
    // Basic Metrics
    recordApdex(trace.elapsed)
    recordValue(NewRelicMetric.ID("WebTransaction", None), trace.elapsed)
    recordValue(NewRelicMetric.ID("HttpDispatcher", None), trace.elapsed)
    recordValue(NewRelicMetric.ID("WebTransaction/Custom/" + trace.name, None), trace.elapsed)

    println("Recorded Apdex: " + apdex)
    println("Current Metrics: " + metrics.mkString("\n"))
  }
}