Skip to content

Commit

Permalink
+ kamon-armeria module
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasamoroso committed Sep 19, 2020
1 parent e357bac commit d80e40b
Show file tree
Hide file tree
Showing 21 changed files with 1,684 additions and 1 deletion.
19 changes: 18 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ lazy val instrumentation = (project in file("instrumentation"))
`kamon-akka-http`,
`kamon-play`,
`kamon-okhttp`,
`kamon-armeria`
)


Expand Down Expand Up @@ -423,6 +424,21 @@ lazy val `kamon-okhttp` = (project in file("instrumentation/kamon-okhttp"))
).dependsOn(`kamon-core`, `kamon-executors`, `kamon-testkit` % "test")


lazy val `kamon-armeria` = (project in file("instrumentation/kamon-armeria"))
.disablePlugins(AssemblyPlugin)
.enablePlugins(JavaAgent)
.settings(instrumentationSettings)
.settings(
libraryDependencies ++= Seq(
kanelaAgent % "provided",
"com.linecorp.armeria" % "armeria" % "1.0.0" % "provided",

scalatest % "test",
okHttp % "test",
logbackClassic % "test"
)
).dependsOn(`kamon-instrumentation-common`, `kamon-testkit` % "test")

/**
* Reporters
*/
Expand Down Expand Up @@ -637,4 +653,5 @@ val `kamon-bundle` = (project in file("bundle/kamon-bundle"))
`kamon-akka-http` % "shaded",
`kamon-play` % "shaded",
`kamon-okhttp` % "shaded",
)
`kamon-armeria` % "shaded"
)
254 changes: 254 additions & 0 deletions instrumentation/kamon-armeria/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
# =================================== #
# kamon-armeria reference configuration #
# =================================== #


kamon.instrumentation.armeria {

# Settings to control the HTTP Server instrumentation.
#
# IMPORTANT: Besides the "initial-operation-name" and "unhandled-operation-name" settings, the entire configuration of
# the HTTP Server Instrumentation is based on the constructs provided by the Kamon Instrumentation Common library
# which will always fallback to the settings found under the "kamon.instrumentation.http-server.default" path. The
# default settings have been included here to make them easy to find and understand in the context of this project and
# commented out so that any changes to the default settings will actually have effect.
#
http-server {

#
# Configuration for HTTP context propagation.
#
propagation {

# Enables or disables HTTP context propagation on this HTTP server instrumentation. Please note that if
# propagation is disabled then some distributed tracing features will not be work as expected (e.g. Spans can
# be created and reported but will not be linked across boundaries nor take trace identifiers from tags).
#enabled = yes

# HTTP propagation channel to b used by this instrumentation. Take a look at the kamon.propagation.http.default
# configuration for more details on how to configure the detault HTTP context propagation.
channel = "default"


}


#
# Configuration for HTTP server metrics collection.
#
metrics {

# Enables collection of HTTP server metrics. When enabled the following metrics will be collected, assuming
# that the instrumentation is fully compliant:
#
# - http.server.requets
# - http.server.request.active
# - http.server.request.size
# - http.server.response.size
# - http.server.connection.lifetime
# - http.server.connection.usage
# - http.server.connection.open
#
# All metrics have at least three tags: component, interface and port. Additionally, the http.server.requests
# metric will also have a status_code tag with the status code group (1xx, 2xx and so on).
#
#enabled = yes
}


#
# Configuration for HTTP request tracing.
#
tracing {

# Enables HTTP request tracing. When enabled the instrumentation will create Spans for incoming requests
# and finish them when the response is sent back to the clients.
#enabled = yes

# Select a context tag that provides a preferred trace identifier. The preferred trace identifier will be used
# only if all these conditions are met:
# - the context tag is present.
# - there is no parent Span on the incoming context (i.e. this is the first service on the trace).
# - the identifier is valid in accordance to the identity provider.
#preferred-trace-id-tag = "none"

# Enables collection of span metrics using the `span.processing-time` metric.
#span-metrics = on

# Select which tags should be included as span and span metric tags. The possible options are:
# - span: the tag is added as a Span tag (i.e. using span.tag(...))
# - metric: the tag is added a a Span metric tag (i.e. using span.tagMetric(...))
# - off: the tag is not used.
#
tags {

# Use the http.url tag.
#url = span

# Use the http.method tag.
#method = metric

# Use the http.status_code tag.
#status-code = metric

# Copy tags from the context into the Spans with the specified purpouse. For example, to copy a customer_type
# tag from the context into the HTTP Server Span crekamon.trace.sampler = alwaysated by the instrumentation, the following configuration
# should be added:
#
# from-context {
# customer_type = span
# }
#
from-context {

}
}

# Controls writing trace and span identifiers to HTTP response headers sent by the instrumented servers. The
# configuration can be set to either "none" to disable writing the identifiers on the response headers or to
# the header name to be used when writing the identifiers.
response-headers {

# HTTP response header name for the trace identifier, or "none" to disable it.
#trace-id = "trace-id"

# HTTP response header name for the server span identifier, or "none" to disable it.
#span-id = none
}

# Custom mappings between routes and operation names.
operations {

# The default operation name to be used when creating Spans to handle the HTTP server requests. In most
# cases it is not possible to define an operation name right at the moment of starting the HTTP server Span
# and in those cases, this operation name will be initially assigned to the Span. Instrumentation authors
# should do their best effort to provide a suitable operation name or make use of the "mappings" facilities.
default = "http.server.request"

# The operation name to be assigned when an application cannot find any route/endpoint/controller to handle
# a given request. Depending on the instrumented framework, it might be possible to apply this operation
# name automatically or not, check the frameworks' instrumentation docs for more details.
unhandled = "unhandled"

# FQCN for a HttpOperationNameGenerator implementation, or ony of the following shorthand forms:
# - default: Uses the set default operation name
# - method: Uses the request HTTP method as the operation name.
#
name-generator = "kamon.armeria.instrumentation.server.KamonArmeriaOperationNameGenerator"

# Provides custom mappings from HTTP paths into operation names. Meant to be used in cases where the bytecode
# instrumentation is not able to provide a sensible operation name that is free of high cardinality values.
# For example, with the following configuration:
# mappings {
# "/organization/*/user/*/profile" = "/organization/:orgID/user/:userID/profile"
# "/events/*/rsvps" = "EventRSVPs"
# }
#
# Requests to "/organization/3651/user/39652/profile" and "/organization/22234/user/54543/profile" will have
# the same operation name "/organization/:orgID/user/:userID/profile".
#
# Similarly, requests to "/events/aaa-bb-ccc/rsvps" and "/events/1234/rsvps" will have the same operation
# name "EventRSVPs".
#
# The patterns are expressed as globs and the operation names are free form.
#
mappings {

}
}
}
}

# Settings to control the HTTP Client instrumentation
#
# IMPORTANT: The entire configuration of the HTTP Client Instrumentation is based on the constructs provided by the
# Kamon Instrumentation Common library which will always fallback to the settings found under the
# "kamon.instrumentation.http-client.default" path. The default settings have been included here to make them easy to
# find and understand in the context of this project and commented out so that any changes to the default settings
# will actually have effect.
#
http-client {
#
# Configuration for HTTP context propagation.
#
propagation {

# Enables or disables HTTP context propagation on this HTTP server instrumentation. Please note that if
# propagation is disabled then some distributed tracing features will not be work as expected (e.g. Spans can
# be created and reported but will not be linked across boundaries nor take trace identifiers from tags).
enabled = yes

# HTTP propagation channel to b used by this instrumentation. Take a look at the kamon.propagation.http.default
# configuration for more details on how to configure the detault HTTP context propagation.
channel = "default"
}

tracing {

# Enables HTTP request tracing. When enabled the instrumentation will create Spans for outgoing requests
# and finish them when the response is received from the server.
enabled = yes

# Enables collection of span metrics using the `span.processing-time` metric.
span-metrics = on

# Select which tags should be included as span and span metric tags. The possible options are:
# - span: the tag is added as a Span tag (i.e. using span.tag(...))
# - metric: the tag is added a a Span metric tag (i.e. using span.tagMetric(...))
# - off: the tag is not used.
#
tags {

# Use the http.url tag.
url = span

# Use the http.method tag.
method = metric

# Use the http.status_code tag.
status-code = metric

# Copy tags from the context into the Spans with the specified purpouse. For example, to copy a customer_type
# tag from the context into the HTTP Server Span created by the instrumentation, the following configuration
# should be added:
#
# from-context {
# customer_type = span
# }
#
from-context {

}
}

operations {

# The default operation name to be used when creating Spans to handle the HTTP client requests. The HTTP
# Client instrumentation will always try to use the HTTP Operation Name Generator configured bellow to get
# a name, but if it fails to generate it then this name will be used.
default = "http.client.request"

# FQCN for a HttpOperationNameGenerator implementation, or ony of the following shorthand forms:
# - hostname: Uses the request Host as the operation name.
# - method: Uses the request HTTP method as the operation name.
#
name-generator = "kamon.armeria.instrumentation.client.KamonArmeriaOperationNameGenerator"
}
}
}
}

kanela {
show-banner = false
modules {
armeria {
name = "Armeria instrumentation"
stoppable = true
instrumentations = [
"kamon.armeria.instrumentation.server.ArmeriaHttpServerInstrumentation",
"kamon.armeria.instrumentation.client.ArmeriaHttpClientInstrumentation"
]
within = [ "io.netty..*", "com.linecorp.armeria..*"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* =========================================================================================
* Copyright © 2013-2020 the kamon project <http://kamon.io/>
*
* Licensed under the Apache License, Version 2.0 (the "License") you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
* =========================================================================================
*/

package kamon.armeria.instrumentation

import kamon.instrumentation.http.HttpMessage.Request
import kamon.instrumentation.http.HttpOperationNameGenerator

import scala.collection.concurrent.TrieMap

trait BaseKamonArmeriaOperationNameGenerator extends HttpOperationNameGenerator {

private val localCache = TrieMap.empty[String, String]
private val normalizePattern = """\$([^<]+)<[^>]+>""".r

def name(request: Request): Option[String] =
Some(
localCache.getOrElseUpdate(key(request), {
// Convert paths of form GET /foo/bar/$paramname<regexp>/blah to foo.bar.paramname.blah.get
val normalisedPath = normalisePath(request.path)
name(request, normalisedPath)
})
)

protected def name(request: Request, normalisedPath: String): String

protected def key(request: Request): String

private def normalisePath(path: String): String = {
val p = normalizePattern.replaceAllIn(path, "$1").replace('/', '.').dropWhile(_ == '.')
val normalisedPath = {
if (p.lastOption.exists(_ != '.')) s"$p."
else p
}
normalisedPath
}
}
Loading

0 comments on commit d80e40b

Please sign in to comment.