From 860c568db90699ea58ef13c5a5cdc08ccc3dbb07 Mon Sep 17 00:00:00 2001 From: Ivan Topolnjak Date: Wed, 23 Jul 2014 15:08:59 -0300 Subject: + core,spray: create and implement http server metrics for spray, related to #56 --- .../main/scala/kamon/http/HttpServerMetrics.scala | 92 ++++++++++++++++++++++ .../src/main/scala/kamon/metric/TraceMetrics.scala | 2 +- 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 kamon-core/src/main/scala/kamon/http/HttpServerMetrics.scala (limited to 'kamon-core/src/main/scala') diff --git a/kamon-core/src/main/scala/kamon/http/HttpServerMetrics.scala b/kamon-core/src/main/scala/kamon/http/HttpServerMetrics.scala new file mode 100644 index 00000000..3773e7d8 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/http/HttpServerMetrics.scala @@ -0,0 +1,92 @@ +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 { + 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.getOrElseUpdate(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.getOrElseUpdate(traceName, TrieMap()).getOrElseUpdate(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 = new MetricGroupFactory { + type GroupRecorder = HttpServerMetricsRecorder + + def create(config: Config, system: ActorSystem): HttpServerMetricsRecorder = + new HttpServerMetricsRecorder() + } + +} \ No newline at end of file diff --git a/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala b/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala index 1ee1eab4..187eb07d 100644 --- a/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala +++ b/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala @@ -54,7 +54,7 @@ object TraceMetrics extends MetricGroupCategory { type GroupSnapshotType = TraceMetricsSnapshot def merge(that: TraceMetricsSnapshot, context: CollectionContext): TraceMetricsSnapshot = - TraceMetricsSnapshot(elapsedTime.merge(that.elapsedTime, context), Map.empty) + TraceMetricsSnapshot(elapsedTime.merge(that.elapsedTime, context), Map.empty) // TODO: Merge the segments metrics correctly and test it! def metrics: Map[MetricIdentity, MetricSnapshot] = segments + (ElapsedTime -> elapsedTime) } -- cgit v1.2.3