This is a necessary step to benefit from most of the instrumentation modules available in Kamon and even though it is
just about adding a -javaagent:aspectjweaver.jar
option when starting your JVM, this topic can become tricky in some
scenarios so we decided to dedicate a whole recipe to it.
You can get the AspectJ Weaver latest stable release from the official website or simply search for it in Maven Central
and then, as mentioned above, start your JVM with one additional option: -javaagent:path-to-aspectjweaver.jar
.
That’s it, the Weaver will pick up the instrumentation from our modules and you will get the metrics and traces you need.
In some cases though, it’s not so straight forward, specially if:
In any of those scenarios just follow in relevant sections bellow.
The simplest solution is to use the sbt-aspectj-runner plugin. This plugin will ensure that the instrumentation is
applied to your classes regardless of whether you are forking the process (via fork in run := true
) or not. To get it
working add these lines to your project/plugins.sbt
file:
resolvers += Resolver.bintrayRepo("kamon-io", "sbt-plugins")
addSbtPlugin("io.kamon" % "sbt-aspectj-runner" % "1.1.0")
That’s it! You can visit the GitHub repo for additional details on how the plugin works.
Once again, the sbt-aspectj-runner is the way to go. The plugin has special variants for Play applications that will have special treatment of Play’s infrastructure for running on Development mode, to include it you must add the right plugin depending on your Play version:
resolvers += Resolver.bintrayIvyRepo("kamon-io", "sbt-plugins")
addSbtPlugin("io.kamon" % "sbt-aspectj-runner-play-2.6" % "1.1.0")
resolvers += Resolver.bintrayIvyRepo("kamon-io", "sbt-plugins")
addSbtPlugin("io.kamon" % "sbt-aspectj-play-runner" % "1.0.4")
Since sbt-assembly puts your entire classpath in a single jar, it needs to merge files that have the same path across
several jars and that includes the META-INF/aop.xml
files that ship in all the instrumentation modules. The default
merge strategy will resolve the conflict by picking the first file found in the classpath and even though that would
generate a jar with all the instrumentation, you will notice that some instrumentation is working and some is missing,
depending on which aop.xml
file was picked.
Cool thing is, there is a custom merge strategy that takes care of merging these files. This is how it looks like:
// Create a new MergeStrategy for aop.xml files
val aopMerge: MergeStrategy = new MergeStrategy {
val name = "aopMerge"
import scala.xml._
import scala.xml.dtd._
def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = {
val dt = DocType("aspectj", PublicID("-//AspectJ//DTD//EN", "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"), Nil)
val file = MergeStrategy.createMergeTarget(tempDir, path)
val xmls: Seq[Elem] = files.map(XML.loadFile)
val aspectsChildren: Seq[Node] = xmls.flatMap(_ \\ "aspectj" \ "aspects" \ "_")
val weaverChildren: Seq[Node] = xmls.flatMap(_ \\ "aspectj" \ "weaver" \ "_")
val options: String = xmls.map(x => (x \\ "aspectj" \ "weaver" \ "@options").text).mkString(" ").trim
val weaverAttr = if (options.isEmpty) Null else new UnprefixedAttribute("options", options, Null)
val aspects = new Elem(null, "aspects", Null, TopScope, false, aspectsChildren: _*)
val weaver = new Elem(null, "weaver", weaverAttr, TopScope, false, weaverChildren: _*)
val aspectj = new Elem(null, "aspectj", Null, TopScope, false, aspects, weaver)
XML.save(file.toString, aspectj, "UTF-8", xmlDecl = false, dt)
IO.append(file, IO.Newline.getBytes(IO.defaultCharset))
Right(Seq(file -> path))
}
}
// Use defaultMergeStrategy with a case for aop.xml
// I like this better than the inline version mentioned in assembly's README
val customMergeStrategy: String => MergeStrategy = {
case PathList("META-INF", "aop.xml") =>
aopMerge
case s =>
defaultMergeStrategy(s)
}
// Use the customMergeStrategy in your settings
mergeStrategy in assembly := customMergeStrategy
You might need to integrate this slightly different in your build if you have other merge strategy configurations. Then
the fat jar can be ran as usual with the -javaagent:path-to-weaver.jar
option.
You can use the sbt-javaagent plugin together with sbt-native-packager to get the AspectJ Weaver options automatically
added to the startup scripts. To achieve this, first add the plugin to your project/plugins.sbt
file:
addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.4")
And enable the plugin in your build.sbt
file:
lazy val root = (project in file(".")).enablePlugins(JavaAppPackaging, JavaAgent) // (1)
javaAgents += "org.aspectj" % "aspectjweaver" % "1.8.13" // (2)
javaOptions in Universal += "-Dorg.aspectj.tracing.factory=default" // (3)
A few comments from the above:
JavaAgent
plugin in your project, if not the root project then make sure you do it in
the right one.You can find additional details on the sbt-javaagent GitHub repo