From e83babb5f88e91661bec2f1013fcb6b03612bea9 Mon Sep 17 00:00:00 2001 From: Ivan Topolnjak Date: Tue, 29 Jul 2014 00:25:26 -0300 Subject: + logreporter: new log reporter for trace, actor and user metrics, closes #57 The new kamon-log-reporter module subscribes itselfs for trace, actor and user metrics and prints the values using ActorLogging in a pretty ascii table format. --- .../main/scala/kamon/logreporter/LogReporter.scala | 227 +++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala (limited to 'kamon-log-reporter/src/main/scala') diff --git a/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala b/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala new file mode 100644 index 00000000..b0cc2551 --- /dev/null +++ b/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala @@ -0,0 +1,227 @@ +/* + * ========================================================================================= + * Copyright © 2013-2014 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 kamon.logreporter + +import akka.actor._ +import kamon.Kamon +import kamon.metric.ActorMetrics.ActorMetricSnapshot +import kamon.metric.Subscriptions.TickMetricSnapshot +import kamon.metric.TraceMetrics.TraceMetricsSnapshot +import kamon.metric.UserMetrics.{ UserCounter, UserMetricsSnapshot } +import kamon.metric.instrument.{ Counter, Histogram } +import kamon.metric._ + +object LogReporter extends ExtensionId[LogReporterExtension] with ExtensionIdProvider { + override def lookup(): ExtensionId[_ <: Extension] = LogReporter + override def createExtension(system: ExtendedActorSystem): LogReporterExtension = new LogReporterExtension(system) + + trait MetricKeyGenerator { + def localhostName: String + def normalizedLocalhostName: String + def generateKey(groupIdentity: MetricGroupIdentity, metricIdentity: MetricIdentity): String + } +} + +class LogReporterExtension(system: ExtendedActorSystem) extends Kamon.Extension { + val subscriber = system.actorOf(Props[LogReporterSubscriber], "kamon-log-reporter") + Kamon(Metrics)(system).subscribe(TraceMetrics, "*", subscriber, permanently = true) + Kamon(Metrics)(system).subscribe(ActorMetrics, "*", subscriber, permanently = true) + Kamon(Metrics)(system).subscribe(UserMetrics.category, "*", subscriber, permanently = true) + +} + +class LogReporterSubscriber extends Actor with ActorLogging { + import LogReporterSubscriber.RichHistogramSnapshot + + def receive = { + case tick: TickMetricSnapshot ⇒ printMetricSnapshot(tick) + } + + def printMetricSnapshot(tick: TickMetricSnapshot): Unit = tick.metrics foreach { + case (identity, ams: ActorMetricSnapshot) ⇒ logActorMetrics(identity.name, ams) + case (identity, tms: TraceMetricsSnapshot) ⇒ logTraceMetrics(identity.name, tms) + case (_, ums: UserMetricsSnapshot) ⇒ logUserMetrics(ums) + } + + def logActorMetrics(name: String, ams: ActorMetricSnapshot): Unit = { + log.info( + """ + |+--------------------------------------------------------------------------------------------------+ + || | + || Actor: %-83s | + || | + || Processing Time (nanoseconds) Time in Mailbox (nanoseconds) Mailbox Size | + || Msg Count: %-12s Msg Count: %-12s Min: %-8s | + || Min: %-12s Min: %-12s Avg.: %-8s | + || 50th Perc: %-12s 50th Perc: %-12s Max: %-8s | + || 90th Perc: %-12s 90th Perc: %-12s | + || 95th Perc: %-12s 95th Perc: %-12s | + || 99th Perc: %-12s 99th Perc: %-12s Error Count: %-6s | + || 99.9th Perc: %-12s 99.9th Perc: %-12s | + || Max: %-12s Max: %-12s | + || | + |+--------------------------------------------------------------------------------------------------+""" + .stripMargin.format( + name, + ams.processingTime.numberOfMeasurements, ams.timeInMailbox.numberOfMeasurements, ams.mailboxSize.min, + ams.processingTime.min, ams.timeInMailbox.min, ams.mailboxSize.average, + ams.processingTime.percentile(0.50F), ams.timeInMailbox.percentile(0.50F), ams.mailboxSize.max, + ams.processingTime.percentile(0.90F), ams.timeInMailbox.percentile(0.90F), + ams.processingTime.percentile(0.95F), ams.timeInMailbox.percentile(0.95F), + ams.processingTime.percentile(0.99F), ams.timeInMailbox.percentile(0.99F), ams.errors.count, + ams.processingTime.percentile(0.999F), ams.timeInMailbox.percentile(0.999F), + ams.processingTime.max, ams.timeInMailbox.max)) + } + + def logTraceMetrics(name: String, tms: TraceMetricsSnapshot): Unit = { + val traceMetricsData = StringBuilder.newBuilder + + traceMetricsData.append( + """ + |+--------------------------------------------------------------------------------------------------+ + || | + || Trace: %-83s | + || Count: %-8s | + || | + || Elapsed Time (nanoseconds): | + |""" + .stripMargin.format( + name, tms.elapsedTime.numberOfMeasurements)) + + traceMetricsData.append(compactHistogramView(tms.elapsedTime)) + traceMetricsData.append( + """ + || | + |+--------------------------------------------------------------------------------------------------+""" + .stripMargin) + + log.info(traceMetricsData.toString()) + } + + def logUserMetrics(ums: UserMetricsSnapshot): Unit = { + val userMetricsData = StringBuilder.newBuilder + + userMetricsData.append( + """ + |+--------------------------------------------------------------------------------------------------+ + || | + || User Counters | + || ------------- | + |""".stripMargin) + + ums.counters.toList.sortBy(_._1.name.toLowerCase).foreach { + case (counter, snapshot) ⇒ userMetricsData.append(userCounterString(counter, snapshot)) + } + + userMetricsData.append( + """|| | + || | + || User Histograms | + || --------------- | + |""".stripMargin) + + ums.histograms.foreach { + case (histogram, snapshot) ⇒ + userMetricsData.append("| %-40s |\n".format(histogram.name)) + userMetricsData.append(compactHistogramView(snapshot)) + userMetricsData.append("\n| |\n") + } + + userMetricsData.append( + """|| | + || User MinMaxCounters | + || ------------------- | + |""".stripMargin) + + ums.minMaxCounters.foreach { + case (minMaxCounter, snapshot) ⇒ + userMetricsData.append("| %-40s |\n".format(minMaxCounter.name)) + userMetricsData.append(simpleHistogramView(snapshot)) + userMetricsData.append("\n| |\n") + } + + userMetricsData.append( + """|| | + || User Gauges | + || ----------- | + |""" + .stripMargin) + + ums.gauges.foreach { + case (gauge, snapshot) ⇒ + userMetricsData.append("| %-40s |\n".format(gauge.name)) + userMetricsData.append(simpleHistogramView(snapshot)) + userMetricsData.append("\n| |\n") + } + + userMetricsData.append( + """|| | + |+--------------------------------------------------------------------------------------------------+""" + .stripMargin) + + log.info(userMetricsData.toString()) + } + + def userCounterString(counter: UserCounter, snapshot: Counter.Snapshot): String = { + "| %30s => %-12s |\n" + .format(counter.name, snapshot.count) + } + + def compactHistogramView(histogram: Histogram.Snapshot): String = { + val sb = StringBuilder.newBuilder + + sb.append("| Min: %-11s 50th Perc: %-12s 90th Perc: %-12s 95th Perc: %-12s |\n".format( + histogram.min, histogram.percentile(0.50F), histogram.percentile(0.90F), histogram.percentile(0.95F))) + sb.append("| 99th Perc: %-12s 99.9th Perc: %-12s Max: %-12s |".format( + histogram.percentile(0.99F), histogram.percentile(0.999F), histogram.max)) + + sb.toString() + } + + def simpleHistogramView(histogram: Histogram.Snapshot): String = + "| Min: %-12s Average: %-12s Max: %-12s |" + .format(histogram.min, histogram.average, histogram.max) +} + +object LogReporterSubscriber { + + implicit class RichHistogramSnapshot(histogram: Histogram.Snapshot) { + def percentile(q: Float): Long = { + val records = histogram.recordsIterator + val qThreshold = histogram.numberOfMeasurements * q + var countToCurrentLevel = 0L + var qLevel = 0L + + while (countToCurrentLevel < qThreshold && records.hasNext) { + val record = records.next() + countToCurrentLevel += record.count + qLevel = record.level + } + + qLevel + } + + def average: Double = { + var acc = 0L + for (record ← histogram.recordsIterator) { + acc += record.count * record.level + } + + return acc / histogram.numberOfMeasurements + } + } +} \ No newline at end of file -- cgit v1.2.3