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

Optimize within pattern matching for Kanela modules [WIP] #141

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ jobs:
with:
java-version: ${{ matrix.java }}
- name: Test
run: ./gradlew wrapper --gradle-version=7.3.1 :agent:publishToMavenLocal && ./gradlew wrapper --gradle-version=7.3.1 assemble && ./travis-test.sh
run: ./gradlew :agent:publishToMavenLocal && ./gradlew assemble && ./travis-test.sh

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ crashlytics.properties
crashlytics-build.properties
fabric.properties

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.html
hs_err_pid*

/agent/src/main/scala/*.sc
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ please squash them into a single commit. Let the PR rain begin!


[open an issue]: https://github.com/kamon-io/Kamon/issues/new
[mailing list]: https://groups.google.com/forum/#!forum/kamon-user
[mailing list]: https://groups.google.com/g/kamon-user
[commit message conventions]: http://spray.io/project-info/contributing/
[CLA]: https://docs.google.com/forms/d/1G_IDrBTFzOMwHvhxfKRBwNtpRelSa_MZ6jecH8lpTlc/viewform
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
**Kanela** is a Java Agent written in Java 8+ and powered by [ByteBuddy] with some additionally [ASM] features to provide a simple way to instrument applications running on the JVM and allow introduce Kamon features such as context propagation and metrics.


[ByteBuddy]:http://bytebuddy.net/#/
[ASM]:http://asm.ow2.org/
[ByteBuddy]:https://bytebuddy.net/#/
[ASM]:https://asm.ow2.io/
12 changes: 5 additions & 7 deletions agent-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,21 @@ plugins {
id 'java'
id 'application'
id 'scala'
id 'com.github.maiflai.scalatest' version '0.31'
id 'com.github.maiflai.scalatest' version '0.32'
}

mainClassName = 'app.kanela.JavaMainWithAgent'

dependencies {
implementation "org.scala-lang:scala-library:${scala_version}"
implementation 'org.slf4j:slf4j-api:1.7.25'
implementation 'ch.qos.logback:logback-core:1.2.3'
implementation 'ch.qos.logback:logback-classic:1.2.3'
implementation 'ch.qos.logback:logback-classic:1.2.11'

implementation "io.kamon:kanela-agent:${agent_version}"

provided(kamon_agent_dep)

testImplementation 'org.mockito:mockito-core:2.28.2'
testImplementation 'org.scalatest:scalatest_2.12:3.0.8'
testImplementation 'org.mockito:mockito-core:4.6.1'
testImplementation 'org.scalatest:scalatest_2.12:3.2.12'
testRuntimeOnly 'org.pegdown:pegdown:1.6.0'
}

Expand Down Expand Up @@ -106,5 +104,5 @@ gradle.taskGraph.whenReady { taskGraph ->
KamonAgentTestRunning = result
}

test.reports.html.enabled = false
test.reports.html.required = false

24 changes: 12 additions & 12 deletions agent-test/src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
kanela {
modules {
example-module {
name = "Example Module"
stoppable = false
instrumentations = [
"app.kanela.instrumentation.MonitorInstrumentation",
]
within = [ "app.kanela.cases.+" ]
}
modules {
example-module {
name = "Example Module"
stoppable = false
instrumentations = [
"app.kanela.instrumentation.MonitorInstrumentation",
]
within = [ "app\\.kanela\\.cases\\..+" ]
}
debug-mode = false
class-dumper.enabled = false
}
}
debug-mode = false
class-dumper.enabled = false
}
4 changes: 2 additions & 2 deletions agent-test/src/test/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ kanela {
instrumentations = [ ] // written by spec

within = [
"app.kanela..*",
"java.net.*"
"app\\.kanela\\..*",
"java\\.net\\..*",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, these could be .+, but it shouldn't really matter

]

bootstrap-injection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ package app.kanela.specs

import app.kanela.cases.simple.TestClass
import kanela.agent.attacher.Attacher
import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.collection.mutable.ListBuffer

class AttachInRuntimeSpec extends FlatSpec with Matchers with BeforeAndAfterAll {
class AttachInRuntimeSpec extends AnyFlatSpec with Matchers {

"Kanela agent" should "be able to attach in runtime and instrument the loaded classes" in {
val testClass = new TestClass()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

package app.kanela.specs

import java.net.{HttpURLConnection, URL}

import kanela.agent.attacher.Attacher
import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import java.net.{HttpURLConnection, URL}

class BootstrapInstrumentationSpec extends FlatSpec with Matchers with BeforeAndAfterAll {
class BootstrapInstrumentationSpec extends AnyFlatSpec with Matchers {
"The Bootstrap Injection feature" should
"provide the necessary helper classes when we instrument the bootstrap class loader" in {
Attacher.attach()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ package app.kanela.specs

import app.kanela.cases.bridge.PrivateClass
import app.kanela.instrumentation.PrivateClassBridge
import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers


class BridgeInstrumentationSpec extends FlatSpec with Matchers with BeforeAndAfterAll {
class BridgeInstrumentationSpec extends AnyFlatSpec with Matchers {
"The Bridge" should
"generate an accessor method for a private field or method" in {
val privateClass = new PrivateClass()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ package app.kanela.specs

import app.kanela.cases.multimixins.MixinAware.{MixinAware1, MixinAware2, MixinAware3}
import app.kanela.cases.multimixins.WithMultiMixinsClass
import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class MultiMixinsInstrumentationSpec extends FlatSpec with Matchers with BeforeAndAfterAll {
class MultiMixinsInstrumentationSpec extends AnyFlatSpec with Matchers {

"Multiple Mixins over a single subType" should
"introduce all types appropriately" in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
package app.kanela.specs

import app.kanela.cases.simple.{SpyAware, TestClass}
import org.scalatest.{FlatSpec, Matchers}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.collection.mutable.ListBuffer

class SimpleInstrumentationSpec extends FlatSpec with Matchers {
class SimpleInstrumentationSpec extends AnyFlatSpec with Matchers {

"An Advisor with OnMethodEnter and OnMethodExit" should "be able to instrument a specific method of a class" in {
val testClass = new TestClass()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ import app.kanela.cases.simple.TestClass
import kanela.agent.attacher.Attacher
import kanela.agent.broker.EventBroker
import kanela.agent.reinstrument.Reinstrumenter.ReinstrumentationProtocol._
import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.collection.mutable.ListBuffer

class StoppableInstrumentationSpec extends FlatSpec with Matchers with BeforeAndAfterAll {
class StoppableInstrumentationSpec extends AnyFlatSpec with Matchers {

"A module stoppable" should "be able to retransform and reset instrumentation under critical state" in {
val testClass = new TestClass()
Expand Down
28 changes: 14 additions & 14 deletions agent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ plugins {
id 'scala'
id 'maven-publish'
id 'signing'
id 'com.github.maiflai.scalatest' version '0.31'
id 'com.github.johnrengelman.shadow' version '7.0.0'
id 'com.github.maiflai.scalatest' version '0.32'
id 'com.github.johnrengelman.shadow' version '7.1.2'
}

configurations {
kamonInstrumentationBundle
}

dependencies {
implementation 'com.typesafe:config:1.3.4'
implementation 'com.typesafe:config:1.4.2'
implementation 'com.blogspot.mydailyjava:weak-lock-free:0.18'

implementation 'org.tinylog:tinylog:1.3.6'
Expand All @@ -38,8 +38,8 @@ dependencies {

implementation files('libs/byte-buddy-1.11.18.jar')

testImplementation 'org.mockito:mockito-core:2.28.2'
testImplementation 'org.scalatest:scalatest_2.12:3.0.1'
testImplementation 'org.mockito:mockito-core:4.6.1'
testImplementation 'org.scalatest:scalatest_2.12:3.2.12'
testImplementation "org.scala-lang:scala-library:${scala_version}"
testImplementation 'org.pegdown:pegdown:1.6.0'
}
Expand All @@ -48,8 +48,8 @@ def agentBootstrapClasses = 'kanela/agent/bootstrap/**'

task bootstrapJar(type: Jar) {
// Output to 'bootstrap.jar'.
baseName = 'bootstrap'
version = null
archiveBaseName = 'bootstrap'
archiveVersion = null

from sourceSets.main.output
include agentBootstrapClasses
Expand All @@ -58,8 +58,8 @@ task bootstrapJar(type: Jar) {
shadowJar.dependsOn bootstrapJar

shadowJar {
baseName = 'kanela-agent'
classifier = null
archiveBaseName = 'kanela-agent'
archiveClassifier = null

mergeServiceFiles {
path = 'META-INF/services/org.pmw.tinylog.*'
Expand Down Expand Up @@ -88,14 +88,14 @@ shadowJar {
}

dependencies {
exclude('org.projectlombok:lombok:1.18.20')
exclude('org.projectlombok:lombok:1.18.24')
}

doLast {
def agentBootstrapJar = 'kanela/agent/bootstrap.jar'

// Bundle bootstrap.jar.
ant.jar(update: 'true', destfile: shadowJar.archivePath) {
ant.jar(update: 'true', destfile: shadowJar.archiveFile.get()) {
mappedresources {
fileset(file: bootstrapJar.archivePath)
globmapper(from: '*', to: agentBootstrapJar)
Expand Down Expand Up @@ -123,7 +123,7 @@ def pomConfig = {
licenses {
license {
name "The Apache Software License, Version 2.0"
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
url "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
Expand Down Expand Up @@ -164,7 +164,7 @@ publishing {
def root = asNode()
root.appendNode('description', 'The Kamon Instrumentation Agent')
root.appendNode('name', 'Kanela')
root.appendNode('url', 'http://kamon.io')
root.appendNode('url', 'https://kamon.io/')
root.children().last() + pomConfig
}
}
Expand Down Expand Up @@ -211,4 +211,4 @@ classes {
dependsOn createBuildInfoFile
}

test.reports.html.enabled = false
test.reports.html.required = false
8 changes: 4 additions & 4 deletions agent/src/main/java/kanela/agent/InstrumentationLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ public class InstrumentationLoader {
* Load from the current classpath all defined instrumentations {@link InstrumentationBuilder}.
*
* @param instrumentation {@link Instrumentation}
* @param ctxClassloader {@link ClassLoader}
* @param configuration {@link KanelaConfiguration}
* @param ctxClassloader {@link ClassLoader}
* @param configuration {@link KanelaConfiguration}
* @return a list of {@link KanelaFileTransformer}
*/
public static List<KanelaFileTransformer> load(Instrumentation instrumentation, ClassLoader ctxClassloader, KanelaConfiguration configuration) {
return configuration.getAgentModules().map((moduleConfiguration) -> {
Logger.info(() -> format("Loading {0} ", moduleConfiguration.getName()));
Logger.info(() -> format("Loading {0}", moduleConfiguration.getName()));
return moduleConfiguration.getInstrumentations()
.flatMap(instrumentationClassName -> loadInstrumentation(instrumentationClassName, ctxClassloader))
.filter(kanelaInstrumentation -> kanelaInstrumentation.isEnabled(moduleConfiguration))
Expand All @@ -55,7 +55,7 @@ public static List<KanelaFileTransformer> load(Instrumentation instrumentation,
private static Option<InstrumentationBuilder> loadInstrumentation(String instrumentationClassName, ClassLoader classLoader) {
return Try.of(() -> {
Logger.info(() -> format(" ==> Loading {0} ", instrumentationClassName));
return (InstrumentationBuilder) Class.forName(instrumentationClassName, true, classLoader).newInstance();
return (InstrumentationBuilder) Class.forName(instrumentationClassName, true, classLoader).getConstructors()[0].newInstance();
}).onFailure((cause) -> Logger.warn(() -> format("Error trying to load Instrumentation: {0} with error: {1}", instrumentationClassName, cause))
).toOption();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,12 @@ public Builder withInterceptorFor(ElementMatcher.Junction<MethodDescription> met
return this;
}

public Builder withTransformation(Function4<DynamicType.Builder, TypeDescription, ClassLoader, JavaModule, DynamicType.Builder> f) {
public Builder withTransformation(Function4<DynamicType.Builder<?>, TypeDescription, ClassLoader, JavaModule, DynamicType.Builder<?>> f) {
transformers.add(withTransformer(f));
return this;
}

private AgentBuilder.Transformer withTransformer(Function4<DynamicType.Builder, TypeDescription, ClassLoader, JavaModule, DynamicType.Builder> f) { return f::apply; }
private AgentBuilder.Transformer withTransformer(Function4<DynamicType.Builder<?>, TypeDescription, ClassLoader, JavaModule, DynamicType.Builder<?>> f) { return f::apply; }

public InstrumentationDescription build() {
return new InstrumentationDescription(this);
Expand Down
16 changes: 14 additions & 2 deletions agent/src/main/java/kanela/agent/builder/KanelaAgentBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import kanela.agent.cache.PoolStrategyCache;
import kanela.agent.resubmitter.PeriodicResubmitter;
import kanela.agent.util.ListBuilder;
import kanela.agent.util.PatternMatcher;
import kanela.agent.util.conf.KanelaConfiguration;
import kanela.agent.util.log.Logger;
import lombok.Value;
Expand All @@ -36,6 +37,7 @@
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.NameMatcher;

import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
Expand Down Expand Up @@ -141,10 +143,20 @@ private AgentBuilder.Listener additionalListeners() {
}

private ElementMatcher.Junction<NamedElement> ignoreMatches() {
return not(nameMatches(moduleDescription.getWithinPackage()));
final String withinPackage = moduleDescription.getWithinPackage();
if (withinPackage.isEmpty()) {
return any();
} else {
return not(new NameMatcher<>(new PatternMatcher(withinPackage)));
}
}

private ElementMatcher.Junction<NamedElement> moduleExcludes() {
return nameMatches(moduleDescription.getExcludePackage());
final String moduleExcludes = moduleDescription.getExcludePackage();
if (moduleExcludes.isEmpty()) {
return none();
} else {
return new NameMatcher<>(new PatternMatcher(moduleExcludes));
}
}
}
18 changes: 18 additions & 0 deletions agent/src/main/java/kanela/agent/util/PatternMatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package kanela.agent.util;

import net.bytebuddy.matcher.ElementMatcher;

import java.util.regex.Pattern;

public class PatternMatcher extends ElementMatcher.Junction.AbstractBase<String> {
private final Pattern regex;

public PatternMatcher(final String regex) {
this.regex = Pattern.compile(regex);
}

@Override
public boolean matches(final String target) {
return regex.matcher(target).matches();
}
}
Loading