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()
}
|