1 /*
2  *  Make.org Core API
3  *  Copyright (C) 2018 Make.org
4  *
5  * This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  *
18  */
19 
20 package org.make.api.technical.monitoring
21 import grizzled.slf4j.Logging
22 import kamon.Kamon
23 import kamon.metric.{DynamicRange, Histogram, MeasurementUnit}
24 import kamon.tag.TagSet
25 import org.make.api.technical.tracking.FrontPerformanceTimings
26 
27 import scala.concurrent.duration.DurationInt
28 import scala.util.{Failure, Success, Try}
29 
30 trait MonitoringService {
31   def monitorPerformance(applicationName: String, metrics: FrontPerformanceTimings): Unit
32 }
33 
34 trait MonitoringServiceComponent {
35   def monitoringService: MonitoringService
36 }
37 
38 final case class HistogramName(applicationName: String, metricName: String) {
39   def fullMetricName: String = s"loadtime.$applicationName.$metricName"
40 }
41 
42 trait DefaultMonitoringService extends MonitoringServiceComponent with Logging {
43   override lazy val monitoringService: MonitoringService = new DefaultMonitoringService
44 
45   class DefaultMonitoringService extends MonitoringService {
46 
47     private val maxLoadingTime: Long = 1.minute.toMillis
48     private val range: DynamicRange = DynamicRange(1L, maxLoadingTime, 2)
49 
50     private var histograms: Map[HistogramName, Histogram] = Map.empty
51 
52     private def getHistogram(histogramName: HistogramName): Histogram = {
53       if (!histograms.contains(histogramName)) {
54         val value =
55           Kamon
56             .histogram(name = "load_time", unit = MeasurementUnit.time.milliseconds, dynamicRange = range)
57             .withTags(
58               TagSet.from(
59                 Map(
60                   "application" -> MonitoringMessageHelper.format(histogramName.applicationName),
61                   "metric" -> histogramName.metricName
62                 )
63               )
64             )
65 
66         histograms += histogramName -> value
67       }
68       histograms(histogramName)
69     }
70 
71     private def recordIfPositive(applicationName: String, metric: String, value: Long): Unit = {
72       if (value >= 0) {
73         Try(getHistogram(HistogramName(applicationName, metric)).record(value)) match {
74           case Success(_) =>
75           case Failure(e) =>
76             logger.error(s"Error when logging value $value for metric $metric on application $applicationName", e)
77         }
78       }
79     }
80 
81     override def monitorPerformance(applicationName: String, metrics: FrontPerformanceTimings): Unit = {
82       recordIfPositive(applicationName, "connect", metrics.connectEnd - metrics.connectStart)
83       recordIfPositive(applicationName, "domain_lookup", metrics.domainLookupEnd - metrics.domainLookupStart)
84       recordIfPositive(applicationName, "dom_complete", metrics.domComplete - metrics.responseEnd)
85       recordIfPositive(applicationName, "dom_interactive", metrics.domInteractive - metrics.responseEnd)
86       recordIfPositive(applicationName, "dom_loading", metrics.domLoading - metrics.responseEnd)
87       recordIfPositive(applicationName, "request_time", metrics.responseEnd - metrics.requestStart)
88       recordIfPositive(applicationName, "first_byte", metrics.responseStart - metrics.requestStart)
89       recordIfPositive(applicationName, "transfer_time", metrics.responseEnd - metrics.responseStart)
90     }
91   }
92 }
93 
94 object MonitoringMessageHelper {
95   def format(value: String): String = value.filterNot(_ < ' ').replace("\"", "\\\"")
96 }
Line Stmt Id Pos Tree Symbol Tests Code
47 38440 1699 - 1716 Select scala.concurrent.duration.FiniteDuration.toMillis scala.concurrent.duration.`package`.DurationInt(1).minute.toMillis
47 46305 1699 - 1700 Literal <nosymbol> 1
48 40071 1788 - 1789 Literal <nosymbol> 2
48 47951 1772 - 1786 Select org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.maxLoadingTime DefaultMonitoringService.this.maxLoadingTime
48 31933 1755 - 1790 Apply kamon.metric.DynamicRange.apply kamon.metric.DynamicRange.apply(1L, DefaultMonitoringService.this.maxLoadingTime, 2)
48 30896 1768 - 1770 Literal <nosymbol> 1L
50 45247 1852 - 1861 TypeApply scala.collection.immutable.Map.empty scala.Predef.Map.empty[org.make.api.technical.monitoring.HistogramName, Nothing]
53 33539 1943 - 1943 Block <nosymbol> ()
53 45815 1984 - 2454 Block <nosymbol> { val value: kamon.metric.Histogram = kamon.Kamon.histogram("load_time", kamon.metric.MeasurementUnit.time.milliseconds, DefaultMonitoringService.this.range).withTags(kamon.tag.TagSet.from(scala.Predef.Map.apply[String, String](scala.Predef.ArrowAssoc[String]("application").->[String](MonitoringMessageHelper.format(histogramName.applicationName)), scala.Predef.ArrowAssoc[String]("metric").->[String](histogramName.metricName)))); DefaultMonitoringService.this.histograms_=(DefaultMonitoringService.this.histograms.+[kamon.metric.Histogram](scala.Predef.ArrowAssoc[org.make.api.technical.monitoring.HistogramName](histogramName).->[kamon.metric.Histogram](value))) }
53 37461 1947 - 1982 Select scala.Boolean.unary_! DefaultMonitoringService.this.histograms.contains(histogramName).unary_!
53 37951 1943 - 1943 Literal <nosymbol> ()
56 33586 2052 - 2063 Literal <nosymbol> "load_time"
56 47363 2072 - 2105 Select kamon.metric.MeasurementUnit.TimeUnits.milliseconds kamon.metric.MeasurementUnit.time.milliseconds
56 38475 2122 - 2127 Select org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.range DefaultMonitoringService.this.range
57 31405 2016 - 2400 Apply kamon.metric.Tagging.withTags kamon.Kamon.histogram("load_time", kamon.metric.MeasurementUnit.time.milliseconds, DefaultMonitoringService.this.range).withTags(kamon.tag.TagSet.from(scala.Predef.Map.apply[String, String](scala.Predef.ArrowAssoc[String]("application").->[String](MonitoringMessageHelper.format(histogramName.applicationName)), scala.Predef.ArrowAssoc[String]("metric").->[String](histogramName.metricName))))
58 38515 2166 - 2386 Apply kamon.tag.TagSet.from kamon.tag.TagSet.from(scala.Predef.Map.apply[String, String](scala.Predef.ArrowAssoc[String]("application").->[String](MonitoringMessageHelper.format(histogramName.applicationName)), scala.Predef.ArrowAssoc[String]("metric").->[String](histogramName.metricName)))
59 47397 2195 - 2370 Apply scala.collection.MapFactory.apply scala.Predef.Map.apply[String, String](scala.Predef.ArrowAssoc[String]("application").->[String](MonitoringMessageHelper.format(histogramName.applicationName)), scala.Predef.ArrowAssoc[String]("metric").->[String](histogramName.metricName))
60 47697 2266 - 2295 Select org.make.api.technical.monitoring.HistogramName.applicationName histogramName.applicationName
60 31652 2218 - 2231 Literal <nosymbol> "application"
60 33003 2218 - 2296 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[String]("application").->[String](MonitoringMessageHelper.format(histogramName.applicationName))
60 40885 2235 - 2296 Apply org.make.api.technical.monitoring.MonitoringMessageHelper.format MonitoringMessageHelper.format(histogramName.applicationName)
61 38194 2328 - 2352 Select org.make.api.technical.monitoring.HistogramName.metricName histogramName.metricName
61 33336 2316 - 2352 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[String]("metric").->[String](histogramName.metricName)
61 45774 2316 - 2324 Literal <nosymbol> "metric"
66 44720 2424 - 2446 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[org.make.api.technical.monitoring.HistogramName](histogramName).->[kamon.metric.Histogram](value)
66 40634 2410 - 2446 Apply scala.collection.immutable.MapOps.+ DefaultMonitoringService.this.histograms.+[kamon.metric.Histogram](scala.Predef.ArrowAssoc[org.make.api.technical.monitoring.HistogramName](histogramName).->[kamon.metric.Histogram](value))
66 33040 2410 - 2446 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.histograms_= DefaultMonitoringService.this.histograms_=(DefaultMonitoringService.this.histograms.+[kamon.metric.Histogram](scala.Predef.ArrowAssoc[org.make.api.technical.monitoring.HistogramName](histogramName).->[kamon.metric.Histogram](value)))
68 47150 2461 - 2486 Apply scala.collection.MapOps.apply DefaultMonitoringService.this.histograms.apply(histogramName)
72 39577 2601 - 2611 Apply scala.Long.>= value.>=(0)
72 33290 2597 - 2597 Block <nosymbol> ()
72 37990 2597 - 2597 Literal <nosymbol> ()
73 45567 2623 - 2885 Match <nosymbol> scala.util.Try.apply[kamon.metric.Histogram](DefaultMonitoringService.this.getHistogram(HistogramName.apply(applicationName, metric)).record(value)) match { case (value: kamon.metric.Histogram): scala.util.Success[kamon.metric.Histogram](_) => () case (exception: Throwable): scala.util.Failure[kamon.metric.Histogram]((e @ _)) => DefaultMonitoringService.this.logger.error(("Error when logging value ".+(value).+(" for metric ").+(metric).+(" on application ").+(applicationName): String), e) }
73 44480 2623 - 2694 Apply scala.util.Try.apply scala.util.Try.apply[kamon.metric.Histogram](DefaultMonitoringService.this.getHistogram(HistogramName.apply(applicationName, metric)).record(value))
73 31443 2627 - 2693 Apply kamon.metric.Histogram.record DefaultMonitoringService.this.getHistogram(HistogramName.apply(applicationName, metric)).record(value)
74 40066 2729 - 2731 Literal <nosymbol> ()
76 33079 2773 - 2875 Apply grizzled.slf4j.Logger.error DefaultMonitoringService.this.logger.error(("Error when logging value ".+(value).+(" for metric ").+(metric).+(" on application ").+(applicationName): String), e)
82 43985 3012 - 3099 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "connect", metrics.connectEnd.-(metrics.connectStart))
82 46587 3046 - 3055 Literal <nosymbol> "connect"
82 39077 3078 - 3098 Select org.make.api.technical.tracking.FrontPerformanceTimings.connectStart metrics.connectStart
82 31189 3057 - 3098 Apply scala.Long.- metrics.connectEnd.-(metrics.connectStart)
83 37747 3106 - 3209 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "domain_lookup", metrics.domainLookupEnd.-(metrics.domainLookupStart))
83 40105 3140 - 3155 Literal <nosymbol> "domain_lookup"
83 45604 3157 - 3208 Apply scala.Long.- metrics.domainLookupEnd.-(metrics.domainLookupStart)
83 32224 3183 - 3208 Select org.make.api.technical.tracking.FrontPerformanceTimings.domainLookupStart metrics.domainLookupStart
84 46629 3288 - 3307 Select org.make.api.technical.tracking.FrontPerformanceTimings.responseEnd metrics.responseEnd
84 31227 3216 - 3308 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "dom_complete", metrics.domComplete.-(metrics.responseEnd))
84 39534 3266 - 3307 Apply scala.Long.- metrics.domComplete.-(metrics.responseEnd)
84 50520 3250 - 3264 Literal <nosymbol> "dom_complete"
85 33037 3368 - 3412 Apply scala.Long.- metrics.domInteractive.-(metrics.responseEnd)
85 39864 3393 - 3412 Select org.make.api.technical.tracking.FrontPerformanceTimings.responseEnd metrics.responseEnd
85 43733 3349 - 3366 Literal <nosymbol> "dom_interactive"
85 46061 3315 - 3413 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "dom_interactive", metrics.domInteractive.-(metrics.responseEnd))
86 50561 3490 - 3509 Select org.make.api.technical.tracking.FrontPerformanceTimings.responseEnd metrics.responseEnd
86 46385 3469 - 3509 Apply scala.Long.- metrics.domLoading.-(metrics.responseEnd)
86 39572 3420 - 3510 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "dom_loading", metrics.domLoading.-(metrics.responseEnd))
86 37239 3454 - 3467 Literal <nosymbol> "dom_loading"
87 36213 3567 - 3609 Apply scala.Long.- metrics.responseEnd.-(metrics.requestStart)
87 43772 3589 - 3609 Select org.make.api.technical.tracking.FrontPerformanceTimings.requestStart metrics.requestStart
87 31690 3551 - 3565 Literal <nosymbol> "request_time"
87 32780 3517 - 3610 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "request_time", metrics.responseEnd.-(metrics.requestStart))
88 37980 3689 - 3709 Select org.make.api.technical.tracking.FrontPerformanceTimings.requestStart metrics.requestStart
88 50316 3665 - 3709 Apply scala.Long.- metrics.responseStart.-(metrics.requestStart)
88 47180 3617 - 3710 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "first_byte", metrics.responseStart.-(metrics.requestStart))
88 46094 3651 - 3663 Literal <nosymbol> "first_byte"
89 39326 3751 - 3766 Literal <nosymbol> "transfer_time"
89 35970 3717 - 3812 Apply org.make.api.technical.monitoring.DefaultMonitoringService.DefaultMonitoringService.recordIfPositive DefaultMonitoringService.this.recordIfPositive(applicationName, "transfer_time", metrics.responseEnd.-(metrics.responseStart))
89 31180 3790 - 3811 Select org.make.api.technical.tracking.FrontPerformanceTimings.responseStart metrics.responseStart
89 44509 3768 - 3811 Apply scala.Long.- metrics.responseEnd.-(metrics.responseStart)
95 32821 3897 - 3943 Apply java.lang.String.replace org.make.api.technical.monitoring.monitoringmessagehelpertest scala.Predef.augmentString(value).filterNot(((x$1: Char) => x$1.<(' '))).replace("\"", "\\\"")