@@ -24,27 +24,33 @@ import java.util.Locale
2424
2525import com .typesafe .config .Config
2626import kamon .metric .MeasurementUnit .Dimension .{ Information , Time }
27- import kamon .metric .{ MeasurementUnit , MetricDistribution , MetricValue , PeriodSnapshot }
28- import kamon .util .{ EnvironmentTagBuilder , Matcher }
29- import kamon .{ Kamon , MetricReporter }
27+ import kamon .metric .{ MeasurementUnit , MetricSnapshot , PeriodSnapshot }
28+ import kamon .tag .{ Tag , TagSet }
29+ import kamon .util .{ EnvironmentTags , Filter }
30+ import kamon .{ module , Kamon }
31+ import kamon .datadog .DatadogAPIReporter .Configuration
32+ import kamon .module .{ MetricReporter , ModuleFactory }
3033import org .slf4j .LoggerFactory
3134
3235import scala .util .{ Failure , Success }
3336
34- class DatadogAPIReporter extends MetricReporter {
37+ class DatadogAPIReporterFactory extends ModuleFactory {
38+ override def create (settings : ModuleFactory .Settings ): DatadogAPIReporter = {
39+ val config = DatadogAPIReporter .readConfiguration(settings.config)
40+ new DatadogAPIReporter (config, new HttpClient (config.httpConfig, usingAgent = false ))
41+ }
42+ }
43+
44+ class DatadogAPIReporter (@ volatile private var configuration : Configuration , @ volatile private var httpClient : HttpClient ) extends MetricReporter {
3545 import DatadogAPIReporter ._
3646
3747 private val logger = LoggerFactory .getLogger(classOf [DatadogAPIReporter ])
3848 private val symbols = DecimalFormatSymbols .getInstance(Locale .US )
3949 symbols.setDecimalSeparator('.' ) // Just in case there is some weird locale config we are not aware of.
4050
4151 private val valueFormat = new DecimalFormat (" #0.#########" , symbols)
42- private var configuration = readConfiguration(Kamon .config())
43- private var httpClient : HttpClient = new HttpClient (configuration.httpConfig)
4452
45- override def start (): Unit = {
46- logger.info(" Started the Datadog API reporter." )
47- }
53+ logger.info(" Started the Datadog API reporter." )
4854
4955 override def stop (): Unit = {
5056 logger.info(" Stopped the Datadog API reporter." )
@@ -53,15 +59,15 @@ class DatadogAPIReporter extends MetricReporter {
5359 override def reconfigure (config : Config ): Unit = {
5460 val newConfiguration = readConfiguration(config)
5561 configuration = newConfiguration
56- httpClient = new HttpClient (configuration.httpConfig)
62+ httpClient = new HttpClient (configuration.httpConfig, usingAgent = false )
5763 }
5864
5965 override def reportPeriodSnapshot (snapshot : PeriodSnapshot ): Unit = {
6066 httpClient.doPost(" application/json; charset=utf-8" , buildRequestBody(snapshot)) match {
6167 case Failure (e) =>
6268 logger.error(e.getMessage)
6369 case Success (response) =>
64- logger.info (response)
70+ logger.trace (response)
6571 }
6672 }
6773
@@ -72,20 +78,23 @@ class DatadogAPIReporter extends MetricReporter {
7278 val interval = Math .round(Duration .between(snapshot.from, snapshot.to).toMillis() / 1000D )
7379 val seriesBuilder = new StringBuilder ()
7480
75- def addDistribution (metric : MetricDistribution ): Unit = {
76- import metric ._
77-
78- val average = if (distribution.count > 0L ) (distribution.sum / distribution.count) else 0L
79- addMetric(name + " .avg" , valueFormat.format(scale(average, unit)), gauge, metric.tags)
80- addMetric(name + " .count" , valueFormat.format(distribution.count), count, metric.tags)
81- addMetric(name + " .median" , valueFormat.format(scale(distribution.percentile(50D ).value, unit)), gauge, metric.tags)
82- addMetric(name + " .95percentile" , valueFormat.format(scale(distribution.percentile(95D ).value, unit)), gauge, metric.tags)
83- addMetric(name + " .max" , valueFormat.format(scale(distribution.max, unit)), gauge, metric.tags)
84- addMetric(name + " .min" , valueFormat.format(scale(distribution.min, unit)), gauge, metric.tags)
81+ def addDistribution (metric : MetricSnapshot .Distributions ): Unit = {
82+ val unit = metric.settings.unit
83+ metric.instruments.foreach { d =>
84+ val dist = d.value
85+
86+ val average = if (dist.count > 0L ) (dist.sum / dist.count) else 0L
87+ addMetric(metric.name + " .avg" , valueFormat.format(scale(average, unit)), gauge, d.tags)
88+ addMetric(metric.name + " .count" , valueFormat.format(dist.count), count, d.tags)
89+ addMetric(metric.name + " .median" , valueFormat.format(scale(dist.percentile(50D ).value, unit)), gauge, d.tags)
90+ addMetric(metric.name + " .95percentile" , valueFormat.format(scale(dist.percentile(95D ).value, unit)), gauge, d.tags)
91+ addMetric(metric.name + " .max" , valueFormat.format(scale(dist.max, unit)), gauge, d.tags)
92+ addMetric(metric.name + " .min" , valueFormat.format(scale(dist.min, unit)), gauge, d.tags)
93+ }
8594 }
8695
87- def addMetric (metricName : String , value : String , metricType : String , tags : Map [ String , String ] ): Unit = {
88- val customTags = (configuration.extraTags ++ tags.filterKeys( configuration.tagFilter.accept)) .map { case (k, v) ⇒ quote " $k: $v" }.toSeq
96+ def addMetric (metricName : String , value : String , metricType : String , tags : TagSet ): Unit = {
97+ val customTags = (configuration.extraTags ++ tags.iterator(_.toString).map(p => p.key -> p.value).filter(t => configuration.tagFilter.accept(t._1))) .map { case (k, v) ⇒ quote " $k: $v" }
8998 val allTagsString = customTags.mkString(" [" , " ," , " ]" )
9099
91100 if (seriesBuilder.length() > 0 ) seriesBuilder.append(" ," )
@@ -94,13 +103,28 @@ class DatadogAPIReporter extends MetricReporter {
94103 .append(s """ {"metric":" $metricName","interval": $interval,"points":[[ $timestamp, $value]],"type":" $metricType","host":" $host","tags": $allTagsString} """ )
95104 }
96105
97- def add (metric : MetricValue , metricType : String ): Unit =
98- addMetric(metric.name, valueFormat.format(scale(metric.value, metric.unit)), metricType, metric.tags)
99-
100- snapshot.metrics.counters.foreach(add(_, count))
101- snapshot.metrics.gauges.foreach(add(_, gauge))
106+ snapshot.counters.foreach { snap =>
107+ snap.instruments.foreach { instrument =>
108+ addMetric(
109+ snap.name,
110+ valueFormat.format(scale(instrument.value, snap.settings.unit)),
111+ count,
112+ instrument.tags
113+ )
114+ }
115+ }
116+ snapshot.gauges.foreach { snap =>
117+ snap.instruments.foreach { instrument =>
118+ addMetric(
119+ snap.name,
120+ valueFormat.format(scale(instrument.value, snap.settings.unit)),
121+ gauge,
122+ instrument.tags
123+ )
124+ }
125+ }
102126
103- (snapshot.metrics. histograms ++ snapshot.metrics .rangeSamplers).foreach(addDistribution)
127+ (snapshot.histograms ++ snapshot.rangeSamplers).foreach(addDistribution)
104128
105129 seriesBuilder
106130 .insert(0 , " {\" series\" :[" )
@@ -110,36 +134,36 @@ class DatadogAPIReporter extends MetricReporter {
110134
111135 }
112136
113- private def scale (value : Long , unit : MeasurementUnit ): Double = unit.dimension match {
137+ private def scale (value : Double , unit : MeasurementUnit ): Double = unit.dimension match {
114138 case Time if unit.magnitude != configuration.timeUnit.magnitude =>
115- MeasurementUnit .scale (value, unit, configuration.timeUnit)
139+ MeasurementUnit .convert (value, unit, configuration.timeUnit)
116140
117141 case Information if unit.magnitude != configuration.informationUnit.magnitude =>
118- MeasurementUnit .scale(value, unit, configuration.informationUnit)
119-
120- case _ => value.toDouble
121- }
142+ MeasurementUnit .convert(value, unit, configuration.informationUnit)
122143
123- private def readConfiguration (config : Config ): Configuration = {
124- val datadogConfig = config.getConfig(" kamon.datadog" )
125- Configuration (
126- datadogConfig.getConfig(" http" ),
127- timeUnit = readTimeUnit(datadogConfig.getString(" time-unit" )),
128- informationUnit = readInformationUnit(datadogConfig.getString(" information-unit" )),
129- // Remove the "host" tag since it gets added to the datadog payload separately
130- EnvironmentTagBuilder .create(datadogConfig.getConfig(" additional-tags" )) - " host" ,
131- Kamon .filter(datadogConfig.getString(" filter-config-key" ))
132- )
144+ case _ => value
133145 }
134146}
135147
136148private object DatadogAPIReporter {
137149 val count = " count"
138150 val gauge = " gauge"
139151
140- case class Configuration (httpConfig : Config , timeUnit : MeasurementUnit , informationUnit : MeasurementUnit , extraTags : Map [ String , String ], tagFilter : Matcher )
152+ case class Configuration (httpConfig : Config , timeUnit : MeasurementUnit , informationUnit : MeasurementUnit , extraTags : Seq [( String , String ) ], tagFilter : Filter )
141153
142154 implicit class QuoteInterp (val sc : StringContext ) extends AnyVal {
143155 def quote (args : Any * ): String = " \" " + sc.s(args : _* ) + " \" "
144156 }
157+
158+ def readConfiguration (config : Config ): Configuration = {
159+ val datadogConfig = config.getConfig(" kamon.datadog" )
160+ Configuration (
161+ datadogConfig.getConfig(" api" ),
162+ timeUnit = readTimeUnit(datadogConfig.getString(" time-unit" )),
163+ informationUnit = readInformationUnit(datadogConfig.getString(" information-unit" )),
164+ // Remove the "host" tag since it gets added to the datadog payload separately
165+ EnvironmentTags .from(Kamon .environment, datadogConfig.getConfig(" environment-tags" )).without(" host" ).all().map(p => p.key -> Tag .unwrapValue(p).toString),
166+ Kamon .filter(" kamon.datadog.environment-tags.filter" )
167+ )
168+ }
145169}
0 commit comments