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

+ enable context tag propagation to http server metrics #1309

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ kamon.instrumentation.akka.http {
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
#enabled = yes
#
# Metrics can also use tags from context; this will apply to the following subset of metrics:
# - http.server.request
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
#
#use-context-tags = no
}

#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ kamon.instrumentation.akka.http {
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
#enabled = yes
#
# Metrics can also use tags from context; this will apply to the following subset of metrics:
# - http.server.request
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
#
#use-context-tags = no
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ kamon {
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
enabled = yes
#
# Metrics can also use tags from context; this will apply to the following subset of metrics:
# - http.server.request
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
#
use-context-tags = no
}

#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import kamon.instrumentation.trace.SpanTagger.TagMode
import kamon.instrumentation.tag.TagKeys
import kamon.instrumentation.trace.SpanTagger
import kamon.tag.Lookups.option
import kamon.tag.TagSet
import kamon.trace.Span
import kamon.trace.Trace.SamplingDecision
import kamon.util.Filter
Expand Down Expand Up @@ -152,6 +153,10 @@ object HttpServerInstrumentation {
*/
def span: Span

/**
* Tag set to propagate to metrics from context
* */
def contextTagSet: TagSet
/**
* Signals that the entire request (headers and body) has been received.
*/
Expand Down Expand Up @@ -231,8 +236,11 @@ object HttpServerInstrumentation {
incomingContext.withEntry(Span.Key, requestSpan)
else incomingContext

val _contextTagSet: TagSet =
TagSet.from(settings.contextTagsForMetrics.collect{ case (k, TagMode.Metric) => handlerContext.getTag(option(k)).map(k -> _) }.flatten.toMap)

_metrics.foreach { httpServerMetrics =>
httpServerMetrics.activeRequests.increment()
httpServerMetrics.activeRequests.withTags(_contextTagSet).increment()
}

new HttpServerInstrumentation.RequestHandler {
Expand All @@ -242,10 +250,12 @@ object HttpServerInstrumentation {
override def span: Span =
requestSpan

override def contextTagSet: TagSet = _contextTagSet

override def requestReceived(receivedBytes: Long): RequestHandler = {
if(receivedBytes >= 0) {
_metrics.foreach { httpServerMetrics =>
httpServerMetrics.requestSize.record(receivedBytes)
httpServerMetrics.requestSize.withTags(contextTagSet).record(receivedBytes)
}
}

Expand All @@ -254,14 +264,14 @@ object HttpServerInstrumentation {

override def buildResponse[HttpResponse](response: HttpMessage.ResponseBuilder[HttpResponse], context: Context): HttpResponse = {
_metrics.foreach { httpServerMetrics =>
httpServerMetrics.countCompletedRequest(response.statusCode)
httpServerMetrics.countCompletedRequest(response.statusCode, contextTagSet)
}

if(!span.isEmpty) {
settings.traceIDResponseHeader.foreach(traceIDHeader => response.write(traceIDHeader, span.trace.id.string))
settings.spanIDResponseHeader.foreach(spanIDHeader => response.write(spanIDHeader, span.id.string))
settings.httpServerResponseHeaderGenerator.headers(handlerContext).foreach(header => response.write(header._1, header._2))

SpanTagger.tag(span, TagKeys.HttpStatusCode, response.statusCode, settings.statusCodeTagMode)

val statusCode = response.statusCode
Expand All @@ -275,10 +285,10 @@ object HttpServerInstrumentation {

override def responseSent(sentBytes: Long): Unit = {
_metrics.foreach { httpServerMetrics =>
httpServerMetrics.activeRequests.decrement()
httpServerMetrics.activeRequests.withTags(contextTagSet).decrement()

if(sentBytes >= 0)
httpServerMetrics.responseSize.record(sentBytes)
httpServerMetrics.responseSize.withTags(contextTagSet).record(sentBytes)
}

span.finish()
Expand Down Expand Up @@ -336,7 +346,7 @@ object HttpServerInstrumentation {
.foreach(tagValue => SpanTagger.tag(span, tagName, tagValue, mode))
}


span.start()
}
}
Expand All @@ -359,9 +369,11 @@ object HttpServerInstrumentation {
unhandledOperationName: String,
operationMappings: Map[Filter.Glob, String],
operationNameGenerator: HttpOperationNameGenerator,
httpServerResponseHeaderGenerator:HttpServerResponseHeaderGenerator
httpServerResponseHeaderGenerator:HttpServerResponseHeaderGenerator,
tagMetricsFromContext: Boolean
) {
val operationNameSettings = OperationNameSettings(defaultOperationName, operationMappings, operationNameGenerator)
def contextTagsForMetrics: Map[String, TagMode] = if (tagMetricsFromContext) contextTags else Map.empty
}

object Settings {
Expand All @@ -375,6 +387,7 @@ object HttpServerInstrumentation {

// HTTP Server metrics settings
val enableServerMetrics = config.getBoolean("metrics.enabled")
val tagMetricsFromContext = config.getBoolean("metrics.use-context-tags")

// Tracing settings
val enableTracing = config.getBoolean("tracing.enabled")
Expand Down Expand Up @@ -435,7 +448,8 @@ object HttpServerInstrumentation {
unhandledOperationName,
operationMappings,
operationNameGenerator.get,
httpServerResponseHeaderGenerator.get
httpServerResponseHeaderGenerator.get,
tagMetricsFromContext
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,17 @@ object HttpServerMetrics {
/**
* Increments the appropriate response counter depending on the the status code.
*/
def countCompletedRequest(statusCode: Int): Unit = {
def countCompletedRequest(statusCode: Int, contextTagSet: TagSet): Unit = {
if(statusCode >= 200 && statusCode <= 299)
requestsSuccessful.increment()
requestsSuccessful.withTags(contextTagSet).increment()
else if(statusCode >= 500 && statusCode <= 599)
requestsServerError.increment()
requestsServerError.withTags(contextTagSet).increment()
else if(statusCode >= 400 && statusCode <= 499)
requestsClientError.increment()
requestsClientError.withTags(contextTagSet).increment()
else if(statusCode >= 300 && statusCode <= 399)
requestsRedirection.increment()
requestsRedirection.withTags(contextTagSet).increment()
else if(statusCode >= 100 && statusCode <= 199)
requestsInformational.increment()
requestsInformational.withTags(contextTagSet).increment()
else {
_logger.warn("Unknown HTTP status code {} found when recording HTTP server metrics", statusCode.toString)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ kamon {
propagation.http.default {
tags.mappings {
"correlation-id" = "x-correlation-id"
"namespace" = "namespace"
}
}

instrumentation {
http-server {
default {
metrics {
use-context-tags = yes
}
tracing {
preferred-trace-id-tag = "correlation-id"
tags.from-context.peer = span
tags.from-context.namespace = metric
response-headers {
trace-id = "x-trace-id"
span-id = "x-span-id"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.time.Duration
import com.typesafe.config.ConfigFactory
import kamon.Kamon
import kamon.context.Context
import kamon.instrumentation.trace.SpanTagger.TagMode
import kamon.tag.Lookups._
import kamon.metric.{Counter, Histogram, RangeSampler, Timer}
import kamon.testkit.{InitAndStopKamonAfterAll, InstrumentInspection, SpanInspection}
Expand All @@ -17,7 +18,7 @@ import org.scalatest.wordspec.AnyWordSpec
import scala.collection.mutable

class HttpServerInstrumentationSpec extends AnyWordSpec with Matchers with InstrumentInspection.Syntax with OptionValues
with SpanInspection.Syntax with Eventually with InitAndStopKamonAfterAll {
with SpanInspection.Syntax with Eventually with InitAndStopKamonAfterAll {

"the HTTP server instrumentation" when {
"configured for context propagation" should {
Expand Down Expand Up @@ -174,6 +175,21 @@ class HttpServerInstrumentationSpec extends AnyWordSpec with Matchers with Instr
completedRequests(8081, 400).value() shouldBe 1L
completedRequests(8081, 500).value() shouldBe 1L
}

"populate context tags" in {
activeRequests(8081).distribution()

val handlerOne = httpServer().createHandler(fakeRequest("http://localhost:8080/", "/", "GET", Map("namespace" -> "env1")))
val handlerTwo = httpServer().createHandler(fakeRequest("http://localhost:8080/", "/", "GET", Map("namespace" -> "env1")))

handlerOne.contextTagSet.get(option("namespace")) shouldBe Some("env1")
handlerTwo.contextTagSet.get(option("namespace")) shouldBe Some("env1")
handlerOne.buildResponse(fakeResponse(200, mutable.Map.empty), Context.Empty)
handlerTwo.buildResponse(fakeResponse(200, mutable.Map.empty), Context.Empty)
handlerOne.responseSent(0L)
handlerTwo.responseSent(0L)

}
}

"configured for distributed tracing" should {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ kamon.instrumentation.pekko.http {
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
#enabled = yes
#
# Metrics can also use tags from context; this will apply to the following subset of metrics:
# - http.server.request
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
#
#use-context-tags = no
}

#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ kamon.instrumentation.pekko.http {
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
#enabled = yes
#
# Metrics can also use tags from context; this will apply to the following subset of metrics:
# - http.server.request
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
#use-context-tags = no
}


Expand Down
8 changes: 8 additions & 0 deletions instrumentation/kamon-play/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ kamon.instrumentation.play.http {
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
#enabled = yes
#
# Metrics can also use tags from context; this will apply to the following subset of metrics:
# - http.server.request
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
#
#use-context-tags = no
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ kamon.instrumentation.spring {
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
enabled = yes
#
# Metrics can also use tags from context; this will apply to the following subset of metrics:
# - http.server.request
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
#
use-context-tags = no
}

#
Expand Down
Loading