Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the possibility to filter out context tags not to propagate #1015

Merged
merged 1 commit into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/kamon-core-tests/src/test/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ kamon {
}

propagation.http.default {
tags.filter = []
tags.mappings {
"correlation-id" = "x-correlation-id"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
context.getTag(option("correlation")).value shouldBe "1234"
context.getTag(option("unknown")) shouldBe empty
}

"not read filtered headers into context" in {
val headers = Map(
"x-content-tags" -> "hello=world;correlation=1234;privateMappedTag=value;myLocalTag=value",
"string-header" -> "hey",
"integer-header" -> "123"
)

val context = httpPropagation.read(headerReaderFromMap(headers))
context.get(HttpPropagationSpec.StringKey) shouldBe "hey"
context.get(HttpPropagationSpec.IntegerKey) shouldBe 123
context.get(HttpPropagationSpec.OptionalKey) shouldBe empty
context.getTag(plain("hello")) shouldBe "world"
context.getTag(option("correlation")).value shouldBe "1234"
context.getTag(option("unknown")) shouldBe empty
context.getTag(option("myLocalTag")) shouldBe empty //should be removed by the filter
context.getTag(option("privateMappedTag")) shouldBe empty //should be removed by the filter
}
}


Expand Down Expand Up @@ -92,6 +110,24 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
"string-header" -> "out-we-go"
)
}

"not write filtered context tags" in {
val headers = mutable.Map.empty[String, String]
val context = Context.of(TagSet.from(Map(
"hello" -> "world",
"mappedTag" -> "value",
"privateHello" -> "world", //should be filtered
"privateMappedTag" -> "value", //should be filtered
"myLocalTag" -> "value" //should be filtered
)))

httpPropagation.write(context, headerWriterFromMap(headers))
headers should contain only(
"x-content-tags" -> "hello=world;upstream.name=kamon-application;",
"x-mapped-tag" -> "value"
)
}

}
}

Expand All @@ -102,7 +138,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
|tags {
| header-name = "x-content-tags"
| include-upstream-name = yes
|
| filter = ["private*", "myLocalTag"]
| mappings {
| mappedTag = "x-mapped-tag"
| }
Expand Down
10 changes: 9 additions & 1 deletion core/kamon-core/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,15 @@ kamon {
# Enables automatic inclusion of the "upstream.name" tag in outgoing requests.
include-upstream-name = yes

# Provide explicit mappins between context tags and the HTTP headers that will carry them. When there is
# Provides the means to filter which tags NOT to propagate as HTTP headers
# E.g having stored a tag in the context with the name 'mytag' one can exlude it from being propagated
# filter = ["mytag"]
# or using Glob patterns
# filter = ["my*"]
#
filter = []

# Provide explicit mappings between context tags and the HTTP headers that will carry them. When there is
# an explicit mapping for a tag, it will not be included in the default context header. For example, if
# you wanted to use the an HTTP header called `X-Correlation-ID` for a context tag with key `correlationID`
# you would need to include a the following configuration:
Expand Down
20 changes: 16 additions & 4 deletions core/kamon-core/src/main/scala/kamon/context/HttpPropagation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ package context
import com.typesafe.config.Config
import kamon.tag.{Tag, TagSet}
import kamon.trace.{Span, SpanPropagation}
import kamon.util.Filter.Glob
import org.slf4j.LoggerFactory

import scala.collection.JavaConverters._
import scala.reflect.ClassTag
import scala.util.control.NonFatal

Expand Down Expand Up @@ -108,8 +110,11 @@ object HttpPropagation {
}
}

//apply filter on tags on the exclusion list
val filteredTags = tags.result().filterNot(kv => tagFilter(kv._1))

// Incoming Entries
settings.incomingEntries.foldLeft(Context.of(TagSet.from(tags.result()))) {
settings.incomingEntries.foldLeft(Context.of(TagSet.from(filteredTags))) {
case (context, (entryName, entryDecoder)) =>
var result = context
try {
Expand All @@ -136,7 +141,7 @@ object HttpPropagation {
}

// Write tags with specific mappings or append them to the context tags header.
context.tags.iterator().foreach { tag =>
context.tags.iterator().filterNot(tagFilter).foreach { tag =>
val tagKey = tag.key

settings.tagsMappings.get(tagKey) match {
Expand Down Expand Up @@ -165,10 +170,15 @@ object HttpPropagation {
}
}


private val _longTypePrefix = "l:"
private val _booleanTypePrefix = "b:"

/**
* Filter for checking the tag towards the configured filter from ''kamon.propagation.http.default.tags.filter''
*/
private def tagFilter(tag:Tag):Boolean = tagFilter(tag.key)
private def tagFilter(tagName:String):Boolean = settings.tagsFilter.exists(_.accept(tagName))

/**
* Tries to infer and parse a value into one of the supported tag types: String, Long or Boolean by looking for the
* type indicator prefix on the tag value. If the inference fails it will default to treat the value as a String.
Expand Down Expand Up @@ -214,6 +224,7 @@ object HttpPropagation {
case class Settings(
tagsHeaderName: String,
includeUpstreamName: Boolean,
tagsFilter: Seq[Glob],
tagsMappings: Map[String, String],
incomingEntries: Map[String, Propagation.EntryReader[HeaderReader]],
outgoingEntries: Map[String, Propagation.EntryWriter[HeaderWriter]]
Expand Down Expand Up @@ -257,11 +268,12 @@ object HttpPropagation {

val tagsHeaderName = config.getString("tags.header-name")
val tagsIncludeUpstreamName = config.getBoolean("tags.include-upstream-name")
val tagsFilter = config.getStringList("tags.filter").asScala.map(Glob).toSeq
val tagsMappings = config.getConfig("tags.mappings").pairs
val incomingEntries = buildInstances[Propagation.EntryReader[HeaderReader]](config.getConfig("entries.incoming").pairs)
val outgoingEntries = buildInstances[Propagation.EntryWriter[HeaderWriter]](config.getConfig("entries.outgoing").pairs)

Settings(tagsHeaderName, tagsIncludeUpstreamName, tagsMappings, incomingEntries, outgoingEntries)
Settings(tagsHeaderName, tagsIncludeUpstreamName, tagsFilter, tagsMappings, incomingEntries, outgoingEntries)
}
}

Expand Down