-
Notifications
You must be signed in to change notification settings - Fork 325
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…569)
- Loading branch information
1 parent
93fb8e6
commit 3fdca7d
Showing
8 changed files
with
332 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>apm-agent-plugins</artifactId> | ||
<groupId>co.elastic.apm</groupId> | ||
<version>1.5.1-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>apm-scheduled-annotation-plugin</artifactId> | ||
<name>${project.groupId}:${project.artifactId}</name> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework</groupId> | ||
<artifactId>spring-context</artifactId> | ||
<version>${version.spring}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>javax</groupId> | ||
<artifactId>javaee-api</artifactId> | ||
<version>7.0</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
109 changes: 109 additions & 0 deletions
109
...n/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/*- | ||
* #%L | ||
* Elastic APM Java agent | ||
* %% | ||
* Copyright (C) 2018 - 2019 Elastic and contributors | ||
* %% | ||
* 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. | ||
* #L% | ||
*/ | ||
package co.elastic.apm.agent.spring.scheduled; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
|
||
import javax.annotation.Nullable; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import co.elastic.apm.agent.bci.ElasticApmInstrumentation; | ||
import co.elastic.apm.agent.bci.VisibleForAdvice; | ||
import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory.SimpleMethodSignature; | ||
import co.elastic.apm.agent.impl.ElasticApmTracer; | ||
import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration; | ||
import co.elastic.apm.agent.impl.transaction.TraceContext; | ||
import co.elastic.apm.agent.impl.transaction.TraceContextHolder; | ||
import co.elastic.apm.agent.impl.transaction.Transaction; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.NamedElement; | ||
import net.bytebuddy.description.method.MethodDescription; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import net.bytebuddy.matcher.ElementMatchers; | ||
|
||
import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.isInAnyPackage; | ||
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
|
||
public class ScheduledTransactionNameInstrumentation extends ElasticApmInstrumentation { | ||
|
||
@VisibleForAdvice | ||
public static final Logger logger = LoggerFactory.getLogger(ScheduledTransactionNameInstrumentation.class); | ||
|
||
private Collection<String> applicationPackages = Collections.emptyList(); | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
private static void setTransactionName(@SimpleMethodSignature String signature, @Advice.Origin Class<?> clazz, @Advice.Local("transaction") Transaction transaction) { | ||
if (tracer != null) { | ||
TraceContextHolder<?> active = tracer.getActive(); | ||
if (active == null) { | ||
transaction = tracer.startTransaction(TraceContext.asRoot(), null, clazz.getClassLoader()) | ||
.withName(signature) | ||
.withType("scheduled") | ||
.activate(); | ||
|
||
} else { | ||
logger.debug("Not creating transaction for method {} because there is already a transaction running ({})", signature, active); | ||
} | ||
} | ||
} | ||
|
||
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) | ||
public static void onMethodExit(@Nullable @Advice.Local("transaction") Transaction transaction, | ||
@Advice.Thrown Throwable t) { | ||
if (transaction != null) { | ||
transaction.captureException(t) | ||
.deactivate() | ||
.end(); | ||
} | ||
} | ||
|
||
@Override | ||
public void init(ElasticApmTracer tracer) { | ||
applicationPackages = tracer.getConfig(StacktraceConfiguration.class).getApplicationPackages(); | ||
} | ||
|
||
@Override | ||
public ElementMatcher<? super TypeDescription> getTypeMatcher() { | ||
return isInAnyPackage(applicationPackages, ElementMatchers.<NamedElement>none()) | ||
.and(declaresMethod(getMethodMatcher())); | ||
} | ||
|
||
@Override | ||
public ElementMatcher<? super MethodDescription> getMethodMatcher() { | ||
return isAnnotatedWith( | ||
named("org.springframework.scheduling.annotation.Scheduled") | ||
.or(named("org.springframework.scheduling.annotation.Schedules")) | ||
.or(named("javax.ejb.Schedule")) | ||
.or(named("javax.ejb.Schedules")) | ||
); | ||
} | ||
|
||
@Override | ||
public Collection<String> getInstrumentationGroupNames() { | ||
return Arrays.asList("concurrent", "scheduled"); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
...d-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/*- | ||
* #%L | ||
* Elastic APM Java agent | ||
* %% | ||
* Copyright (C) 2018 - 2019 Elastic and contributors | ||
* %% | ||
* 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. | ||
* #L% | ||
*/ | ||
@NonnullApi | ||
package co.elastic.apm.agent.spring.scheduled; | ||
|
||
import co.elastic.apm.agent.annotation.NonnullApi; |
1 change: 1 addition & 0 deletions
1
...n/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
co.elastic.apm.agent.spring.scheduled.ScheduledTransactionNameInstrumentation |
161 changes: 161 additions & 0 deletions
161
...va/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/*- | ||
* #%L | ||
* Elastic APM Java agent | ||
* %% | ||
* Copyright (C) 2018 - 2019 Elastic and contributors | ||
* %% | ||
* 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. | ||
* #L% | ||
*/ | ||
package co.elastic.apm.agent.spring.scheduled; | ||
|
||
import java.util.Collections; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import javax.ejb.Schedule; | ||
|
||
import org.junit.BeforeClass; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.scheduling.annotation.Scheduled; | ||
import org.springframework.scheduling.annotation.Schedules; | ||
|
||
import co.elastic.apm.agent.MockReporter; | ||
import co.elastic.apm.agent.bci.ElasticApmAgent; | ||
import co.elastic.apm.agent.configuration.SpyConfiguration; | ||
import co.elastic.apm.agent.impl.ElasticApmTracer; | ||
import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; | ||
import net.bytebuddy.agent.ByteBuddyAgent; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
|
||
class ScheduledTransactionNameInstrumentationTest { | ||
|
||
private static MockReporter reporter; | ||
private static ElasticApmTracer tracer; | ||
|
||
@BeforeClass | ||
@BeforeAll | ||
static void setUpAll() { | ||
reporter = new MockReporter(); | ||
tracer = new ElasticApmTracerBuilder() | ||
.configurationRegistry(SpyConfiguration.createSpyConfig()) | ||
.reporter(reporter) | ||
.build(); | ||
ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), | ||
Collections.singletonList(new ScheduledTransactionNameInstrumentation())); | ||
} | ||
|
||
|
||
@Test | ||
void testSpringScheduledAnnotatedMethodsAreTraced() { | ||
reporter.reset(); | ||
SpringCounter springCounter = new SpringCounter(); | ||
springCounter.scheduled(); | ||
springCounter.scheduled(); | ||
assertThat(reporter.getTransactions().size()).isEqualTo(springCounter.getInvocationCount()); | ||
assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("SpringCounter#scheduled"); | ||
} | ||
|
||
@Test | ||
void testSpringJ8RepeatableScheduledAnnotatedMethodsAreTraced() { | ||
reporter.reset(); | ||
SpringCounter springCounter = new SpringCounter(); | ||
springCounter.scheduledJava8Repeatable(); | ||
springCounter.scheduledJava8Repeatable(); | ||
assertThat(reporter.getTransactions().size()).isEqualTo(springCounter.getInvocationCount()); | ||
assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("SpringCounter#scheduledJava8Repeatable"); | ||
} | ||
|
||
@Test | ||
void testSpringJ7RepeatableScheduledAnnotatedMethodsAreTraced() { | ||
reporter.reset(); | ||
SpringCounter springCounter = new SpringCounter(); | ||
springCounter.scheduledJava7Repeatable(); | ||
springCounter.scheduledJava7Repeatable(); | ||
assertThat(reporter.getTransactions().size()).isEqualTo(springCounter.getInvocationCount()); | ||
assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("SpringCounter#scheduledJava7Repeatable"); | ||
} | ||
|
||
@Test | ||
void testJeeScheduledAnnotatedMethodsAreTraced() { | ||
reporter.reset(); | ||
JeeCounter jeeCounter = new JeeCounter(); | ||
jeeCounter.scheduled(); | ||
jeeCounter.scheduled(); | ||
assertThat(reporter.getTransactions().size()).isEqualTo(jeeCounter.getInvocationCount()); | ||
assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("JeeCounter#scheduled"); | ||
} | ||
|
||
@Test | ||
void testJeeJ7RepeatableScheduledAnnotatedMethodsAreTraced() { | ||
reporter.reset(); | ||
JeeCounter jeeCounter = new JeeCounter(); | ||
jeeCounter.scheduledJava7Repeatable(); | ||
jeeCounter.scheduledJava7Repeatable(); | ||
assertThat(reporter.getTransactions().size()).isEqualTo(jeeCounter.getInvocationCount()); | ||
assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("JeeCounter#scheduledJava7Repeatable"); | ||
} | ||
|
||
|
||
private class SpringCounter { | ||
private AtomicInteger count = new AtomicInteger(0); | ||
|
||
@Scheduled(fixedDelay = 5) | ||
public void scheduled() { | ||
this.count.incrementAndGet(); | ||
} | ||
|
||
@Scheduled(fixedDelay = 5) | ||
@Scheduled(fixedDelay = 10) | ||
public void scheduledJava8Repeatable() { | ||
this.count.incrementAndGet(); | ||
} | ||
|
||
@Schedules({ | ||
@Scheduled(fixedDelay = 5), | ||
@Scheduled(fixedDelay = 10) | ||
}) | ||
public void scheduledJava7Repeatable() { | ||
this.count.incrementAndGet(); | ||
} | ||
|
||
public int getInvocationCount() { | ||
return this.count.get(); | ||
} | ||
} | ||
|
||
private class JeeCounter { | ||
private AtomicInteger count = new AtomicInteger(0); | ||
|
||
@Schedule(minute = "5") | ||
public void scheduled() { | ||
this.count.incrementAndGet(); | ||
} | ||
|
||
@javax.ejb.Schedules({ | ||
@Schedule(minute = "5"), | ||
@Schedule(minute = "10") | ||
}) | ||
public void scheduledJava7Repeatable() { | ||
this.count.incrementAndGet(); | ||
} | ||
|
||
public int getInvocationCount() { | ||
return this.count.get(); | ||
} | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters