Merge tag 'v0.50.0' into correct-history
This commit is contained in:
commit
6d1a58d151
|
@ -142,11 +142,21 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
- TEST_TASK: testJava13
|
- TEST_TASK: testJava13
|
||||||
|
|
||||||
|
test_zulu13:
|
||||||
|
<<: *default_test_job
|
||||||
|
environment:
|
||||||
|
- TEST_TASK: testJavaZULU13
|
||||||
|
|
||||||
test_14:
|
test_14:
|
||||||
<<: *default_test_job
|
<<: *default_test_job
|
||||||
environment:
|
environment:
|
||||||
- TEST_TASK: testJava14
|
- TEST_TASK: testJava14
|
||||||
|
|
||||||
|
test_zulu14:
|
||||||
|
<<: *default_test_job
|
||||||
|
environment:
|
||||||
|
- TEST_TASK: testJavaZULU14
|
||||||
|
|
||||||
check:
|
check:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
|
|
||||||
|
@ -243,25 +253,25 @@ workflows:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
# - test_ibm8:
|
# - test_ibm8:
|
||||||
# requires:
|
# requires:
|
||||||
# - build
|
# - build
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
# only: /.*/
|
# only: /.*/
|
||||||
# - test_zulu8:
|
# - test_zulu8:
|
||||||
# requires:
|
# requires:
|
||||||
# - build
|
# - build
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
# only: /.*/
|
# only: /.*/
|
||||||
# - test_9:
|
# - test_9:
|
||||||
# requires:
|
# requires:
|
||||||
# - build
|
# - build
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
# only: /.*/
|
# only: /.*/
|
||||||
# - test_10:
|
# - test_10:
|
||||||
# requires:
|
# requires:
|
||||||
# - build
|
# - build
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
# only: /.*/
|
# only: /.*/
|
||||||
|
@ -273,13 +283,13 @@ workflows:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
# - test_zulu11:
|
# - test_zulu11:
|
||||||
# requires:
|
# requires:
|
||||||
# - build
|
# - build
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
# only: /.*/
|
# only: /.*/
|
||||||
# - test_12:
|
# - test_12:
|
||||||
# requires:
|
# requires:
|
||||||
# - build
|
# - build
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
# only: /.*/
|
# only: /.*/
|
||||||
|
@ -289,12 +299,24 @@ workflows:
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
# only: /.*/
|
# only: /.*/
|
||||||
|
# - test_zulu13:
|
||||||
|
# requires:
|
||||||
|
# - build
|
||||||
|
# filters:
|
||||||
|
# tags:
|
||||||
|
# only: /.*/
|
||||||
- test_14:
|
- test_14:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
|
# - test_zulu14:
|
||||||
|
# requires:
|
||||||
|
# - build
|
||||||
|
# filters:
|
||||||
|
# tags:
|
||||||
|
# only: /.*/
|
||||||
|
|
||||||
- check:
|
- check:
|
||||||
requires:
|
requires:
|
||||||
|
@ -323,7 +345,9 @@ workflows:
|
||||||
# - test_zulu11
|
# - test_zulu11
|
||||||
# - test_12
|
# - test_12
|
||||||
# - test_13
|
# - test_13
|
||||||
|
# - test_zulu13
|
||||||
- test_14
|
- test_14
|
||||||
|
# - test_zulu14
|
||||||
- check
|
- check
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
|
@ -344,7 +368,9 @@ workflows:
|
||||||
# - test_zulu11
|
# - test_zulu11
|
||||||
# - test_12
|
# - test_12
|
||||||
# - test_13
|
# - test_13
|
||||||
|
# - test_zulu13
|
||||||
- test_14
|
- test_14
|
||||||
|
# - test_zulu14
|
||||||
- check
|
- check
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
name: Draft release notes on tag
|
||||||
|
on:
|
||||||
|
create
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
draft_release_notes:
|
||||||
|
if: github.event.ref_type == 'tag' && github.event.master_branch == 'master'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Get milestone title
|
||||||
|
id: milestoneTitle
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
result-encoding: string
|
||||||
|
script: |
|
||||||
|
// Our tags are of the form vX.X.X and milestones don't have the "v"
|
||||||
|
return '${{github.event.ref}}'.startsWith('v') ? '${{github.event.ref}}'.substring(1) : '${{github.event.ref}}';
|
||||||
|
- name: Get milestone for tag
|
||||||
|
id: milestone
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
script: |
|
||||||
|
const options = github.issues.listMilestonesForRepo.endpoint.merge({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'all'
|
||||||
|
})
|
||||||
|
|
||||||
|
const milestones = await github.paginate(options)
|
||||||
|
|
||||||
|
const milestone = milestones.find( milestone => milestone.title == '${{steps.milestoneTitle.outputs.result}}' )
|
||||||
|
|
||||||
|
if (milestone) {
|
||||||
|
return milestone.number
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
- name: Get pull requests for milestone
|
||||||
|
if: fromJSON(steps.milestone.outputs.result)
|
||||||
|
id: pulls
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
script: |
|
||||||
|
const options = github.pulls.list.endpoint.merge({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'closed'
|
||||||
|
})
|
||||||
|
|
||||||
|
const pullRequests = await github.paginate(options)
|
||||||
|
|
||||||
|
return pullRequests.filter(pullRequest => pullRequest.merged_at
|
||||||
|
&& pullRequest.milestone
|
||||||
|
&& pullRequest.milestone.number == ${{steps.milestone.outputs.result}})
|
||||||
|
- name: Generate release notes text
|
||||||
|
if: fromJSON(steps.milestone.outputs.result)
|
||||||
|
id: generate
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
result-encoding: string
|
||||||
|
script: |
|
||||||
|
var draftText = "# Improvements \n\n# Changes \n\n"
|
||||||
|
for (let pull of ${{ steps.pulls.outputs.result }}) {
|
||||||
|
draftText += "* " + pull.title + " #" + pull.number + " \n"
|
||||||
|
}
|
||||||
|
draftText += "\n# Fixes \n"
|
||||||
|
return draftText
|
||||||
|
- name: Create release notes
|
||||||
|
if: fromJSON(steps.milestone.outputs.result)
|
||||||
|
# can't use actions/create-release because it doesn't like the text coming from JS
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
script: |
|
||||||
|
await github.repos.createRelease({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
tag_name: '${{ github.event.ref }}',
|
||||||
|
name: '${{ steps.milestoneTitle.outputs.result }}',
|
||||||
|
draft: true,
|
||||||
|
body: `${{ steps.generate.outputs.result }}`
|
||||||
|
})
|
|
@ -0,0 +1,63 @@
|
||||||
|
name: Increment milestones on tag
|
||||||
|
on:
|
||||||
|
create
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
increment_milestone:
|
||||||
|
if: github.event.ref_type == 'tag' && github.event.master_branch == 'master'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Get milestone title
|
||||||
|
id: milestoneTitle
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
result-encoding: string
|
||||||
|
script: |
|
||||||
|
// Our tags are of the form vX.X.X and milestones don't have the "v"
|
||||||
|
return '${{github.event.ref}}'.startsWith('v') ? '${{github.event.ref}}'.substring(1) : '${{github.event.ref}}';
|
||||||
|
- name: Get milestone for tag
|
||||||
|
id: milestone
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
script: |
|
||||||
|
const options = github.issues.listMilestonesForRepo.endpoint.merge({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'all'
|
||||||
|
})
|
||||||
|
|
||||||
|
const milestones = await github.paginate(options)
|
||||||
|
|
||||||
|
const milestone = milestones.find( milestone => milestone.title == '${{steps.milestoneTitle.outputs.result}}' )
|
||||||
|
|
||||||
|
if (milestone) {
|
||||||
|
return milestone.number
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
- name: Close milestone
|
||||||
|
if: fromJSON(steps.milestone.outputs.result)
|
||||||
|
uses: actions/github-script@0.9.0
|
||||||
|
with:
|
||||||
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
script: |
|
||||||
|
await github.issues.updateMilestone({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'closed',
|
||||||
|
milestone_number: ${{steps.milestone.outputs.result}}
|
||||||
|
})
|
||||||
|
- name: Get next minor version
|
||||||
|
if: fromJSON(steps.milestone.outputs.result)
|
||||||
|
id: semvers
|
||||||
|
uses: WyriHaximus/github-action-next-semvers@0.1.0
|
||||||
|
with:
|
||||||
|
version: ${{steps.milestoneTitle.outputs.result}}
|
||||||
|
- name: Create next milestone
|
||||||
|
if: fromJSON(steps.milestone.outputs.result)
|
||||||
|
uses: WyriHaximus/github-action-create-milestone@0.1.0
|
||||||
|
with:
|
||||||
|
title: ${{ steps.semvers.outputs.minor }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -34,17 +34,18 @@ to capture telemetry from a number of popular libraries and frameworks.
|
||||||
| [JMS](https://javaee.github.io/javaee-spec/javadocs/javax/jms/package-summary.html) | 1.1+ |
|
| [JMS](https://javaee.github.io/javaee-spec/javadocs/javax/jms/package-summary.html) | 1.1+ |
|
||||||
| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3+ |
|
| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3+ |
|
||||||
| [Kafka](https://kafka.apache.org/20/javadoc/overview-summary.html) | 0.11+ |
|
| [Kafka](https://kafka.apache.org/20/javadoc/overview-summary.html) | 0.11+ |
|
||||||
| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 5.0+ |
|
| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ |
|
||||||
| [Log4j](https://logging.apache.org/log4j/2.x/) | 1.1+ |
|
| [Log4j](https://logging.apache.org/log4j/2.x/) | 1.1+ |
|
||||||
| [Logback](https://github.com/qos-ch/logback) | 1.0+ |
|
| [Logback](https://github.com/qos-ch/logback) | 1.0+ |
|
||||||
| [MongoDB Drivers](https://mongodb.github.io/mongo-java-driver/) | 3.3+ |
|
| [MongoDB Drivers](https://mongodb.github.io/mongo-java-driver/) | 3.3+ |
|
||||||
| [Netty](https://github.com/netty/netty) | 4.0+ |
|
| [Netty](https://github.com/netty/netty) | 3.8+ |
|
||||||
| [OkHttp](https://github.com/square/okhttp/) | 3.0+ |
|
| [OkHttp](https://github.com/square/okhttp/) | 3.0+ |
|
||||||
| [Play](https://github.com/playframework/playframework) | 2.4+ (not including 2.8.x yet) |
|
| [Play](https://github.com/playframework/playframework) | 2.3+ (not including 2.8.x yet) |
|
||||||
| [Play WS](https://github.com/playframework/play-ws) | 1.0+ |
|
| [Play WS](https://github.com/playframework/play-ws) | 1.0+ |
|
||||||
| [Project Reactor](https://github.com/reactor/reactor-core) | 3.1+ |
|
|
||||||
| [RabbitMQ Client](https://github.com/rabbitmq/rabbitmq-java-client) | 2.7+ |
|
| [RabbitMQ Client](https://github.com/rabbitmq/rabbitmq-java-client) | 2.7+ |
|
||||||
| [Ratpack](https://github.com/ratpack/ratpack) | 1.5+ |
|
| [Ratpack](https://github.com/ratpack/ratpack) | 1.5+ |
|
||||||
|
| [Reactor](https://github.com/reactor/reactor-core) | 3.1+ |
|
||||||
|
| [Rediscala](https://github.com/etaty/rediscala) | 1.8+ |
|
||||||
| [RMI](https://docs.oracle.com/en/java/javase/11/docs/api/java.rmi/java/rmi/package-summary.html) | Java 7+ |
|
| [RMI](https://docs.oracle.com/en/java/javase/11/docs/api/java.rmi/java/rmi/package-summary.html) | Java 7+ |
|
||||||
| [RxJava](https://github.com/ReactiveX/RxJava) | 1.0+ |
|
| [RxJava](https://github.com/ReactiveX/RxJava) | 1.0+ |
|
||||||
| [Servlet](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/package-summary.html) | 2.3+ |
|
| [Servlet](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/package-summary.html) | 2.3+ |
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class State {
|
||||||
|
|
||||||
public void setParentSpan(final Span parentSpan) {
|
public void setParentSpan(final Span parentSpan) {
|
||||||
final boolean result = parentSpanRef.compareAndSet(null, parentSpan);
|
final boolean result = parentSpanRef.compareAndSet(null, parentSpan);
|
||||||
if (!result && parentSpanRef.get() != parentSpan) {
|
if (!result) {
|
||||||
log.debug(
|
log.debug(
|
||||||
"Failed to set parent span because another parent span is already set {}: new: {}, old: {}",
|
"Failed to set parent span because another parent span is already set {}: new: {}, old: {}",
|
||||||
this,
|
this,
|
||||||
|
|
|
@ -47,6 +47,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ToString(includeFieldNames = true)
|
@ToString(includeFieldNames = true)
|
||||||
public class Config {
|
public class Config {
|
||||||
|
private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
|
||||||
|
|
||||||
/** Config keys below */
|
/** Config keys below */
|
||||||
private static final String PREFIX = "ota.";
|
private static final String PREFIX = "ota.";
|
||||||
|
@ -404,7 +405,7 @@ public class Config {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return (T)
|
return (T)
|
||||||
MethodHandles.publicLookup()
|
PUBLIC_LOOKUP
|
||||||
.findStatic(tClass, "valueOf", MethodType.methodType(tClass, String.class))
|
.findStatic(tClass, "valueOf", MethodType.methodType(tClass, String.class))
|
||||||
.invoke(value);
|
.invoke(value);
|
||||||
} catch (final NumberFormatException e) {
|
} catch (final NumberFormatException e) {
|
||||||
|
|
|
@ -71,9 +71,11 @@ class DropwizardAsyncTest extends DropwizardTest {
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("query")
|
@Path("query")
|
||||||
Response query_param(@QueryParam("some") String param) {
|
Response query_param(@QueryParam("some") String param, @Suspended final AsyncResponse asyncResponse) {
|
||||||
controller(QUERY_PARAM) {
|
executor.execute {
|
||||||
Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build()
|
controller(QUERY_PARAM) {
|
||||||
|
asyncResponse.resume(Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import static io.opentelemetry.auto.bootstrap.instrumentation.java.concurrent.Ad
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.nameMatches;
|
import static net.bytebuddy.matcher.ElementMatchers.nameMatches;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import io.opentelemetry.auto.bootstrap.ContextStore;
|
import io.opentelemetry.auto.bootstrap.ContextStore;
|
||||||
|
@ -59,7 +60,7 @@ public final class JavaExecutorInstrumentation extends AbstractExecutorInstrumen
|
||||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||||
transformers.put(
|
transformers.put(
|
||||||
named("execute").and(takesArgument(0, Runnable.class)),
|
named("execute").and(takesArgument(0, Runnable.class)).and(takesArguments(1)),
|
||||||
JavaExecutorInstrumentation.class.getName() + "$SetExecuteRunnableStateAdvice");
|
JavaExecutorInstrumentation.class.getName() + "$SetExecuteRunnableStateAdvice");
|
||||||
transformers.put(
|
transformers.put(
|
||||||
named("execute").and(takesArgument(0, ForkJoinTask.class)),
|
named("execute").and(takesArgument(0, ForkJoinTask.class)),
|
||||||
|
|
|
@ -29,6 +29,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
|
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
|
||||||
|
import io.opentelemetry.auto.bootstrap.ContextStore;
|
||||||
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
|
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
|
||||||
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
|
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
|
||||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
@ -99,7 +100,28 @@ public final class JaxRsAnnotationsInstrumentation extends Instrumenter.Default
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static SpanWithScope nameSpan(
|
public static SpanWithScope nameSpan(
|
||||||
@Advice.This final Object target, @Advice.Origin final Method method) {
|
@Advice.This final Object target,
|
||||||
|
@Advice.Origin final Method method,
|
||||||
|
@Advice.AllArguments final Object[] args,
|
||||||
|
@Advice.Local("asyncResponse") AsyncResponse asyncResponse) {
|
||||||
|
ContextStore<AsyncResponse, Span> contextStore = null;
|
||||||
|
for (final Object arg : args) {
|
||||||
|
if (arg instanceof AsyncResponse) {
|
||||||
|
asyncResponse = (AsyncResponse) arg;
|
||||||
|
contextStore = InstrumentationContext.get(AsyncResponse.class, Span.class);
|
||||||
|
if (contextStore.get(asyncResponse) != null) {
|
||||||
|
/**
|
||||||
|
* We are probably in a recursive call and don't want to start a new span because it
|
||||||
|
* would replace the existing span in the asyncResponse and cause it to never finish. We
|
||||||
|
* could work around this by using a list instead, but we likely don't want the extra
|
||||||
|
* span anyway.
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (CallDepthThreadLocalMap.incrementCallDepth(Path.class) > 0) {
|
if (CallDepthThreadLocalMap.incrementCallDepth(Path.class) > 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -111,6 +133,10 @@ public final class JaxRsAnnotationsInstrumentation extends Instrumenter.Default
|
||||||
DECORATE.onJaxRsSpan(span, parent, target.getClass(), method);
|
DECORATE.onJaxRsSpan(span, parent, target.getClass(), method);
|
||||||
DECORATE.afterStart(span);
|
DECORATE.afterStart(span);
|
||||||
|
|
||||||
|
if (contextStore != null && asyncResponse != null) {
|
||||||
|
contextStore.put(asyncResponse, span);
|
||||||
|
}
|
||||||
|
|
||||||
return new SpanWithScope(span, currentContextWith(span));
|
return new SpanWithScope(span, currentContextWith(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +144,7 @@ public final class JaxRsAnnotationsInstrumentation extends Instrumenter.Default
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
@Advice.Enter final SpanWithScope spanWithScope,
|
@Advice.Enter final SpanWithScope spanWithScope,
|
||||||
@Advice.Thrown final Throwable throwable,
|
@Advice.Thrown final Throwable throwable,
|
||||||
@Advice.AllArguments final Object[] args) {
|
@Advice.Local("asyncResponse") final AsyncResponse asyncResponse) {
|
||||||
if (spanWithScope == null) {
|
if (spanWithScope == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -133,16 +159,11 @@ public final class JaxRsAnnotationsInstrumentation extends Instrumenter.Default
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncResponse asyncResponse = null;
|
if (asyncResponse != null && !asyncResponse.isSuspended()) {
|
||||||
for (final Object arg : args) {
|
// Clear span from the asyncResponse. Logically this should never happen. Added to be safe.
|
||||||
if (arg instanceof AsyncResponse) {
|
InstrumentationContext.get(AsyncResponse.class, Span.class).put(asyncResponse, null);
|
||||||
asyncResponse = (AsyncResponse) arg;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (asyncResponse != null && asyncResponse.isSuspended()) {
|
if (asyncResponse == null || !asyncResponse.isSuspended()) {
|
||||||
InstrumentationContext.get(AsyncResponse.class, Span.class).put(asyncResponse, span);
|
|
||||||
} else {
|
|
||||||
DECORATE.beforeFinish(span);
|
DECORATE.beforeFinish(span);
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class JDBCDecorator extends DatabaseClientDecorator<DBInfo> {
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
try {
|
try {
|
||||||
dbInfo = JDBCConnectionUrlParser.parse(url, connection.getClientInfo());
|
dbInfo = JDBCConnectionUrlParser.parse(url, connection.getClientInfo());
|
||||||
} catch (final Exception ex) {
|
} catch (final Throwable ex) {
|
||||||
// getClientInfo is likely not allowed.
|
// getClientInfo is likely not allowed.
|
||||||
dbInfo = JDBCConnectionUrlParser.parse(url, null);
|
dbInfo = JDBCConnectionUrlParser.parse(url, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,7 @@ class TestConnection implements Connection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Properties getClientInfo() throws SQLException {
|
Properties getClientInfo() throws SQLException {
|
||||||
throw new UnsupportedOperationException("Test 123")
|
throw new Throwable("Test 123")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Set properties before any plugins get loaded
|
||||||
|
ext {
|
||||||
|
minJavaVersionForTests = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/instrumentation.gradle"
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = "biz.paluch.redis"
|
||||||
|
module = "lettuce"
|
||||||
|
versions = "[4.0.Final,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly group: 'biz.paluch.redis', name: 'lettuce', version: '4.0.Final'
|
||||||
|
main_java8CompileOnly group: 'biz.paluch.redis', name: 'lettuce', version: '4.0.Final'
|
||||||
|
|
||||||
|
testCompile group: 'com.github.kstyrc', name: 'embedded-redis', version: '0.6'
|
||||||
|
testCompile group: 'biz.paluch.redis', name: 'lettuce', version: '4.0.Final'
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'biz.paluch.redis', name: 'lettuce', version: '4.+'
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.lettuce.v4_0;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class LettuceAsyncCommandsInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public LettuceAsyncCommandsInstrumentation() {
|
||||||
|
super("lettuce", "lettuce-4-async");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("com.lambdaworks.redis.AbstractRedisAsyncCommands");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
packageName + ".LettuceClientDecorator", packageName + ".InstrumentationPoints"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod()
|
||||||
|
.and(named("dispatch"))
|
||||||
|
.and(takesArgument(0, named("com.lambdaworks.redis.protocol.RedisCommand"))),
|
||||||
|
// Cannot reference class directly here because it would lead to class load failure on Java7
|
||||||
|
packageName + ".LettuceAsyncCommandsAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.lettuce.v4_0;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class LettuceClientInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public LettuceClientInstrumentation() {
|
||||||
|
super("lettuce");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("com.lambdaworks.redis.RedisClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
packageName + ".LettuceClientDecorator", packageName + ".InstrumentationPoints"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod().and(named("connectStandalone")),
|
||||||
|
// Cannot reference class directly here because it would lead to class load failure on Java7
|
||||||
|
packageName + ".RedisConnectionAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.lettuce.v4_0;
|
||||||
|
|
||||||
|
import static com.lambdaworks.redis.protocol.CommandKeyword.SEGFAULT;
|
||||||
|
import static com.lambdaworks.redis.protocol.CommandType.DEBUG;
|
||||||
|
import static com.lambdaworks.redis.protocol.CommandType.SHUTDOWN;
|
||||||
|
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
||||||
|
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
|
||||||
|
|
||||||
|
import com.lambdaworks.redis.RedisURI;
|
||||||
|
import com.lambdaworks.redis.protocol.AsyncCommand;
|
||||||
|
import com.lambdaworks.redis.protocol.CommandType;
|
||||||
|
import com.lambdaworks.redis.protocol.ProtocolKeyword;
|
||||||
|
import com.lambdaworks.redis.protocol.RedisCommand;
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
|
public final class InstrumentationPoints {
|
||||||
|
|
||||||
|
private static final Set<CommandType> NON_INSTRUMENTING_COMMANDS = EnumSet.of(SHUTDOWN, DEBUG);
|
||||||
|
|
||||||
|
public static SpanWithScope beforeCommand(final RedisCommand<?, ?, ?> command) {
|
||||||
|
final String spanName = command == null ? "Redis Command" : command.getType().name();
|
||||||
|
final Span span =
|
||||||
|
LettuceClientDecorator.TRACER.spanBuilder(spanName).setSpanKind(CLIENT).startSpan();
|
||||||
|
LettuceClientDecorator.DECORATE.afterStart(span);
|
||||||
|
return new SpanWithScope(span, currentContextWith(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void afterCommand(
|
||||||
|
final RedisCommand<?, ?, ?> command,
|
||||||
|
final SpanWithScope spanWithScope,
|
||||||
|
final Throwable throwable,
|
||||||
|
final AsyncCommand<?, ?, ?> asyncCommand) {
|
||||||
|
final Span span = spanWithScope.getSpan();
|
||||||
|
if (throwable != null) {
|
||||||
|
LettuceClientDecorator.DECORATE.onError(span, throwable);
|
||||||
|
LettuceClientDecorator.DECORATE.beforeFinish(span);
|
||||||
|
span.end();
|
||||||
|
} else if (finishSpanEarly(command)) {
|
||||||
|
span.end();
|
||||||
|
} else {
|
||||||
|
asyncCommand.handleAsync(
|
||||||
|
(value, ex) -> {
|
||||||
|
if (ex instanceof CancellationException) {
|
||||||
|
span.setAttribute("db.command.cancelled", true);
|
||||||
|
} else {
|
||||||
|
LettuceClientDecorator.DECORATE.onError(span, ex);
|
||||||
|
}
|
||||||
|
LettuceClientDecorator.DECORATE.beforeFinish(span);
|
||||||
|
span.end();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
spanWithScope.closeScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpanWithScope beforeConnect(final RedisURI redisURI) {
|
||||||
|
final Span span =
|
||||||
|
LettuceClientDecorator.TRACER.spanBuilder("CONNECT").setSpanKind(CLIENT).startSpan();
|
||||||
|
LettuceClientDecorator.DECORATE.afterStart(span);
|
||||||
|
LettuceClientDecorator.DECORATE.onConnection(span, redisURI);
|
||||||
|
return new SpanWithScope(span, currentContextWith(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void afterConnect(final SpanWithScope spanWithScope, final Throwable throwable) {
|
||||||
|
final Span span = spanWithScope.getSpan();
|
||||||
|
if (throwable != null) {
|
||||||
|
LettuceClientDecorator.DECORATE.onError(span, throwable);
|
||||||
|
LettuceClientDecorator.DECORATE.beforeFinish(span);
|
||||||
|
}
|
||||||
|
span.end();
|
||||||
|
spanWithScope.closeScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a redis command should finish its relevant span early (as soon as tags are
|
||||||
|
* added and the command is executed) because these commands have no return values/call backs, so
|
||||||
|
* we must close the span early in order to provide info for the users
|
||||||
|
*
|
||||||
|
* @param command
|
||||||
|
* @return true if finish the span early (the command will not have a return value)
|
||||||
|
*/
|
||||||
|
public static boolean finishSpanEarly(final RedisCommand<?, ?, ?> command) {
|
||||||
|
final ProtocolKeyword keyword = command.getType();
|
||||||
|
return isNonInstrumentingCommand(keyword) || isNonInstrumentingKeyword(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNonInstrumentingCommand(final ProtocolKeyword keyword) {
|
||||||
|
return keyword instanceof CommandType && NON_INSTRUMENTING_COMMANDS.contains(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNonInstrumentingKeyword(final ProtocolKeyword keyword) {
|
||||||
|
return keyword == SEGFAULT;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.lettuce.v4_0;
|
||||||
|
|
||||||
|
import com.lambdaworks.redis.protocol.AsyncCommand;
|
||||||
|
import com.lambdaworks.redis.protocol.RedisCommand;
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
public class LettuceAsyncCommandsAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SpanWithScope onEnter(@Advice.Argument(0) final RedisCommand<?, ?, ?> command) {
|
||||||
|
return InstrumentationPoints.beforeCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(
|
||||||
|
@Advice.Argument(0) final RedisCommand<?, ?, ?> command,
|
||||||
|
@Advice.Enter final SpanWithScope spanWithScope,
|
||||||
|
@Advice.Thrown final Throwable throwable,
|
||||||
|
@Advice.Return final AsyncCommand<?, ?, ?> asyncCommand) {
|
||||||
|
InstrumentationPoints.afterCommand(command, spanWithScope, throwable, asyncCommand);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.lettuce.v4_0;
|
||||||
|
|
||||||
|
import com.lambdaworks.redis.RedisURI;
|
||||||
|
import io.opentelemetry.OpenTelemetry;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.DatabaseClientDecorator;
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.MoreTags;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import io.opentelemetry.trace.Tracer;
|
||||||
|
|
||||||
|
public class LettuceClientDecorator extends DatabaseClientDecorator<RedisURI> {
|
||||||
|
|
||||||
|
public static final LettuceClientDecorator DECORATE = new LettuceClientDecorator();
|
||||||
|
|
||||||
|
public static final Tracer TRACER =
|
||||||
|
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.lettuce-4.0");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbType() {
|
||||||
|
return "redis";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbUser(final RedisURI connection) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbInstance(final RedisURI connection) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Span onConnection(final Span span, final RedisURI connection) {
|
||||||
|
if (connection != null) {
|
||||||
|
span.setAttribute(MoreTags.NET_PEER_NAME, connection.getHost());
|
||||||
|
span.setAttribute(MoreTags.NET_PEER_PORT, connection.getPort());
|
||||||
|
|
||||||
|
span.setAttribute("db.redis.dbIndex", connection.getDatabase());
|
||||||
|
}
|
||||||
|
return super.onConnection(span, connection);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.lettuce.v4_0;
|
||||||
|
|
||||||
|
import com.lambdaworks.redis.RedisURI;
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
public class RedisConnectionAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SpanWithScope onEnter(@Advice.Argument(1) final RedisURI redisURI) {
|
||||||
|
return InstrumentationPoints.beforeConnect(redisURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void onExit(
|
||||||
|
@Advice.Enter final SpanWithScope scope, @Advice.Thrown final Throwable throwable) {
|
||||||
|
InstrumentationPoints.afterConnect(scope, throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,483 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import com.lambdaworks.redis.ClientOptions
|
||||||
|
import com.lambdaworks.redis.RedisClient
|
||||||
|
import com.lambdaworks.redis.RedisConnectionException
|
||||||
|
import com.lambdaworks.redis.RedisFuture
|
||||||
|
import com.lambdaworks.redis.RedisURI
|
||||||
|
import com.lambdaworks.redis.api.StatefulConnection
|
||||||
|
import com.lambdaworks.redis.api.async.RedisAsyncCommands
|
||||||
|
import com.lambdaworks.redis.api.sync.RedisCommands
|
||||||
|
import com.lambdaworks.redis.codec.Utf8StringCodec
|
||||||
|
import com.lambdaworks.redis.protocol.AsyncCommand
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.MoreTags
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.Tags
|
||||||
|
import io.opentelemetry.auto.test.AgentTestRunner
|
||||||
|
import io.opentelemetry.auto.test.utils.PortUtils
|
||||||
|
import redis.embedded.RedisServer
|
||||||
|
import spock.lang.Shared
|
||||||
|
import spock.util.concurrent.AsyncConditions
|
||||||
|
|
||||||
|
import java.util.concurrent.CancellationException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.function.BiConsumer
|
||||||
|
import java.util.function.BiFunction
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
import static io.opentelemetry.trace.Span.Kind.CLIENT
|
||||||
|
|
||||||
|
class LettuceAsyncClientTest extends AgentTestRunner {
|
||||||
|
public static final String HOST = "127.0.0.1"
|
||||||
|
public static final int DB_INDEX = 0
|
||||||
|
// Disable autoreconnect so we do not get stray traces popping up on server shutdown
|
||||||
|
public static final ClientOptions CLIENT_OPTIONS = new ClientOptions.Builder().autoReconnect(false).build()
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
int port
|
||||||
|
@Shared
|
||||||
|
int incorrectPort
|
||||||
|
@Shared
|
||||||
|
String dbAddr
|
||||||
|
@Shared
|
||||||
|
String dbAddrNonExistent
|
||||||
|
@Shared
|
||||||
|
String dbUriNonExistent
|
||||||
|
@Shared
|
||||||
|
String embeddedDbUri
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
RedisServer redisServer
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
Map<String, String> testHashMap = [
|
||||||
|
firstname: "John",
|
||||||
|
lastname : "Doe",
|
||||||
|
age : "53"
|
||||||
|
]
|
||||||
|
|
||||||
|
RedisClient redisClient
|
||||||
|
StatefulConnection connection
|
||||||
|
RedisAsyncCommands<String, ?> asyncCommands
|
||||||
|
RedisCommands<String, ?> syncCommands
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
port = PortUtils.randomOpenPort()
|
||||||
|
incorrectPort = PortUtils.randomOpenPort()
|
||||||
|
dbAddr = HOST + ":" + port + "/" + DB_INDEX
|
||||||
|
dbAddrNonExistent = HOST + ":" + incorrectPort + "/" + DB_INDEX
|
||||||
|
dbUriNonExistent = "redis://" + dbAddrNonExistent
|
||||||
|
embeddedDbUri = "redis://" + dbAddr
|
||||||
|
|
||||||
|
redisServer = RedisServer.builder()
|
||||||
|
// bind to localhost to avoid firewall popup
|
||||||
|
.setting("bind " + HOST)
|
||||||
|
// set max memory to avoid problems in CI
|
||||||
|
.setting("maxmemory 128M")
|
||||||
|
.port(port).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup() {
|
||||||
|
redisClient = RedisClient.create(embeddedDbUri)
|
||||||
|
|
||||||
|
println "Using redis: $redisServer.args"
|
||||||
|
redisServer.start()
|
||||||
|
redisClient.setOptions(CLIENT_OPTIONS)
|
||||||
|
|
||||||
|
connection = redisClient.connect()
|
||||||
|
asyncCommands = connection.async()
|
||||||
|
syncCommands = connection.sync()
|
||||||
|
|
||||||
|
syncCommands.set("TESTKEY", "TESTVAL")
|
||||||
|
|
||||||
|
// 1 set + 1 connect trace
|
||||||
|
TEST_WRITER.waitForTraces(2)
|
||||||
|
TEST_WRITER.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanup() {
|
||||||
|
connection.close()
|
||||||
|
redisServer.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "connect using get on ConnectionFuture"() {
|
||||||
|
setup:
|
||||||
|
RedisClient testConnectionClient = RedisClient.create(embeddedDbUri)
|
||||||
|
testConnectionClient.setOptions(CLIENT_OPTIONS)
|
||||||
|
|
||||||
|
when:
|
||||||
|
StatefulConnection connection = testConnectionClient.connect(new Utf8StringCodec(),
|
||||||
|
new RedisURI(HOST, port, 3, TimeUnit.SECONDS))
|
||||||
|
|
||||||
|
then:
|
||||||
|
connection != null
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "CONNECT"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$MoreTags.NET_PEER_NAME" HOST
|
||||||
|
"$MoreTags.NET_PEER_PORT" port
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"db.redis.dbIndex" 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
connection.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "connect exception inside the connection future"() {
|
||||||
|
setup:
|
||||||
|
RedisClient testConnectionClient = RedisClient.create(dbUriNonExistent)
|
||||||
|
testConnectionClient.setOptions(CLIENT_OPTIONS)
|
||||||
|
|
||||||
|
when:
|
||||||
|
StatefulConnection connection = testConnectionClient.connect(new Utf8StringCodec(),
|
||||||
|
new RedisURI(HOST, incorrectPort, 3, TimeUnit.SECONDS))
|
||||||
|
|
||||||
|
then:
|
||||||
|
connection == null
|
||||||
|
thrown RedisConnectionException
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "CONNECT"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored true
|
||||||
|
tags {
|
||||||
|
"$MoreTags.NET_PEER_NAME" HOST
|
||||||
|
"$MoreTags.NET_PEER_PORT" incorrectPort
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"db.redis.dbIndex" 0
|
||||||
|
errorTags RedisConnectionException, String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "set command using Future get with timeout"() {
|
||||||
|
setup:
|
||||||
|
RedisFuture<String> redisFuture = asyncCommands.set("TESTSETKEY", "TESTSETVAL")
|
||||||
|
String res = redisFuture.get(3, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
expect:
|
||||||
|
res == "OK"
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "SET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "get command chained with thenAccept"() {
|
||||||
|
setup:
|
||||||
|
def conds = new AsyncConditions()
|
||||||
|
Consumer<String> consumer = new Consumer<String>() {
|
||||||
|
@Override
|
||||||
|
void accept(String res) {
|
||||||
|
conds.evaluate {
|
||||||
|
assert res == "TESTVAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when:
|
||||||
|
RedisFuture<String> redisFuture = asyncCommands.get("TESTKEY")
|
||||||
|
redisFuture.thenAccept(consumer)
|
||||||
|
|
||||||
|
then:
|
||||||
|
conds.await()
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "GET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to make sure instrumentation's chained completion stages won't interfere with user's, while still
|
||||||
|
// recording metrics
|
||||||
|
def "get non existent key command with handleAsync and chained with thenApply"() {
|
||||||
|
setup:
|
||||||
|
def conds = new AsyncConditions()
|
||||||
|
final String successStr = "KEY MISSING"
|
||||||
|
BiFunction<String, Throwable, String> firstStage = new BiFunction<String, Throwable, String>() {
|
||||||
|
@Override
|
||||||
|
String apply(String res, Throwable throwable) {
|
||||||
|
conds.evaluate {
|
||||||
|
assert res == null
|
||||||
|
assert throwable == null
|
||||||
|
}
|
||||||
|
return (res == null ? successStr : res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Function<String, Object> secondStage = new Function<String, Object>() {
|
||||||
|
@Override
|
||||||
|
Object apply(String input) {
|
||||||
|
conds.evaluate {
|
||||||
|
assert input == successStr
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when:
|
||||||
|
RedisFuture<String> redisFuture = asyncCommands.get("NON_EXISTENT_KEY")
|
||||||
|
redisFuture.handleAsync(firstStage).thenApply(secondStage)
|
||||||
|
|
||||||
|
then:
|
||||||
|
conds.await()
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "GET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "command with no arguments using a biconsumer"() {
|
||||||
|
setup:
|
||||||
|
def conds = new AsyncConditions()
|
||||||
|
BiConsumer<String, Throwable> biConsumer = new BiConsumer<String, Throwable>() {
|
||||||
|
@Override
|
||||||
|
void accept(String keyRetrieved, Throwable throwable) {
|
||||||
|
conds.evaluate {
|
||||||
|
assert keyRetrieved != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when:
|
||||||
|
RedisFuture<String> redisFuture = asyncCommands.randomkey()
|
||||||
|
redisFuture.whenCompleteAsync(biConsumer)
|
||||||
|
|
||||||
|
then:
|
||||||
|
conds.await()
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "RANDOMKEY"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "hash set and then nest apply to hash getall"() {
|
||||||
|
setup:
|
||||||
|
def conds = new AsyncConditions()
|
||||||
|
|
||||||
|
when:
|
||||||
|
RedisFuture<String> hmsetFuture = asyncCommands.hmset("TESTHM", testHashMap)
|
||||||
|
hmsetFuture.thenApplyAsync(new Function<String, Object>() {
|
||||||
|
@Override
|
||||||
|
Object apply(String setResult) {
|
||||||
|
TEST_WRITER.waitForTraces(1) // Wait for 'hmset' trace to get written
|
||||||
|
conds.evaluate {
|
||||||
|
assert setResult == "OK"
|
||||||
|
}
|
||||||
|
RedisFuture<Map<String, String>> hmGetAllFuture = asyncCommands.hgetall("TESTHM")
|
||||||
|
hmGetAllFuture.exceptionally(new Function<Throwable, Map<String, String>>() {
|
||||||
|
@Override
|
||||||
|
Map<String, String> apply(Throwable throwable) {
|
||||||
|
println("unexpected:" + throwable.toString())
|
||||||
|
throwable.printStackTrace()
|
||||||
|
assert false
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
hmGetAllFuture.thenAccept(new Consumer<Map<String, String>>() {
|
||||||
|
@Override
|
||||||
|
void accept(Map<String, String> hmGetAllResult) {
|
||||||
|
conds.evaluate {
|
||||||
|
assert testHashMap == hmGetAllResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
then:
|
||||||
|
conds.await()
|
||||||
|
assertTraces(2) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "HMSET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace(1, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "HGETALL"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "command completes exceptionally"() {
|
||||||
|
setup:
|
||||||
|
// turn off auto flush to complete the command exceptionally manually
|
||||||
|
asyncCommands.setAutoFlushCommands(false)
|
||||||
|
def conds = new AsyncConditions()
|
||||||
|
RedisFuture redisFuture = asyncCommands.del("key1", "key2")
|
||||||
|
boolean completedExceptionally = ((AsyncCommand) redisFuture).completeExceptionally(new IllegalStateException("TestException"))
|
||||||
|
redisFuture.exceptionally({
|
||||||
|
throwable ->
|
||||||
|
conds.evaluate {
|
||||||
|
assert throwable != null
|
||||||
|
assert throwable instanceof IllegalStateException
|
||||||
|
assert throwable.getMessage() == "TestException"
|
||||||
|
}
|
||||||
|
throw throwable
|
||||||
|
})
|
||||||
|
|
||||||
|
when:
|
||||||
|
// now flush and execute the command
|
||||||
|
asyncCommands.flushCommands()
|
||||||
|
redisFuture.get()
|
||||||
|
|
||||||
|
then:
|
||||||
|
conds.await()
|
||||||
|
completedExceptionally == true
|
||||||
|
thrown Exception
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "DEL"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored true
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
errorTags(IllegalStateException, "TestException")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "cancel command before it finishes"() {
|
||||||
|
setup:
|
||||||
|
asyncCommands.setAutoFlushCommands(false)
|
||||||
|
def conds = new AsyncConditions()
|
||||||
|
RedisFuture redisFuture = asyncCommands.sadd("SKEY", "1", "2")
|
||||||
|
redisFuture.whenCompleteAsync({
|
||||||
|
res, throwable ->
|
||||||
|
conds.evaluate {
|
||||||
|
assert throwable != null
|
||||||
|
assert throwable instanceof CancellationException
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
when:
|
||||||
|
boolean cancelSuccess = redisFuture.cancel(true)
|
||||||
|
asyncCommands.flushCommands()
|
||||||
|
|
||||||
|
then:
|
||||||
|
conds.await()
|
||||||
|
cancelSuccess == true
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "SADD"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"db.command.cancelled" true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "debug segfault command (returns void) with no argument should produce span"() {
|
||||||
|
setup:
|
||||||
|
asyncCommands.debugSegfault()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "DEBUG"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def "shutdown command (returns void) should produce a span"() {
|
||||||
|
setup:
|
||||||
|
asyncCommands.shutdown(false)
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "SHUTDOWN"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import com.lambdaworks.redis.ClientOptions
|
||||||
|
import com.lambdaworks.redis.RedisClient
|
||||||
|
import com.lambdaworks.redis.RedisConnectionException
|
||||||
|
import com.lambdaworks.redis.api.StatefulConnection
|
||||||
|
import com.lambdaworks.redis.api.sync.RedisCommands
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.MoreTags
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.Tags
|
||||||
|
import io.opentelemetry.auto.test.AgentTestRunner
|
||||||
|
import io.opentelemetry.auto.test.utils.PortUtils
|
||||||
|
import redis.embedded.RedisServer
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
import static io.opentelemetry.trace.Span.Kind.CLIENT
|
||||||
|
|
||||||
|
class LettuceSyncClientTest extends AgentTestRunner {
|
||||||
|
public static final String HOST = "127.0.0.1"
|
||||||
|
public static final int DB_INDEX = 0
|
||||||
|
// Disable autoreconnect so we do not get stray traces popping up on server shutdown
|
||||||
|
public static final ClientOptions CLIENT_OPTIONS = new ClientOptions.Builder().autoReconnect(false).build()
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
int port
|
||||||
|
@Shared
|
||||||
|
int incorrectPort
|
||||||
|
@Shared
|
||||||
|
String dbAddr
|
||||||
|
@Shared
|
||||||
|
String dbAddrNonExistent
|
||||||
|
@Shared
|
||||||
|
String dbUriNonExistent
|
||||||
|
@Shared
|
||||||
|
String embeddedDbUri
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
RedisServer redisServer
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
Map<String, String> testHashMap = [
|
||||||
|
firstname: "John",
|
||||||
|
lastname : "Doe",
|
||||||
|
age : "53"
|
||||||
|
]
|
||||||
|
|
||||||
|
RedisClient redisClient
|
||||||
|
StatefulConnection connection
|
||||||
|
RedisCommands<String, ?> syncCommands
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
port = PortUtils.randomOpenPort()
|
||||||
|
incorrectPort = PortUtils.randomOpenPort()
|
||||||
|
dbAddr = HOST + ":" + port + "/" + DB_INDEX
|
||||||
|
dbAddrNonExistent = HOST + ":" + incorrectPort + "/" + DB_INDEX
|
||||||
|
dbUriNonExistent = "redis://" + dbAddrNonExistent
|
||||||
|
embeddedDbUri = "redis://" + dbAddr
|
||||||
|
|
||||||
|
redisServer = RedisServer.builder()
|
||||||
|
// bind to localhost to avoid firewall popup
|
||||||
|
.setting("bind " + HOST)
|
||||||
|
// set max memory to avoid problems in CI
|
||||||
|
.setting("maxmemory 128M")
|
||||||
|
.port(port).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup() {
|
||||||
|
redisClient = RedisClient.create(embeddedDbUri)
|
||||||
|
|
||||||
|
redisServer.start()
|
||||||
|
connection = redisClient.connect()
|
||||||
|
syncCommands = connection.sync()
|
||||||
|
|
||||||
|
syncCommands.set("TESTKEY", "TESTVAL")
|
||||||
|
syncCommands.hmset("TESTHM", testHashMap)
|
||||||
|
|
||||||
|
// 2 sets + 1 connect trace
|
||||||
|
TEST_WRITER.waitForTraces(3)
|
||||||
|
TEST_WRITER.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanup() {
|
||||||
|
connection.close()
|
||||||
|
redisServer.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "connect"() {
|
||||||
|
setup:
|
||||||
|
RedisClient testConnectionClient = RedisClient.create(embeddedDbUri)
|
||||||
|
testConnectionClient.setOptions(CLIENT_OPTIONS)
|
||||||
|
|
||||||
|
when:
|
||||||
|
StatefulConnection connection = testConnectionClient.connect()
|
||||||
|
|
||||||
|
then:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "CONNECT"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$MoreTags.NET_PEER_NAME" HOST
|
||||||
|
"$MoreTags.NET_PEER_PORT" port
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"db.redis.dbIndex" 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
connection.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "connect exception"() {
|
||||||
|
setup:
|
||||||
|
RedisClient testConnectionClient = RedisClient.create(dbUriNonExistent)
|
||||||
|
testConnectionClient.setOptions(CLIENT_OPTIONS)
|
||||||
|
|
||||||
|
when:
|
||||||
|
testConnectionClient.connect()
|
||||||
|
|
||||||
|
then:
|
||||||
|
thrown RedisConnectionException
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "CONNECT"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored true
|
||||||
|
tags {
|
||||||
|
"$MoreTags.NET_PEER_NAME" HOST
|
||||||
|
"$MoreTags.NET_PEER_PORT" incorrectPort
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"db.redis.dbIndex" 0
|
||||||
|
errorTags RedisConnectionException, String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "set command"() {
|
||||||
|
setup:
|
||||||
|
String res = syncCommands.set("TESTSETKEY", "TESTSETVAL")
|
||||||
|
|
||||||
|
expect:
|
||||||
|
res == "OK"
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "SET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "get command"() {
|
||||||
|
setup:
|
||||||
|
String res = syncCommands.get("TESTKEY")
|
||||||
|
|
||||||
|
expect:
|
||||||
|
res == "TESTVAL"
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "GET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "get non existent key command"() {
|
||||||
|
setup:
|
||||||
|
String res = syncCommands.get("NON_EXISTENT_KEY")
|
||||||
|
|
||||||
|
expect:
|
||||||
|
res == null
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "GET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "command with no arguments"() {
|
||||||
|
setup:
|
||||||
|
def keyRetrieved = syncCommands.randomkey()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
keyRetrieved != null
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "RANDOMKEY"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "list command"() {
|
||||||
|
setup:
|
||||||
|
long res = syncCommands.lpush("TESTLIST", "TESTLIST ELEMENT")
|
||||||
|
|
||||||
|
expect:
|
||||||
|
res == 1
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "LPUSH"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "hash set command"() {
|
||||||
|
setup:
|
||||||
|
def res = syncCommands.hmset("user", testHashMap)
|
||||||
|
|
||||||
|
expect:
|
||||||
|
res == "OK"
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "HMSET"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "hash getall command"() {
|
||||||
|
setup:
|
||||||
|
Map<String, String> res = syncCommands.hgetall("TESTHM")
|
||||||
|
|
||||||
|
expect:
|
||||||
|
res == testHashMap
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "HGETALL"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "debug segfault command (returns void) with no argument should produce span"() {
|
||||||
|
setup:
|
||||||
|
syncCommands.debugSegfault()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "DEBUG"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "shutdown command (returns void) should produce a span"() {
|
||||||
|
setup:
|
||||||
|
syncCommands.shutdown(false)
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "SHUTDOWN"
|
||||||
|
spanKind CLIENT
|
||||||
|
errored false
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.nameEndsWith;
|
import static net.bytebuddy.matcher.ElementMatchers.nameEndsWith;
|
|
@ -13,10 +13,8 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.TRACER;
|
|
||||||
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
||||||
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
|
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
|
||||||
|
|
||||||
|
@ -30,9 +28,10 @@ public class ConnectionFutureAdvice {
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static SpanWithScope onEnter(@Advice.Argument(1) final RedisURI redisURI) {
|
public static SpanWithScope onEnter(@Advice.Argument(1) final RedisURI redisURI) {
|
||||||
final Span span = TRACER.spanBuilder("CONNECT").setSpanKind(CLIENT).startSpan();
|
final Span span =
|
||||||
DECORATE.afterStart(span);
|
LettuceClientDecorator.TRACER.spanBuilder("CONNECT").setSpanKind(CLIENT).startSpan();
|
||||||
DECORATE.onConnection(span, redisURI);
|
LettuceClientDecorator.DECORATE.afterStart(span);
|
||||||
|
LettuceClientDecorator.DECORATE.onConnection(span, redisURI);
|
||||||
return new SpanWithScope(span, currentContextWith(span));
|
return new SpanWithScope(span, currentContextWith(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +42,8 @@ public class ConnectionFutureAdvice {
|
||||||
@Advice.Return final ConnectionFuture<?> connectionFuture) {
|
@Advice.Return final ConnectionFuture<?> connectionFuture) {
|
||||||
final Span span = spanWithScope.getSpan();
|
final Span span = spanWithScope.getSpan();
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
DECORATE.onError(span, throwable);
|
LettuceClientDecorator.DECORATE.onError(span, throwable);
|
||||||
DECORATE.beforeFinish(span);
|
LettuceClientDecorator.DECORATE.beforeFinish(span);
|
||||||
span.end();
|
span.end();
|
||||||
spanWithScope.closeScope();
|
spanWithScope.closeScope();
|
||||||
return;
|
return;
|
|
@ -13,9 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
|
||||||
|
|
||||||
import io.opentelemetry.trace.Span;
|
import io.opentelemetry.trace.Span;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
@ -44,9 +42,9 @@ public class LettuceAsyncBiFunction<T extends Object, U extends Throwable, R ext
|
||||||
if (throwable instanceof CancellationException) {
|
if (throwable instanceof CancellationException) {
|
||||||
span.setAttribute("db.command.cancelled", true);
|
span.setAttribute("db.command.cancelled", true);
|
||||||
} else {
|
} else {
|
||||||
DECORATE.onError(span, throwable);
|
LettuceClientDecorator.DECORATE.onError(span, throwable);
|
||||||
}
|
}
|
||||||
DECORATE.beforeFinish(span);
|
LettuceClientDecorator.DECORATE.beforeFinish(span);
|
||||||
span.end();
|
span.end();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
|
@ -13,11 +13,8 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.TRACER;
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceInstrumentationUtil.doFinishSpanEarly;
|
|
||||||
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
||||||
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
|
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
|
||||||
|
|
||||||
|
@ -33,11 +30,11 @@ public class LettuceAsyncCommandsAdvice {
|
||||||
public static SpanWithScope onEnter(@Advice.Argument(0) final RedisCommand command) {
|
public static SpanWithScope onEnter(@Advice.Argument(0) final RedisCommand command) {
|
||||||
|
|
||||||
final Span span =
|
final Span span =
|
||||||
TRACER
|
LettuceClientDecorator.TRACER
|
||||||
.spanBuilder(LettuceInstrumentationUtil.getCommandName(command))
|
.spanBuilder(LettuceInstrumentationUtil.getCommandName(command))
|
||||||
.setSpanKind(CLIENT)
|
.setSpanKind(CLIENT)
|
||||||
.startSpan();
|
.startSpan();
|
||||||
DECORATE.afterStart(span);
|
LettuceClientDecorator.DECORATE.afterStart(span);
|
||||||
|
|
||||||
return new SpanWithScope(span, currentContextWith(span));
|
return new SpanWithScope(span, currentContextWith(span));
|
||||||
}
|
}
|
||||||
|
@ -51,15 +48,15 @@ public class LettuceAsyncCommandsAdvice {
|
||||||
|
|
||||||
final Span span = spanWithScope.getSpan();
|
final Span span = spanWithScope.getSpan();
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
DECORATE.onError(span, throwable);
|
LettuceClientDecorator.DECORATE.onError(span, throwable);
|
||||||
DECORATE.beforeFinish(span);
|
LettuceClientDecorator.DECORATE.beforeFinish(span);
|
||||||
span.end();
|
span.end();
|
||||||
spanWithScope.closeScope();
|
spanWithScope.closeScope();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// close spans on error or normal completion
|
// close spans on error or normal completion
|
||||||
if (doFinishSpanEarly(command)) {
|
if (LettuceInstrumentationUtil.doFinishSpanEarly(command)) {
|
||||||
span.end();
|
span.end();
|
||||||
} else {
|
} else {
|
||||||
asyncCommand.handleAsync(new LettuceAsyncBiFunction<>(span));
|
asyncCommand.handleAsync(new LettuceAsyncBiFunction<>(span));
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import io.lettuce.core.RedisURI;
|
import io.lettuce.core.RedisURI;
|
||||||
import io.opentelemetry.OpenTelemetry;
|
import io.opentelemetry.OpenTelemetry;
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0;
|
||||||
|
|
||||||
import io.lettuce.core.protocol.RedisCommand;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
|
@ -13,9 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce.rx;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0.rx;
|
||||||
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceInstrumentationUtil.doFinishSpanEarly;
|
import static io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceInstrumentationUtil.doFinishSpanEarly;
|
||||||
|
|
||||||
import io.lettuce.core.protocol.RedisCommand;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
|
@ -13,14 +13,14 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce.rx;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0.rx;
|
||||||
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
import static io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceClientDecorator.DECORATE;
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.TRACER;
|
import static io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceClientDecorator.TRACER;
|
||||||
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
||||||
|
|
||||||
import io.lettuce.core.protocol.RedisCommand;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import io.opentelemetry.auto.instrumentation.lettuce.LettuceInstrumentationUtil;
|
import io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceInstrumentationUtil;
|
||||||
import io.opentelemetry.trace.Span;
|
import io.opentelemetry.trace.Span;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.reactivestreams.Subscription;
|
import org.reactivestreams.Subscription;
|
|
@ -13,10 +13,10 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce.rx;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0.rx;
|
||||||
|
|
||||||
import io.lettuce.core.protocol.RedisCommand;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import io.opentelemetry.auto.instrumentation.lettuce.LettuceInstrumentationUtil;
|
import io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceInstrumentationUtil;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
|
@ -13,14 +13,14 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.opentelemetry.auto.instrumentation.lettuce.rx;
|
package io.opentelemetry.auto.instrumentation.lettuce.v5_0.rx;
|
||||||
|
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.DECORATE;
|
import static io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceClientDecorator.DECORATE;
|
||||||
import static io.opentelemetry.auto.instrumentation.lettuce.LettuceClientDecorator.TRACER;
|
import static io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceClientDecorator.TRACER;
|
||||||
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
||||||
|
|
||||||
import io.lettuce.core.protocol.RedisCommand;
|
import io.lettuce.core.protocol.RedisCommand;
|
||||||
import io.opentelemetry.auto.instrumentation.lettuce.LettuceInstrumentationUtil;
|
import io.opentelemetry.auto.instrumentation.lettuce.v5_0.LettuceInstrumentationUtil;
|
||||||
import io.opentelemetry.trace.Span;
|
import io.opentelemetry.trace.Span;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
|
@ -0,0 +1,65 @@
|
||||||
|
ext {
|
||||||
|
minJavaVersionForTests = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/instrumentation.gradle"
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = "com.github.etaty"
|
||||||
|
module = "rediscala_2.11"
|
||||||
|
versions = "[1.5.0,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
pass {
|
||||||
|
group = "com.github.etaty"
|
||||||
|
module = "rediscala_2.12"
|
||||||
|
versions = "[1.8.0,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
pass {
|
||||||
|
group = "com.github.etaty"
|
||||||
|
module = "rediscala_2.13"
|
||||||
|
versions = "[1.9.0,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
pass {
|
||||||
|
group = "com.github.Ma27"
|
||||||
|
module = "rediscala_2.11"
|
||||||
|
versions = "[1.8.1,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
pass {
|
||||||
|
group = "com.github.Ma27"
|
||||||
|
module = "rediscala_2.12"
|
||||||
|
versions = "[1.8.1,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
pass {
|
||||||
|
group = "com.github.Ma27"
|
||||||
|
module = "rediscala_2.13"
|
||||||
|
versions = "[1.9.0,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly group: 'com.github.etaty', name: 'rediscala_2.11', version: '1.8.0'
|
||||||
|
|
||||||
|
testCompile group: 'com.github.etaty', name: 'rediscala_2.11', version: '1.8.0'
|
||||||
|
testCompile group: 'com.github.kstyrc', name: 'embedded-redis', version: '0.6'
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'com.github.etaty', name: 'rediscala_2.11', version: '+'
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.rediscala;
|
||||||
|
|
||||||
|
import io.opentelemetry.OpenTelemetry;
|
||||||
|
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.DatabaseClientDecorator;
|
||||||
|
import io.opentelemetry.trace.Tracer;
|
||||||
|
import redis.RedisCommand;
|
||||||
|
import redis.protocol.RedisReply;
|
||||||
|
|
||||||
|
public class RediscalaClientDecorator
|
||||||
|
extends DatabaseClientDecorator<RedisCommand<? extends RedisReply, ?>> {
|
||||||
|
|
||||||
|
public static final RediscalaClientDecorator DECORATE = new RediscalaClientDecorator();
|
||||||
|
|
||||||
|
public static final Tracer TRACER =
|
||||||
|
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.rediscala-1.8");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbType() {
|
||||||
|
return "redis";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbUser(final RedisCommand<? extends RedisReply, ?> session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String dbInstance(final RedisCommand<? extends RedisReply, ?> session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.rediscala;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.instrumentation.rediscala.RediscalaClientDecorator.DECORATE;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.rediscala.RediscalaClientDecorator.TRACER;
|
||||||
|
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.safeHasSuperType;
|
||||||
|
import static io.opentelemetry.trace.Span.Kind.CLIENT;
|
||||||
|
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import redis.RedisCommand;
|
||||||
|
import scala.concurrent.ExecutionContext;
|
||||||
|
import scala.concurrent.Future;
|
||||||
|
import scala.runtime.AbstractFunction1;
|
||||||
|
import scala.util.Try;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class RediscalaInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public RediscalaInstrumentation() {
|
||||||
|
super("rediscala", "redis");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return safeHasSuperType(named("redis.ActorRequest"))
|
||||||
|
.or(safeHasSuperType(named("redis.Request")))
|
||||||
|
.or(safeHasSuperType(named("redis.BufferedRequest")))
|
||||||
|
.or(safeHasSuperType(named("redis.RoundRobinPoolRequest")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
RediscalaInstrumentation.class.getName() + "$OnCompleteHandler",
|
||||||
|
"io.opentelemetry.auto.bootstrap.instrumentation.decorator.BaseDecorator",
|
||||||
|
"io.opentelemetry.auto.bootstrap.instrumentation.decorator.ClientDecorator",
|
||||||
|
"io.opentelemetry.auto.bootstrap.instrumentation.decorator.DatabaseClientDecorator",
|
||||||
|
packageName + ".RediscalaClientDecorator",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod()
|
||||||
|
.and(isPublic())
|
||||||
|
.and(named("send"))
|
||||||
|
.and(takesArgument(0, named("redis.RedisCommand")))
|
||||||
|
.and(returns(named("scala.concurrent.Future"))),
|
||||||
|
RediscalaInstrumentation.class.getName() + "$RediscalaAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RediscalaAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SpanWithScope onEnter(@Advice.Argument(0) final RedisCommand cmd) {
|
||||||
|
final Span span =
|
||||||
|
TRACER.spanBuilder(cmd.getClass().getName()).setSpanKind(CLIENT).startSpan();
|
||||||
|
DECORATE.afterStart(span);
|
||||||
|
DECORATE.onStatement(span, cmd.getClass().getName());
|
||||||
|
return new SpanWithScope(span, currentContextWith(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void stopSpan(
|
||||||
|
@Advice.Enter final SpanWithScope scope,
|
||||||
|
@Advice.Thrown final Throwable throwable,
|
||||||
|
@Advice.FieldValue("executionContext") final ExecutionContext ctx,
|
||||||
|
@Advice.Return(readOnly = false) final Future<Object> responseFuture) {
|
||||||
|
|
||||||
|
final Span span = scope.getSpan();
|
||||||
|
|
||||||
|
if (throwable == null) {
|
||||||
|
responseFuture.onComplete(new OnCompleteHandler(span), ctx);
|
||||||
|
} else {
|
||||||
|
DECORATE.onError(span, throwable);
|
||||||
|
DECORATE.beforeFinish(span);
|
||||||
|
span.end();
|
||||||
|
}
|
||||||
|
scope.closeScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OnCompleteHandler extends AbstractFunction1<Try<Object>, Void> {
|
||||||
|
private final Span span;
|
||||||
|
|
||||||
|
public OnCompleteHandler(final Span span) {
|
||||||
|
this.span = span;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void apply(final Try<Object> result) {
|
||||||
|
try {
|
||||||
|
if (result.isFailure()) {
|
||||||
|
DECORATE.onError(span, result.failed().get());
|
||||||
|
}
|
||||||
|
DECORATE.beforeFinish(span);
|
||||||
|
} finally {
|
||||||
|
span.end();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.Tags
|
||||||
|
import io.opentelemetry.auto.test.AgentTestRunner
|
||||||
|
import io.opentelemetry.auto.test.utils.PortUtils
|
||||||
|
import redis.ByteStringDeserializerDefault
|
||||||
|
import redis.ByteStringSerializerLowPriority
|
||||||
|
import redis.RedisClient
|
||||||
|
import redis.RedisDispatcher
|
||||||
|
import redis.embedded.RedisServer
|
||||||
|
import scala.Option
|
||||||
|
import scala.concurrent.Await
|
||||||
|
import scala.concurrent.duration.Duration
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
import static io.opentelemetry.trace.Span.Kind.CLIENT
|
||||||
|
|
||||||
|
class RediscalaClientTest extends AgentTestRunner {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
int port = PortUtils.randomOpenPort()
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
RedisServer redisServer = RedisServer.builder()
|
||||||
|
// bind to localhost to avoid firewall popup
|
||||||
|
.setting("bind 127.0.0.1")
|
||||||
|
// set max memory to avoid problems in CI
|
||||||
|
.setting("maxmemory 128M")
|
||||||
|
.port(port).build()
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
ActorSystem system
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
RedisClient redisClient
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
system = ActorSystem.create()
|
||||||
|
redisClient = new RedisClient("localhost",
|
||||||
|
port,
|
||||||
|
Option.apply(null),
|
||||||
|
Option.apply(null),
|
||||||
|
"RedisClient",
|
||||||
|
Option.apply(null),
|
||||||
|
system,
|
||||||
|
new RedisDispatcher("rediscala.rediscala-client-worker-dispatcher"))
|
||||||
|
|
||||||
|
println "Using redis: $redisServer.args"
|
||||||
|
redisServer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanupSpec() {
|
||||||
|
redisServer.stop()
|
||||||
|
system?.terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
def "set command"() {
|
||||||
|
when:
|
||||||
|
def value = redisClient.set("foo",
|
||||||
|
"bar",
|
||||||
|
Option.apply(null),
|
||||||
|
Option.apply(null),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
new ByteStringSerializerLowPriority.String$())
|
||||||
|
|
||||||
|
|
||||||
|
then:
|
||||||
|
Await.result(value, Duration.apply("3 second")) == true
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "redis.api.strings.Set"
|
||||||
|
spanKind CLIENT
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"$Tags.DB_STATEMENT" "redis.api.strings.Set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "get command"() {
|
||||||
|
when:
|
||||||
|
def write = redisClient.set("bar",
|
||||||
|
"baz",
|
||||||
|
Option.apply(null),
|
||||||
|
Option.apply(null),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
new ByteStringSerializerLowPriority.String$())
|
||||||
|
def value = redisClient.get("bar", new ByteStringDeserializerDefault.String$())
|
||||||
|
|
||||||
|
then:
|
||||||
|
Await.result(write, Duration.apply("3 second")) == true
|
||||||
|
Await.result(value, Duration.apply("3 second")) == Option.apply("baz")
|
||||||
|
assertTraces(2) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "redis.api.strings.Set"
|
||||||
|
spanKind CLIENT
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"$Tags.DB_STATEMENT" "redis.api.strings.Set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace(1, 1) {
|
||||||
|
span(0) {
|
||||||
|
operationName "redis.api.strings.Get"
|
||||||
|
spanKind CLIENT
|
||||||
|
tags {
|
||||||
|
"$Tags.DB_TYPE" "redis"
|
||||||
|
"$Tags.DB_STATEMENT" "redis.api.strings.Get"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ muzzle {
|
||||||
pass {
|
pass {
|
||||||
group = "javax.servlet"
|
group = "javax.servlet"
|
||||||
module = "servlet-api"
|
module = "servlet-api"
|
||||||
versions = "[2.3, 3.0)"
|
versions = "[2.2, 3.0)"
|
||||||
assertInverse = true
|
assertInverse = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ testSets {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly group: 'javax.servlet', name: 'servlet-api', version: '2.3'
|
compileOnly group: 'javax.servlet', name: 'servlet-api', version: '2.2'
|
||||||
|
|
||||||
testCompile(project(':testing')) {
|
testCompile(project(':testing')) {
|
||||||
exclude group: 'org.eclipse.jetty', module: 'jetty-server'
|
exclude group: 'org.eclipse.jetty', module: 'jetty-server'
|
||||||
|
|
|
@ -43,8 +43,8 @@ public class Servlet2Advice {
|
||||||
public static SpanWithScope onEnter(
|
public static SpanWithScope onEnter(
|
||||||
@Advice.This final Object servlet,
|
@Advice.This final Object servlet,
|
||||||
@Advice.Argument(0) final ServletRequest request,
|
@Advice.Argument(0) final ServletRequest request,
|
||||||
@Advice.Argument(value = 1, readOnly = false, typing = Assigner.Typing.DYNAMIC)
|
@Advice.Argument(value = 1, typing = Assigner.Typing.DYNAMIC)
|
||||||
ServletResponse response) {
|
final ServletResponse response) {
|
||||||
final boolean hasServletTrace = request.getAttribute(SPAN_ATTRIBUTE) instanceof Span;
|
final boolean hasServletTrace = request.getAttribute(SPAN_ATTRIBUTE) instanceof Span;
|
||||||
final boolean invalidRequest = !(request instanceof HttpServletRequest);
|
final boolean invalidRequest = !(request instanceof HttpServletRequest);
|
||||||
if (invalidRequest || hasServletTrace) {
|
if (invalidRequest || hasServletTrace) {
|
||||||
|
@ -59,7 +59,8 @@ public class Servlet2Advice {
|
||||||
InstrumentationContext.get(HttpServletResponse.class, HttpServletRequest.class)
|
InstrumentationContext.get(HttpServletResponse.class, HttpServletRequest.class)
|
||||||
.put((HttpServletResponse) response, httpServletRequest);
|
.put((HttpServletResponse) response, httpServletRequest);
|
||||||
|
|
||||||
response = new StatusSavingHttpServletResponseWrapper((HttpServletResponse) response);
|
// Default value for checking for uncaught error later
|
||||||
|
InstrumentationContext.get(ServletResponse.class, Integer.class).put(response, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Span.Builder builder =
|
final Span.Builder builder =
|
||||||
|
@ -99,10 +100,17 @@ public class Servlet2Advice {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Span span = spanWithScope.getSpan();
|
final Span span = spanWithScope.getSpan();
|
||||||
DECORATE.onResponse(span, response);
|
|
||||||
|
if (response instanceof HttpServletResponse) {
|
||||||
|
DECORATE.onResponse(
|
||||||
|
span, InstrumentationContext.get(ServletResponse.class, Integer.class).get(response));
|
||||||
|
} else {
|
||||||
|
DECORATE.onResponse(span, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
if (response instanceof StatusSavingHttpServletResponseWrapper
|
if (response instanceof HttpServletResponse
|
||||||
&& ((StatusSavingHttpServletResponseWrapper) response).status
|
&& InstrumentationContext.get(ServletResponse.class, Integer.class).get(response)
|
||||||
== HttpServletResponse.SC_OK) {
|
== HttpServletResponse.SC_OK) {
|
||||||
// exception was thrown but status code wasn't set
|
// exception was thrown but status code wasn't set
|
||||||
span.setAttribute(Tags.HTTP_STATUS, 500);
|
span.setAttribute(Tags.HTTP_STATUS, 500);
|
||||||
|
|
|
@ -21,11 +21,10 @@ import io.opentelemetry.trace.Span;
|
||||||
import io.opentelemetry.trace.Tracer;
|
import io.opentelemetry.trace.Tracer;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import javax.servlet.ServletResponse;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
public class Servlet2Decorator
|
public class Servlet2Decorator
|
||||||
extends HttpServerDecorator<HttpServletRequest, HttpServletRequest, ServletResponse> {
|
extends HttpServerDecorator<HttpServletRequest, HttpServletRequest, Integer> {
|
||||||
public static final Servlet2Decorator DECORATE = new Servlet2Decorator();
|
public static final Servlet2Decorator DECORATE = new Servlet2Decorator();
|
||||||
public static final Tracer TRACER =
|
public static final Tracer TRACER =
|
||||||
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.servlet-2.3");
|
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.servlet-2.3");
|
||||||
|
@ -59,13 +58,8 @@ public class Servlet2Decorator
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Integer status(final ServletResponse httpServletResponse) {
|
protected Integer status(final Integer status) {
|
||||||
if (httpServletResponse instanceof StatusSavingHttpServletResponseWrapper) {
|
return status;
|
||||||
return ((StatusSavingHttpServletResponseWrapper) httpServletResponse).status;
|
|
||||||
} else {
|
|
||||||
// HttpServletResponse doesn't have accessor for status code.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,6 +25,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
@ -52,16 +53,17 @@ public final class Servlet2Instrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
packageName + ".Servlet2Decorator",
|
packageName + ".Servlet2Decorator", packageName + ".HttpServletRequestExtractAdapter",
|
||||||
packageName + ".HttpServletRequestExtractAdapter",
|
|
||||||
packageName + ".StatusSavingHttpServletResponseWrapper",
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> contextStore() {
|
public Map<String, String> contextStore() {
|
||||||
return singletonMap(
|
final Map<String, String> contextStores = new HashMap<>();
|
||||||
|
contextStores.put(
|
||||||
"javax.servlet.http.HttpServletResponse", "javax.servlet.http.HttpServletRequest");
|
"javax.servlet.http.HttpServletResponse", "javax.servlet.http.HttpServletRequest");
|
||||||
|
contextStores.put("javax.servlet.ServletResponse", Integer.class.getName());
|
||||||
|
return contextStores;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.servlet.v2_3;
|
||||||
|
|
||||||
|
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
public class Servlet2ResponseRedirectAdvice {
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onEnter(@Advice.This final HttpServletResponse response) {
|
||||||
|
InstrumentationContext.get(ServletResponse.class, Integer.class).put(response, 302);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.servlet.v2_3;
|
||||||
|
|
||||||
|
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
public class Servlet2ResponseStatusAdvice {
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onEnter(
|
||||||
|
@Advice.This final HttpServletResponse response, @Advice.Argument(0) final Integer status) {
|
||||||
|
InstrumentationContext.get(ServletResponse.class, Integer.class).put(response, status);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* 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 io.opentelemetry.auto.instrumentation.servlet.v2_3;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
|
||||||
|
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.safeHasSuperType;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class Servlet2ResponseStatusInstrumentation extends Instrumenter.Default {
|
||||||
|
public Servlet2ResponseStatusInstrumentation() {
|
||||||
|
super("servlet", "servlet-2");
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is required to make sure servlet 2 instrumentation won't apply to servlet 3
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
||||||
|
return not(hasClassesNamed("javax.servlet.AsyncEvent", "javax.servlet.AsyncListener"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return safeHasSuperType(named("javax.servlet.http.HttpServletResponse"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
return singletonMap("javax.servlet.ServletResponse", Integer.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlike Servlet2Instrumentation it doesn't matter if the HttpServletResponseInstrumentation
|
||||||
|
* applies first
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||||
|
transformers.put(
|
||||||
|
named("sendError").or(named("setStatus")), packageName + ".Servlet2ResponseStatusAdvice");
|
||||||
|
transformers.put(named("sendRedirect"), packageName + ".Servlet2ResponseRedirectAdvice");
|
||||||
|
return transformers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
*
|
|
||||||
* 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 io.opentelemetry.auto.instrumentation.servlet.v2_3;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.servlet.http.HttpServletResponseWrapper;
|
|
||||||
|
|
||||||
public class StatusSavingHttpServletResponseWrapper extends HttpServletResponseWrapper {
|
|
||||||
public int status = 200;
|
|
||||||
|
|
||||||
public StatusSavingHttpServletResponseWrapper(final HttpServletResponse response) {
|
|
||||||
super(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(final int status) throws IOException {
|
|
||||||
this.status = status;
|
|
||||||
super.sendError(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(final int status, final String message) throws IOException {
|
|
||||||
this.status = status;
|
|
||||||
super.sendError(status, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendRedirect(final String location) throws IOException {
|
|
||||||
status = 302;
|
|
||||||
super.sendRedirect(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setStatus(final int status) {
|
|
||||||
this.status = status;
|
|
||||||
super.setStatus(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setStatus(final int status, final String message) {
|
|
||||||
this.status = status;
|
|
||||||
super.setStatus(status, message);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -88,7 +88,7 @@ public class Servlet3Decorator
|
||||||
*/
|
*/
|
||||||
private void onContext(
|
private void onContext(
|
||||||
final Span span, final HttpServletRequest request, final ServletContext context) {
|
final Span span, final HttpServletRequest request, final ServletContext context) {
|
||||||
final Object attribute = context.getAttribute("ota.dispatcher-filter");
|
final Object attribute = context.getAttribute("io.opentelemetry.auto.dispatcher-filter");
|
||||||
if (attribute instanceof Filter) {
|
if (attribute instanceof Filter) {
|
||||||
final Object priorAttr = request.getAttribute(SPAN_ATTRIBUTE);
|
final Object priorAttr = request.getAttribute(SPAN_ATTRIBUTE);
|
||||||
request.setAttribute(SPAN_ATTRIBUTE, span);
|
request.setAttribute(SPAN_ATTRIBUTE, span);
|
||||||
|
|
|
@ -120,8 +120,8 @@ public final class DispatcherServletInstrumentation extends Instrumenter.Default
|
||||||
contextStore.put(dispatcher, filter);
|
contextStore.put(dispatcher, filter);
|
||||||
}
|
}
|
||||||
filter.setHandlerMappings(handlerMappings);
|
filter.setHandlerMappings(handlerMappings);
|
||||||
servletContext.setAttribute(
|
// attribute used by Servlet3Decorator.onContext
|
||||||
"ota.dispatcher-filter", filter); // used by Servlet3Decorator.onContext
|
servletContext.setAttribute("io.opentelemetry.auto.dispatcher-filter", filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,8 @@ include ':instrumentation:jms-1.1'
|
||||||
include ':instrumentation:jsp-2.3'
|
include ':instrumentation:jsp-2.3'
|
||||||
include ':instrumentation:kafka-clients-0.11'
|
include ':instrumentation:kafka-clients-0.11'
|
||||||
include ':instrumentation:kafka-streams-0.11'
|
include ':instrumentation:kafka-streams-0.11'
|
||||||
include ':instrumentation:lettuce-5.0'
|
include ':instrumentation:lettuce:lettuce-4.0'
|
||||||
|
include ':instrumentation:lettuce:lettuce-5.0'
|
||||||
include ':instrumentation:log4j:log4j-1.1'
|
include ':instrumentation:log4j:log4j-1.1'
|
||||||
include ':instrumentation:log4j:log4j-2.0'
|
include ':instrumentation:log4j:log4j-2.0'
|
||||||
include ':instrumentation:logback-1.0'
|
include ':instrumentation:logback-1.0'
|
||||||
|
@ -122,6 +123,7 @@ include ':instrumentation:play-ws:play-ws-common'
|
||||||
include ':instrumentation:rabbitmq-amqp-2.7'
|
include ':instrumentation:rabbitmq-amqp-2.7'
|
||||||
include ':instrumentation:ratpack-1.5'
|
include ':instrumentation:ratpack-1.5'
|
||||||
include ':instrumentation:reactor-3.1'
|
include ':instrumentation:reactor-3.1'
|
||||||
|
include ':instrumentation:rediscala-1.8'
|
||||||
include ':instrumentation:rmi'
|
include ':instrumentation:rmi'
|
||||||
include ':instrumentation:rxjava-1.0'
|
include ':instrumentation:rxjava-1.0'
|
||||||
include ':instrumentation:servlet'
|
include ':instrumentation:servlet'
|
||||||
|
|
Loading…
Reference in New Issue