PageRenderTime 65ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/geomesa-metrics/src/main/scala/org/locationtech/geomesa/metrics/reporters/DelimitedFileReporter.scala

https://gitlab.com/zachcoyle/geomesa
Scala | 246 lines | 196 code | 35 blank | 15 comment | 10 complexity | 24a3584421382336b2d0d952bb56ff7f MD5 | raw file
  1. /***********************************************************************
  2. * Copyright (c) 2013-2016 Commonwealth Computer Research, Inc.
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Apache License, Version 2.0
  5. * which accompanies this distribution and is available at
  6. * http://www.opensource.org/licenses/apache2.0.php.
  7. *************************************************************************/
  8. package org.locationtech.geomesa.metrics.reporters
  9. import java.io._
  10. import java.util.Locale
  11. import java.util.concurrent.TimeUnit
  12. import com.codahale.metrics._
  13. import org.apache.commons.csv.{CSVFormat, CSVPrinter}
  14. import org.joda.time.format.DateTimeFormat
  15. import scala.language.implicitConversions
  16. /**
  17. * Reporter for dropwizard metrics that will write to delimited files. In contrast to the
  18. * dropwizard CSVReporter, allows for either CSV or TSV files and for aggregating metrics by type.
  19. */
  20. object DelimitedFileReporter {
  21. val gaugeCols = Array(("value", "%s"))
  22. val counterCols = Array(("count", "%d"))
  23. val histogramCols = Array(
  24. ("count", "%d"),
  25. ("min", "%d"),
  26. ("max", "%d"),
  27. ("mean", "%2.2f"),
  28. ("stdDev", "%2.2f"),
  29. ("median", "%2.2f"),
  30. ("75thPercentile", "%2.2f"),
  31. ("95thPercentile", "%2.2f"),
  32. ("98thPercentile", "%2.2f"),
  33. ("99thPercentile", "%2.2f"),
  34. ("999thPercentile", "%2.2f")
  35. )
  36. val meterCols = Array(
  37. ("count", "%d"),
  38. ("meanRate", "%2.2f"),
  39. ("oneMinuteRate", "%2.2f"),
  40. ("fiveMinuteRate", "%2.2f"),
  41. ("fifteenMinuteRate", "%2.2f"),
  42. ("rateUnit", "events/%s")
  43. )
  44. val timerCols = Array(
  45. ("count", "%d"),
  46. ("min", "%2.2f"),
  47. ("max", "%2.2f"),
  48. ("mean", "%2.2f"),
  49. ("stdDev", "%2.2f"),
  50. ("median", "%2.2f"),
  51. ("75thPercentile", "%2.2f"),
  52. ("95thPercentile", "%2.2f"),
  53. ("98thPercentile", "%2.2f"),
  54. ("99thPercentile", "%2.2f"),
  55. ("999thPercentile", "%2.2f"),
  56. ("meanRate", "%2.2f"),
  57. ("oneMinuteRate", "%2.2f"),
  58. ("fiveMinuteRate", "%2.2f"),
  59. ("fifteenMinuteRate", "%2.2f"),
  60. ("rateUnit", "calls/%s"),
  61. ("durationUnit", "%s")
  62. )
  63. def forRegistry(registry: MetricRegistry): Builder = new Builder(registry)
  64. class Builder private[metrics] (registry: MetricRegistry) {
  65. private var locale: Locale = Locale.getDefault
  66. private var rateUnit: TimeUnit = TimeUnit.SECONDS
  67. private var durationUnit: TimeUnit = TimeUnit.MILLISECONDS
  68. private var clock: Clock = Clock.defaultClock
  69. private var filter: MetricFilter = MetricFilter.ALL
  70. private var aggregate: Boolean = true
  71. private var format: CSVFormat = CSVFormat.TDF
  72. def formatFor(locale: Locale): Builder = { this.locale = locale; this }
  73. def convertRatesTo(rateUnit: TimeUnit): Builder = { this.rateUnit = rateUnit; this }
  74. def convertDurationsTo(durationUnit: TimeUnit): Builder = { this.durationUnit = durationUnit; this }
  75. def withClock(clock: Clock): Builder = { this.clock = clock; this }
  76. def filter(filter: MetricFilter): Builder = { this.filter = filter; this }
  77. def aggregate(aggregate: Boolean): Builder = { this.aggregate = aggregate; this }
  78. def withCommas(): Builder = { this.format = CSVFormat.DEFAULT; this }
  79. def withTabs(): Builder = { this.format = CSVFormat.TDF; this }
  80. def build(path: String): DelimitedFileReporter = {
  81. new DelimitedFileReporter(registry, path, aggregate, format, clock, locale, filter, rateUnit, durationUnit)
  82. }
  83. }
  84. }
  85. /**
  86. * Reporter for dropwizard metrics that will write to accumulo
  87. */
  88. class DelimitedFileReporter private (registry: MetricRegistry,
  89. path: String,
  90. aggregate: Boolean,
  91. format: CSVFormat,
  92. clock: Clock,
  93. locale: Locale,
  94. filter: MetricFilter,
  95. rateUnit: TimeUnit,
  96. durationUnit: TimeUnit)
  97. extends ScheduledReporter(registry, "DelimitedFileReporter", filter, rateUnit, durationUnit) {
  98. import DelimitedFileReporter._
  99. private val writers = scala.collection.mutable.Map.empty[String, CSVPrinter]
  100. private val folder = new File(path)
  101. if (folder.exists()) {
  102. require(folder.isDirectory, s"Path refers to a file - must be a folder: $path")
  103. } else {
  104. require(folder.mkdirs(), s"Can't create folder at $path")
  105. }
  106. private val timeEncoder = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").withZoneUTC()
  107. def flush(): Unit = writers.synchronized(writers.values.foreach(_.flush()))
  108. override def stop(): Unit = {
  109. writers.synchronized(writers.values.foreach { w => w.flush(); w.close() })
  110. super.stop()
  111. }
  112. override def report(gauges: java.util.SortedMap[String, Gauge[_]],
  113. counters: java.util.SortedMap[String, Counter],
  114. histograms: java.util.SortedMap[String, Histogram],
  115. meters: java.util.SortedMap[String, Meter],
  116. timers: java.util.SortedMap[String, Timer]) = {
  117. import scala.collection.JavaConversions._
  118. lazy val timestamp = timeEncoder.print(clock.getTime)
  119. gauges.foreach { case (name, metric) => writeGauge(name, timestamp, metric) }
  120. counters.foreach { case (name, metric) => writeCounter(name, timestamp, metric) }
  121. histograms.foreach { case (name, metric) => writeHistogram(name, timestamp, metric) }
  122. meters.foreach { case (name, metric) => writeMeter(name, timestamp, metric) }
  123. timers.foreach { case (name, metric) => writeTimer(name, timestamp, metric) }
  124. }
  125. private def writeGauge(name: String, timestamp: String, gauge: Gauge[_]): Unit =
  126. write("gauges", gaugeCols, name, timestamp, Array[Any](gauge.getValue))
  127. private def writeCounter(name: String, timestamp: String, counter: Counter): Unit =
  128. write("counters", counterCols, name, timestamp, Array[Any](counter.getCount))
  129. private def writeHistogram(name: String, timestamp: String, histogram: Histogram): Unit = {
  130. val snapshot = histogram.getSnapshot
  131. val values = Array[Any](
  132. histogram.getCount,
  133. snapshot.getMin,
  134. snapshot.getMax,
  135. snapshot.getMean,
  136. snapshot.getStdDev,
  137. snapshot.getMedian,
  138. snapshot.get75thPercentile,
  139. snapshot.get95thPercentile,
  140. snapshot.get98thPercentile,
  141. snapshot.get99thPercentile,
  142. snapshot.get999thPercentile
  143. )
  144. write("histograms", histogramCols, name, timestamp, values)
  145. }
  146. private def writeMeter(name: String, timestamp: String, meter: Meter): Unit = {
  147. val values = Array[Any](
  148. meter.getCount,
  149. convertRate(meter.getMeanRate),
  150. convertRate(meter.getOneMinuteRate),
  151. convertRate(meter.getFiveMinuteRate),
  152. convertRate(meter.getFifteenMinuteRate),
  153. getRateUnit)
  154. write("meters", meterCols, name, timestamp, values)
  155. }
  156. private def writeTimer(name: String, timestamp: String, timer: Timer): Unit = {
  157. val snapshot = timer.getSnapshot
  158. val values = Array[Any](
  159. timer.getCount,
  160. convertDuration(snapshot.getMin),
  161. convertDuration(snapshot.getMax),
  162. convertDuration(snapshot.getMean),
  163. convertDuration(snapshot.getStdDev),
  164. convertDuration(snapshot.getMedian),
  165. convertDuration(snapshot.get75thPercentile),
  166. convertDuration(snapshot.get95thPercentile),
  167. convertDuration(snapshot.get98thPercentile),
  168. convertDuration(snapshot.get99thPercentile),
  169. convertDuration(snapshot.get999thPercentile),
  170. convertRate(timer.getMeanRate),
  171. convertRate(timer.getOneMinuteRate),
  172. convertRate(timer.getFiveMinuteRate),
  173. convertRate(timer.getFifteenMinuteRate),
  174. getRateUnit,
  175. getDurationUnit
  176. )
  177. write("timers", timerCols, name, timestamp, values)
  178. }
  179. private def write(metric: String,
  180. cols: Array[(String, String)],
  181. name: String,
  182. timestamp: String,
  183. values: Array[Any]): Unit = writers.synchronized {
  184. val key = if (aggregate) metric else name
  185. val writer = writers.getOrElseUpdate(key, {
  186. val fileName = s"$key.${if (format == CSVFormat.TDF) "t" else "c"}sv"
  187. val file = new File(folder, fileName)
  188. val writer = format.print(new FileWriter(file, true))
  189. // write the header row if the file is empty
  190. if (!file.exists() || file.length() == 0) {
  191. writer.print("time")
  192. if (aggregate) {
  193. writer.print("name")
  194. }
  195. cols.foreach { case (header, _) => writer.print(header) }
  196. writer.println()
  197. }
  198. writer
  199. })
  200. writer.print(timestamp)
  201. if (aggregate) {
  202. writer.print(name)
  203. }
  204. var i = 0
  205. while (i < cols.length) {
  206. writer.print(cols(i)._2.formatLocal(locale, values(i)))
  207. i += 1
  208. }
  209. writer.println()
  210. }
  211. }