aboutsummaryrefslogtreecommitdiff
path: root/kamon-core/src/main/scala/kamon/http/HttpServerMetrics.scala
blob: 0dd189f683ae848871eabc5c242a5a072e1c1415 (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
package kamon.http

import akka.actor.ActorSystem
import com.typesafe.config.Config
import kamon.metric.instrument.Counter
import kamon.metric._

import scala.collection.concurrent.TrieMap

object HttpServerMetrics extends MetricGroupIdentity {
  import Metrics.AtomicGetOrElseUpdateForTriemap

  val name: String = "http-server-metrics-recorder"
  val category = new MetricGroupCategory {
    val name: String = "http-server"
  }

  type TraceName = String
  type StatusCode = String

  case class CountPerStatusCode(statusCode: String) extends MetricIdentity {
    def name: String = statusCode
  }

  case class TraceCountPerStatus(traceName: TraceName, statusCode: StatusCode) extends MetricIdentity {
    def name: String = traceName + "_" + statusCode
  }

  class HttpServerMetricsRecorder extends MetricGroupRecorder {

    private val counters = TrieMap[StatusCode, Counter]()
    private val countersPerTrace = TrieMap[TraceName, TrieMap[StatusCode, Counter]]()

    def recordResponse(statusCode: StatusCode): Unit = recordResponse(statusCode, 1L)

    def recordResponse(statusCode: StatusCode, count: Long): Unit =
      counters.atomicGetOrElseUpdate(statusCode, Counter()).increment(count)

    def recordResponse(traceName: TraceName, statusCode: StatusCode): Unit = recordResponse(traceName, statusCode, 1L)

    def recordResponse(traceName: TraceName, statusCode: StatusCode, count: Long): Unit = {
      recordResponse(statusCode, count)
      countersPerTrace.atomicGetOrElseUpdate(traceName, TrieMap()).atomicGetOrElseUpdate(statusCode, Counter()).increment(count)
    }

    def collect(context: CollectionContext): HttpServerMetricsSnapshot = {
      val countsPerStatusCode = counters.map {
        case (statusCode, counter)  (statusCode, counter.collect(context))
      }.toMap

      val countsPerTraceAndStatus = countersPerTrace.map {
        case (traceName, countsPerStatus) 
          (traceName, countsPerStatus.map { case (statusCode, counter)  (statusCode, counter.collect(context)) }.toMap)
      }.toMap

      HttpServerMetricsSnapshot(countsPerStatusCode, countsPerTraceAndStatus)
    }

    def cleanup: Unit = {}
  }

  case class HttpServerMetricsSnapshot(countsPerStatusCode: Map[StatusCode, Counter.Snapshot],
      countsPerTraceAndStatusCode: Map[TraceName, Map[StatusCode, Counter.Snapshot]]) extends MetricGroupSnapshot {

    type GroupSnapshotType = HttpServerMetricsSnapshot

    def merge(that: HttpServerMetricsSnapshot, context: CollectionContext): HttpServerMetricsSnapshot = {
      val combinedCountsPerStatus = combineMaps(countsPerStatusCode, that.countsPerStatusCode)((l, r)  l.merge(r, context))
      val combinedCountsPerTraceAndStatus = combineMaps(countsPerTraceAndStatusCode, that.countsPerTraceAndStatusCode) {
        (leftCounts, rightCounts)  combineMaps(leftCounts, rightCounts)((l, r)  l.merge(r, context))
      }
      HttpServerMetricsSnapshot(combinedCountsPerStatus, combinedCountsPerTraceAndStatus)
    }

    def metrics: Map[MetricIdentity, MetricSnapshot] = {
      countsPerStatusCode.map {
        case (statusCode, count)  (CountPerStatusCode(statusCode), count)
      } ++ {
        for (
          (traceName, countsPerStatus)  countsPerTraceAndStatusCode;
          (statusCode, count)  countsPerStatus
        ) yield (TraceCountPerStatus(traceName, statusCode), count)
      }
    }
  }

  val Factory = HttpServerMetricGroupFactory
}

case object HttpServerMetricGroupFactory extends MetricGroupFactory {

  import HttpServerMetrics._

  type GroupRecorder = HttpServerMetricsRecorder

  def create(config: Config, system: ActorSystem): HttpServerMetricsRecorder =
    new HttpServerMetricsRecorder()

}