diff options
Diffstat (limited to 'kamon-core/src/main/scala')
7 files changed, 138 insertions, 32 deletions
diff --git a/kamon-core/src/main/scala/kamon/Util.scala b/kamon-core/src/main/scala/kamon/Util.scala index 04ce7a04..c8efbdc0 100644 --- a/kamon-core/src/main/scala/kamon/Util.scala +++ b/kamon-core/src/main/scala/kamon/Util.scala @@ -1,6 +1,6 @@ package kamon -import kamon.util.EntityFilter +import kamon.metric.EntityFilter /** * Useful classes for Kamon and submodules. diff --git a/kamon-core/src/main/scala/kamon/metric/EntityFilter.scala b/kamon-core/src/main/scala/kamon/metric/EntityFilter.scala new file mode 100644 index 00000000..cf203609 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/metric/EntityFilter.scala @@ -0,0 +1,110 @@ +package kamon +package metric + +import java.util.regex.Pattern +import com.typesafe.config.Config + +object EntityFilter { + def fromConfig(config: Config): EntityFilter = { + val filtersConfig = config.getConfig("kamon.metric.filters") + val acceptUnmatched = filtersConfig.getBoolean("accept-unmatched") + + val perCategoryFilters = filtersConfig.firstLevelKeys.filter(_ != "accept-unmatched") map { category: String ⇒ + val includes = readFilters(filtersConfig, s"$category.includes") + val excludes = readFilters(filtersConfig, s"$category.excludes") + + (category, new IncludeExcludeNameFilter(includes, excludes, acceptUnmatched)) + } toMap + + new EntityFilter(perCategoryFilters, acceptUnmatched) + } + + private def readFilters(filtersConfig: Config, name: String): Seq[NameFilter] = { + import scala.collection.JavaConverters._ + if(filtersConfig.hasPath(name)) + filtersConfig.getStringList(name).asScala.map(readNameFilter) + else + Seq.empty + } + + private def readNameFilter(pattern: String): NameFilter = { + if(pattern.startsWith("regex:")) + new RegexNameFilter(pattern.drop(6)) + else if(pattern.startsWith("glob:")) + new GlobPathFilter(pattern.drop(5)) + else + new GlobPathFilter(pattern) + } +} + +class EntityFilter(perCategoryFilters: Map[String, NameFilter], acceptUnmatched: Boolean) { + def accept(entity: Entity): Boolean = + perCategoryFilters + .get(entity.category) + .map(_.accept(entity.name)) + .getOrElse(acceptUnmatched) +} + +trait NameFilter { + def accept(name: String): Boolean +} + +class IncludeExcludeNameFilter(includes: Seq[NameFilter], excludes: Seq[NameFilter], acceptUnmatched: Boolean) extends NameFilter { + override def accept(name: String): Boolean = + (includes.exists(_.accept(name)) || acceptUnmatched) && !excludes.exists(_.accept(name)) +} + +class RegexNameFilter(path: String) extends NameFilter { + private val pathRegex = path.r + + override def accept(path: String): Boolean = path match { + case pathRegex(_*) ⇒ true + case _ ⇒ false + } +} + +class GlobPathFilter(glob: String) extends NameFilter { + private val globPattern = Pattern.compile("(\\*\\*?)|(\\?)|(\\\\.)|(/+)|([^*?]+)") + private val compiledPattern = getGlobPattern(glob) + + override def accept(name: String): Boolean = + compiledPattern.matcher(name).matches() + + private def getGlobPattern(glob: String) = { + val patternBuilder = new StringBuilder + val matcher = globPattern.matcher(glob) + while (matcher.find()) { + val (grp1, grp2, grp3, grp4) = (matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4)) + if (grp1 != null) { + // match a * or ** + if (grp1.length == 2) { + // it's a *workers are able to process multiple metrics* + patternBuilder.append(".*") + } + else { + // it's a * + patternBuilder.append("[^/]*") + } + } + else if (grp2 != null) { + // match a '?' glob pattern; any non-slash character + patternBuilder.append("[^/]") + } + else if (grp3 != null) { + // backslash-escaped value + patternBuilder.append(Pattern.quote(grp3.substring(1))) + } + else if (grp4 != null) { + // match any number of / chars + patternBuilder.append("/+") + } + else { + // some other string + patternBuilder.append(Pattern.quote(matcher.group)) + } + } + + Pattern.compile(patternBuilder.toString) + } +} + diff --git a/kamon-core/src/main/scala/kamon/metric/RecorderRegistry.scala b/kamon-core/src/main/scala/kamon/metric/RecorderRegistry.scala index a4d2f4cd..53081760 100644 --- a/kamon-core/src/main/scala/kamon/metric/RecorderRegistry.scala +++ b/kamon-core/src/main/scala/kamon/metric/RecorderRegistry.scala @@ -10,31 +10,31 @@ import scala.collection.concurrent.TrieMap trait RecorderRegistry { + def shouldTrack(entity: Entity): Boolean def getRecorder(entity: Entity): EntityRecorder - def getRecorder(name: String, category: String, tags: Map[String, String]): EntityRecorder - def removeRecorder(entity: Entity): Boolean - def removeRecorder(name: String, category: String, tags: Map[String, String]): Boolean } class RecorderRegistryImpl(initialConfig: Config) extends RecorderRegistry { private val instrumentFactory = new AtomicReference[InstrumentFactory]() + private val entityFilter = new AtomicReference[EntityFilter]() private val entities = TrieMap.empty[Entity, EntityRecorder with EntitySnapshotProducer] reconfigure(initialConfig) - override def getRecorder(entity: Entity): EntityRecorder = - entities.atomicGetOrElseUpdate(entity, new DefaultEntityRecorder(entity, instrumentFactory.get())) - override def getRecorder(name: String, category: String, tags: Map[String, String]): EntityRecorder = - getRecorder(Entity(name, category, tags)) + override def shouldTrack(entity: Entity): Boolean = + entityFilter.get().accept(entity) - override def removeRecorder(entity: Entity): Boolean = ??? + override def getRecorder(entity: Entity): EntityRecorder = + entities.atomicGetOrElseUpdate(entity, new DefaultEntityRecorder(entity, instrumentFactory.get())) - override def removeRecorder(name: String, category: String, tags: Map[String, String]): Boolean = ??? + override def removeRecorder(entity: Entity): Boolean = + entities.remove(entity).nonEmpty - private[kamon] def reconfigure(config: Config): Unit = { - instrumentFactory.set(InstrumentFactory(config.getConfig("kamon.metric.instrument-factory"))) + private[kamon] def reconfigure(config: Config): Unit = synchronized { + instrumentFactory.set(InstrumentFactory.fromConfig(config)) + entityFilter.set(EntityFilter.fromConfig(config)) } private[kamon] def snapshot(): Seq[EntitySnapshot] = { diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala b/kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala index e8d4d569..33a34bdf 100644 --- a/kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala +++ b/kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala @@ -63,14 +63,15 @@ private[kamon] class InstrumentFactory private ( ) } -private[kamon] object InstrumentFactory { +object InstrumentFactory { - private[kamon] def apply(config: Config): InstrumentFactory = { - val histogramDynamicRange = readDynamicRange(config.getConfig("default-settings.histogram")) - val mmCounterDynamicRange = readDynamicRange(config.getConfig("default-settings.min-max-counter")) - val mmCounterSampleInterval = config.getDuration("default-settings.min-max-counter.sample-interval") + def fromConfig(config: Config): InstrumentFactory = { + val factoryConfig = config.getConfig("kamon.metric.instrument-factory") + val histogramDynamicRange = readDynamicRange(factoryConfig.getConfig("default-settings.histogram")) + val mmCounterDynamicRange = readDynamicRange(factoryConfig.getConfig("default-settings.min-max-counter")) + val mmCounterSampleInterval = factoryConfig.getDuration("default-settings.min-max-counter.sample-interval") - val customSettings = config.getConfig("custom-settings") + val customSettings = factoryConfig.getConfig("custom-settings") .configurations .filter(nonEmptyCategories) .flatMap(buildCustomInstrumentSettings) @@ -96,7 +97,6 @@ private[kamon] object InstrumentFactory { significantValueDigits = config.getInt("significant-value-digits") ) - private case class CustomInstrumentSettings( lowestDiscernibleValue: Option[Long], highestTrackableValue: Option[Long], diff --git a/kamon-core/src/main/scala/kamon/trace/Span.scala b/kamon-core/src/main/scala/kamon/trace/Span.scala index 87115e19..804627dc 100644 --- a/kamon-core/src/main/scala/kamon/trace/Span.scala +++ b/kamon-core/src/main/scala/kamon/trace/Span.scala @@ -1,7 +1,7 @@ package kamon package trace -import kamon.metric.RecorderRegistry +import kamon.metric.{Entity, RecorderRegistry} import kamon.metric.instrument.DynamicRange import scala.collection.JavaConverters._ @@ -167,7 +167,8 @@ class Span(spanContext: SpanContext, initialOperationName: String, startTimestam private def recordSpanMetrics(): Unit = { val elapsedTime = endTimestampMicros - startTimestampMicros - val recorder = recorderRegistry.getRecorder(operationName, Span.MetricCategory, metricTags) + val entity = Entity(operationName, Span.MetricCategory, metricTags) + val recorder = recorderRegistry.getRecorder(entity) recorder .histogram(Span.LatencyMetricName, MeasurementUnit.time.microseconds, DynamicRange.Default) @@ -178,6 +179,5 @@ class Span(spanContext: SpanContext, initialOperationName: String, startTimestam recorder.counter(Span.ErrorMetricName).increment() } } - } }
\ No newline at end of file diff --git a/kamon-core/src/main/scala/kamon/trace/SpanContext.scala b/kamon-core/src/main/scala/kamon/trace/SpanContext.scala index 7f5962e0..a3afb36d 100644 --- a/kamon-core/src/main/scala/kamon/trace/SpanContext.scala +++ b/kamon-core/src/main/scala/kamon/trace/SpanContext.scala @@ -1,4 +1,5 @@ package kamon.trace + import java.lang import java.util.Map import scala.collection.JavaConverters._ @@ -6,16 +7,18 @@ import scala.collection.JavaConverters._ class SpanContext(val traceID: Long, val spanID: Long, val parentID: Long) extends io.opentracing.SpanContext { private var baggage = scala.collection.immutable.Map.empty[String, String] - private[kamon] def addBaggageItem(key: String, value: String): Unit = { + private[kamon] def addBaggageItem(key: String, value: String): Unit = synchronized { baggage = baggage + (key -> value) } - private[kamon] def getBaggage(key: String): String = + private[kamon] def getBaggage(key: String): String = synchronized { baggage.get(key).getOrElse(null) + } private[kamon] def baggageMap: scala.collection.immutable.Map[String, String] = baggage - override def baggageItems(): lang.Iterable[Map.Entry[String, String]] = + override def baggageItems(): lang.Iterable[Map.Entry[String, String]] = synchronized { baggage.asJava.entrySet() + } } diff --git a/kamon-core/src/main/scala/kamon/util/EntityFilter.scala b/kamon-core/src/main/scala/kamon/util/EntityFilter.scala deleted file mode 100644 index a8456689..00000000 --- a/kamon-core/src/main/scala/kamon/util/EntityFilter.scala +++ /dev/null @@ -1,7 +0,0 @@ -package kamon.util - -import kamon.metric.Entity - -trait EntityFilter { - def accept(entity: Entity): Boolean -} |