Add support for XXL-JOB (#10421)

Co-authored-by: Lauri Tulmin <tulmin@gmail.com>
This commit is contained in:
Steve Rao 2024-03-13 06:54:54 +08:00 committed by GitHub
parent 93bb1febea
commit 86c3263868
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 1987 additions and 0 deletions

View File

@ -145,6 +145,7 @@ These are the supported libraries and frameworks:
| [Vert.x SQL Client](https://github.com/eclipse-vertx/vertx-sql-client/) | 4.0+ | N/A | [Database Client Spans] |
| [Vert.x Web](https://vertx.io/docs/vertx-web/java/) | 3.0+ | N/A | Provides `http.route` [2] |
| [Vibur DBCP](https://www.vibur.org/) | 11.0+ | [opentelemetry-vibur-dbcp-11.0](../instrumentation/vibur-dbcp-11.0/library) | [Database Pool Metrics] |
| [XXL-JOB](https://www.xuxueli.com/xxl-job/en/) | 1.9.2+ | N/A | none |
| [ZIO](https://zio.dev/) | 2.0+ | N/A | Context propagation |
**[1]** Standalone library instrumentation refers to instrumentation that can be used without the Java agent.

View File

@ -0,0 +1,5 @@
# Settings for the XXL-JOB instrumentation
| System property | Type | Default | Description |
|-------------------------------------------------------------|---------|---------|-----------------------------------------------------|
| `otel.instrumentation.xxl-job.experimental-span-attributes` | Boolean | `false` | Enable the capture of experimental span attributes. |

View File

@ -0,0 +1,36 @@
plugins {
id("otel.javaagent-instrumentation")
}
muzzle {
pass {
group.set("com.xuxueli")
module.set("xxl-job-core")
versions.set("[1.9.2, 2.1.2)")
assertInverse.set(true)
}
}
dependencies {
library("com.xuxueli:xxl-job-core:1.9.2") {
exclude("org.codehaus.groovy", "groovy")
}
implementation(project(":instrumentation:xxl-job:xxl-job-common:javaagent"))
testInstrumentation(project(":instrumentation:xxl-job:xxl-job-2.1.2:javaagent"))
testInstrumentation(project(":instrumentation:xxl-job:xxl-job-2.3.0:javaagent"))
// It needs the javax.annotation-api in xxl-job-core 1.9.2.
testImplementation("javax.annotation:javax.annotation-api:1.3.2")
testImplementation(project(":instrumentation:xxl-job:xxl-job-common:testing"))
latestDepTestLibrary("com.xuxueli:xxl-job-core:2.1.1") {
exclude("org.codehaus.groovy", "groovy")
}
}
tasks.withType<Test>().configureEach {
// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
jvmArgs("-Dotel.instrumentation.xxl-job.experimental-span-attributes=true")
}

View File

@ -0,0 +1,67 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.xxl.job.core.handler.IJobHandler;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class GlueJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.GlueJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()).and(takesArguments(String.class)),
GlueJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("jobHandler") IJobHandler handler,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createGlueJobRequest(handler);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(result, request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.xxl.job.core.glue.GlueTypeEnum;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class ScriptJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.ScriptJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()).and(takesArguments(1).and(takesArgument(0, String.class))),
ScriptJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("glueType") GlueTypeEnum glueType,
@Advice.FieldValue("jobId") int jobId,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createScriptJobRequest(glueType, jobId);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(result, request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_GLUE_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_METHOD_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_SCRIPT_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.xxl.job.core.handler.IJobHandler;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class SimpleJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return hasSuperType(named("com.xxl.job.core.handler.IJobHandler"))
.and(not(namedOneOf(XXL_GLUE_JOB_HANDLER, XXL_SCRIPT_JOB_HANDLER, XXL_METHOD_JOB_HANDLER)));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()).and(takesArguments(1).and(takesArgument(0, String.class))),
SimpleJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
public static class ScheduleAdvice {
@SuppressWarnings("unused")
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.This IJobHandler handler,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createSimpleJobRequest(handler);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@SuppressWarnings("unused")
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(result, request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static java.util.Arrays.asList;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class XxlJobInstrumentationModule extends InstrumentationModule {
public XxlJobInstrumentationModule() {
super("xxl-job", "xxl-job-1.9.2");
}
@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
// Class was added in 2.1.2
return not(hasClassesNamed("com.xxl.job.core.handler.impl.MethodJobHandler"));
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(
new ScriptJobHandlerInstrumentation(),
new SimpleJobHandlerInstrumentation(),
new GlueJobHandlerInstrumentation());
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.glue.GlueTypeEnum;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobHelper;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobInstrumenterFactory;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
public final class XxlJobSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.xxl-job-1.9.2";
private static final Instrumenter<XxlJobProcessRequest, Void> INSTRUMENTER =
XxlJobInstrumenterFactory.create(INSTRUMENTATION_NAME);
private static final XxlJobHelper HELPER =
XxlJobHelper.create(
INSTRUMENTER,
object -> {
if (object != null && (object instanceof ReturnT)) {
ReturnT<?> result = (ReturnT<?>) object;
return result.getCode() == ReturnT.FAIL_CODE;
}
return false;
});
public static XxlJobHelper helper() {
return HELPER;
}
@SuppressWarnings({"Unused", "ReturnValueIgnored"})
private static void limitSupportedVersions() {
// GLUE_POWERSHELL was added in 1.9.2. Using this constant here ensures that muzzle will disable
// this instrumentation on earlier versions where this constant does not exist.
GlueTypeEnum.GLUE_POWERSHELL.name();
}
private XxlJobSingletons() {}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v1_9_2;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.DEFAULT_GLUE_UPDATE_TIME;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.GLUE_JOB_GROOVY_SOURCE_OLD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.GLUE_JOB_SHELL_SCRIPT;
import com.xxl.job.core.glue.GlueFactory;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.impl.GlueJobHandler;
import com.xxl.job.core.handler.impl.ScriptJobHandler;
import io.opentelemetry.instrumentation.xxljob.AbstractXxlJobTest;
import io.opentelemetry.instrumentation.xxljob.CustomizedFailedHandler;
import io.opentelemetry.instrumentation.xxljob.SimpleCustomizedHandler;
class XxlJobTest extends AbstractXxlJobTest {
private static final IJobHandler GROOVY_HANDLER;
static {
try {
GROOVY_HANDLER = GlueFactory.getInstance().loadNewInstance(GLUE_JOB_GROOVY_SOURCE_OLD);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static final GlueJobHandler GLUE_JOB_HANDLER =
new GlueJobHandler(GROOVY_HANDLER, DEFAULT_GLUE_UPDATE_TIME);
private static final ScriptJobHandler SCRIPT_JOB_HANDLER =
new ScriptJobHandler(
2, DEFAULT_GLUE_UPDATE_TIME, GLUE_JOB_SHELL_SCRIPT, GlueTypeEnum.GLUE_SHELL);
@Override
protected String getPackageName() {
return "io.opentelemetry.instrumentation.xxljob";
}
@Override
protected IJobHandler getGlueJobHandler() {
return GLUE_JOB_HANDLER;
}
@Override
protected IJobHandler getScriptJobHandler() {
return SCRIPT_JOB_HANDLER;
}
@Override
protected IJobHandler getCustomizeHandler() {
return new SimpleCustomizedHandler();
}
@Override
protected IJobHandler getCustomizeFailedHandler() {
return new CustomizedFailedHandler();
}
@Override
protected IJobHandler getMethodHandler() {
return null;
}
}

View File

@ -0,0 +1,34 @@
plugins {
id("otel.javaagent-instrumentation")
}
muzzle {
pass {
group.set("com.xuxueli")
module.set("xxl-job-core")
versions.set("[2.1.2,2.3.0)")
assertInverse.set(true)
}
}
dependencies {
library("com.xuxueli:xxl-job-core:2.1.2") {
exclude("org.codehaus.groovy", "groovy")
}
implementation(project(":instrumentation:xxl-job:xxl-job-common:javaagent"))
testInstrumentation(project(":instrumentation:xxl-job:xxl-job-1.9.2:javaagent"))
testInstrumentation(project(":instrumentation:xxl-job:xxl-job-2.3.0:javaagent"))
testImplementation(project(":instrumentation:xxl-job:xxl-job-common:testing"))
latestDepTestLibrary("com.xuxueli:xxl-job-core:2.2.+") {
exclude("org.codehaus.groovy", "groovy")
}
}
tasks.withType<Test>().configureEach {
// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
jvmArgs("-Dotel.instrumentation.xxl-job.experimental-span-attributes=true")
}

View File

@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.xxl.job.core.handler.IJobHandler;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class GlueJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.GlueJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()),
GlueJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("jobHandler") IJobHandler handler,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createGlueJobRequest(handler);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(result, request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class MethodJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.MethodJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()),
MethodJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("target") Object target,
@Advice.FieldValue("method") Method method,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createMethodJobRequest(target, method);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(result, request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.xxl.job.core.glue.GlueTypeEnum;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class ScriptJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.ScriptJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()),
ScriptJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("glueType") GlueTypeEnum glueType,
@Advice.FieldValue("jobId") int jobId,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createScriptJobRequest(glueType, jobId);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(result, request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_GLUE_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_METHOD_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_SCRIPT_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.xxl.job.core.handler.IJobHandler;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
public class SimpleJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return hasSuperType(named("com.xxl.job.core.handler.IJobHandler"))
.and(not(namedOneOf(XXL_GLUE_JOB_HANDLER, XXL_SCRIPT_JOB_HANDLER, XXL_METHOD_JOB_HANDLER)));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()),
SimpleJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
public static class ScheduleAdvice {
@SuppressWarnings("unused")
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.This IJobHandler handler,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createSimpleJobRequest(handler);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@SuppressWarnings("unused")
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(result, request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static java.util.Arrays.asList;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class XxlJobInstrumentationModule extends InstrumentationModule {
public XxlJobInstrumentationModule() {
super("xxl-job", "xxl-job-2.1.2");
}
@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
return hasClassesNamed("com.xxl.job.core.handler.impl.MethodJobHandler")
// Class was added in 2.3.0
.and(not(hasClassesNamed("com.xxl.job.core.context.XxlJobHelper")));
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(
new MethodJobHandlerInstrumentation(),
new ScriptJobHandlerInstrumentation(),
new SimpleJobHandlerInstrumentation(),
new GlueJobHandlerInstrumentation());
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2;
import com.xxl.job.core.biz.model.ReturnT;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobHelper;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobInstrumenterFactory;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
public final class XxlJobSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.xxl-job-2.1.2";
private static final Instrumenter<XxlJobProcessRequest, Void> INSTRUMENTER =
XxlJobInstrumenterFactory.create(INSTRUMENTATION_NAME);
private static final XxlJobHelper HELPER =
XxlJobHelper.create(
INSTRUMENTER,
object -> {
if (object != null && (object instanceof ReturnT)) {
ReturnT<?> result = (ReturnT<?>) object;
return result.getCode() == ReturnT.FAIL_CODE;
}
return false;
});
public static XxlJobHelper helper() {
return HELPER;
}
private XxlJobSingletons() {}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_1_2;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.DEFAULT_GLUE_UPDATE_TIME;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.GLUE_JOB_GROOVY_SOURCE_OLD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.GLUE_JOB_SHELL_SCRIPT;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_DESTROY_METHOD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_INIT_METHOD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_METHOD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_OBJECT;
import com.xxl.job.core.glue.GlueFactory;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.impl.GlueJobHandler;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import com.xxl.job.core.handler.impl.ScriptJobHandler;
import io.opentelemetry.instrumentation.xxljob.AbstractXxlJobTest;
import io.opentelemetry.instrumentation.xxljob.CustomizedFailedHandler;
import io.opentelemetry.instrumentation.xxljob.SimpleCustomizedHandler;
class XxlJobTest extends AbstractXxlJobTest {
private static final MethodJobHandler METHOD_JOB_HANDLER =
new MethodJobHandler(
METHOD_JOB_HANDLER_OBJECT,
METHOD_JOB_HANDLER_METHOD,
METHOD_JOB_HANDLER_INIT_METHOD,
METHOD_JOB_HANDLER_DESTROY_METHOD);
private static final IJobHandler GROOVY_HANDLER;
static {
try {
GROOVY_HANDLER = GlueFactory.getInstance().loadNewInstance(GLUE_JOB_GROOVY_SOURCE_OLD);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static final GlueJobHandler GLUE_JOB_HANDLER =
new GlueJobHandler(GROOVY_HANDLER, DEFAULT_GLUE_UPDATE_TIME);
private static final ScriptJobHandler SCRIPT_JOB_HANDLER =
new ScriptJobHandler(
2, DEFAULT_GLUE_UPDATE_TIME, GLUE_JOB_SHELL_SCRIPT, GlueTypeEnum.GLUE_SHELL);
@Override
protected String getPackageName() {
return "io.opentelemetry.instrumentation.xxljob";
}
@Override
protected IJobHandler getGlueJobHandler() {
return GLUE_JOB_HANDLER;
}
@Override
protected IJobHandler getScriptJobHandler() {
return SCRIPT_JOB_HANDLER;
}
@Override
protected IJobHandler getCustomizeHandler() {
return new SimpleCustomizedHandler();
}
@Override
protected IJobHandler getCustomizeFailedHandler() {
return new CustomizedFailedHandler();
}
@Override
protected IJobHandler getMethodHandler() {
return METHOD_JOB_HANDLER;
}
}

View File

@ -0,0 +1,31 @@
plugins {
id("otel.javaagent-instrumentation")
}
muzzle {
pass {
group.set("com.xuxueli")
module.set("xxl-job-core")
versions.set("[2.3.0,)")
assertInverse.set(true)
}
}
dependencies {
library("com.xuxueli:xxl-job-core:2.3.0") {
exclude("org.codehaus.groovy", "groovy")
}
implementation(project(":instrumentation:xxl-job:xxl-job-common:javaagent"))
testInstrumentation(project(":instrumentation:xxl-job:xxl-job-2.1.2:javaagent"))
testInstrumentation(project(":instrumentation:xxl-job:xxl-job-2.3.0:javaagent"))
testImplementation(project(":instrumentation:xxl-job:xxl-job-common:testing"))
}
tasks.withType<Test>().configureEach {
// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
jvmArgs("-Dotel.instrumentation.xxl-job.experimental-span-attributes=true")
}

View File

@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
import com.xxl.job.core.handler.IJobHandler;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class GlueJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.GlueJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()).and(takesNoArguments()),
GlueJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("jobHandler") IJobHandler handler,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createGlueJobRequest(handler);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class MethodJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.MethodJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()).and(takesNoArguments()),
MethodJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("target") Object target,
@Advice.FieldValue("method") Method method,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createMethodJobRequest(target, method);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
import com.xxl.job.core.glue.GlueTypeEnum;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class ScriptJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.xxl.job.core.handler.impl.ScriptJobHandler");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()).and(takesNoArguments()),
ScriptJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
@SuppressWarnings("unused")
public static class ScheduleAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.FieldValue("glueType") GlueTypeEnum glueType,
@Advice.FieldValue("jobId") int jobId,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createScriptJobRequest(glueType, jobId);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_GLUE_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_METHOD_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobConstants.XXL_SCRIPT_JOB_HANDLER;
import static io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0.XxlJobSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
import com.xxl.job.core.handler.IJobHandler;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class SimpleJobHandlerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return hasSuperType(named("com.xxl.job.core.handler.IJobHandler"))
.and(not(namedOneOf(XXL_GLUE_JOB_HANDLER, XXL_SCRIPT_JOB_HANDLER, XXL_METHOD_JOB_HANDLER)));
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute").and(isPublic()).and(takesNoArguments()),
SimpleJobHandlerInstrumentation.class.getName() + "$ScheduleAdvice");
}
public static class ScheduleAdvice {
@SuppressWarnings("unused")
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onSchedule(
@Advice.This IJobHandler handler,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext();
request = XxlJobProcessRequest.createSimpleJobRequest(handler);
context = helper().startSpan(parentContext, request);
if (context == null) {
return;
}
scope = context.makeCurrent();
}
@SuppressWarnings("unused")
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") XxlJobProcessRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
helper().stopSpan(request, throwable, scope, context);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static java.util.Arrays.asList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class XxlJobInstrumentationModule extends InstrumentationModule {
public XxlJobInstrumentationModule() {
super("xxl-job", "xxl-job-2.3.0");
}
@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
return hasClassesNamed(
"com.xxl.job.core.handler.impl.MethodJobHandler", "com.xxl.job.core.context.XxlJobHelper");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(
new MethodJobHandlerInstrumentation(),
new ScriptJobHandlerInstrumentation(),
new SimpleJobHandlerInstrumentation(),
new GlueJobHandlerInstrumentation());
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import static com.xxl.job.core.context.XxlJobContext.HANDLE_COCE_SUCCESS;
import com.xxl.job.core.context.XxlJobContext;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobHelper;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobInstrumenterFactory;
import io.opentelemetry.javaagent.instrumentation.xxljob.common.XxlJobProcessRequest;
public final class XxlJobSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.xxl-job-2.3.0";
private static final Instrumenter<XxlJobProcessRequest, Void> INSTRUMENTER =
XxlJobInstrumenterFactory.create(INSTRUMENTATION_NAME);
private static final XxlJobHelper HELPER =
XxlJobHelper.create(
INSTRUMENTER,
unused -> {
// From 2.3.0, XxlJobContext is used to store the result of the job execution.
XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext();
if (xxlJobContext != null) {
int handleCode = xxlJobContext.getHandleCode();
return handleCode != HANDLE_COCE_SUCCESS;
}
return false;
});
public static XxlJobHelper helper() {
return HELPER;
}
private XxlJobSingletons() {}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.IJobHandler;
class CustomizedFailedHandler extends IJobHandler {
@Override
public void execute() throws Exception {
XxlJobHelper.handleFail();
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.IJobHandler;
class SimpleCustomizedHandler extends IJobHandler {
@Override
public void execute() throws Exception {
XxlJobHelper.handleSuccess();
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.DEFAULT_GLUE_UPDATE_TIME;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.GLUE_JOB_GROOVY_SOURCE;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.GLUE_JOB_SHELL_SCRIPT;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_DESTROY_METHOD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_INIT_METHOD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_METHOD;
import static io.opentelemetry.instrumentation.xxljob.XxlJobTestingConstants.METHOD_JOB_HANDLER_OBJECT;
import com.xxl.job.core.glue.GlueFactory;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.impl.GlueJobHandler;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import com.xxl.job.core.handler.impl.ScriptJobHandler;
import io.opentelemetry.instrumentation.xxljob.AbstractXxlJobTest;
class XxlJobTest extends AbstractXxlJobTest {
private static final MethodJobHandler METHOD_JOB_HANDLER =
new MethodJobHandler(
METHOD_JOB_HANDLER_OBJECT,
METHOD_JOB_HANDLER_METHOD,
METHOD_JOB_HANDLER_INIT_METHOD,
METHOD_JOB_HANDLER_DESTROY_METHOD);
private static final IJobHandler GROOVY_HANDLER;
static {
try {
GROOVY_HANDLER = GlueFactory.getInstance().loadNewInstance(GLUE_JOB_GROOVY_SOURCE);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static final GlueJobHandler GLUE_JOB_HANDLER =
new GlueJobHandler(GROOVY_HANDLER, DEFAULT_GLUE_UPDATE_TIME);
private static final ScriptJobHandler SCRIPT_JOB_HANDLER =
new ScriptJobHandler(
2, DEFAULT_GLUE_UPDATE_TIME, GLUE_JOB_SHELL_SCRIPT, GlueTypeEnum.GLUE_SHELL);
@Override
protected String getPackageName() {
return "io.opentelemetry.javaagent.instrumentation.xxljob.v2_3_0";
}
@Override
protected IJobHandler getGlueJobHandler() {
return GLUE_JOB_HANDLER;
}
@Override
protected IJobHandler getScriptJobHandler() {
return SCRIPT_JOB_HANDLER;
}
@Override
protected IJobHandler getCustomizeHandler() {
return new SimpleCustomizedHandler();
}
@Override
protected IJobHandler getCustomizeFailedHandler() {
return new CustomizedFailedHandler();
}
@Override
protected IJobHandler getMethodHandler() {
return METHOD_JOB_HANDLER;
}
}

View File

@ -0,0 +1,6 @@
plugins {
id("otel.javaagent-instrumentation")
}
dependencies {
compileOnly("com.xuxueli:xxl-job-core:2.1.2")
}

View File

@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.common;
import com.xxl.job.core.glue.GlueTypeEnum;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
import javax.annotation.Nullable;
class XxlJobCodeAttributesGetter implements CodeAttributesGetter<XxlJobProcessRequest> {
@Nullable
@Override
public Class<?> getCodeClass(XxlJobProcessRequest xxlJobProcessRequest) {
GlueTypeEnum glueType = xxlJobProcessRequest.getGlueType();
if (!glueType.isScript()) {
return xxlJobProcessRequest.getDeclaringClass();
}
return null;
}
@Nullable
@Override
public String getMethodName(XxlJobProcessRequest xxlJobProcessRequest) {
GlueTypeEnum glueType = xxlJobProcessRequest.getGlueType();
if (!glueType.isScript()) {
return xxlJobProcessRequest.getMethodName();
}
return null;
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.common;
public final class XxlJobConstants {
private XxlJobConstants() {}
public static final String XXL_GLUE_JOB_HANDLER = "com.xxl.job.core.handler.impl.GlueJobHandler";
public static final String XXL_SCRIPT_JOB_HANDLER =
"com.xxl.job.core.handler.impl.ScriptJobHandler";
public static final String XXL_METHOD_JOB_HANDLER =
"com.xxl.job.core.handler.impl.MethodJobHandler";
}

View File

@ -0,0 +1,44 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.common;
import com.xxl.job.core.glue.GlueTypeEnum;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import javax.annotation.Nullable;
class XxlJobExperimentalAttributeExtractor
implements AttributesExtractor<XxlJobProcessRequest, Void> {
private static final AttributeKey<String> XXL_JOB_GLUE_TYPE =
AttributeKey.stringKey("scheduling.xxl-job.glue.type");
private static final AttributeKey<Long> XXL_JOB_JOB_ID =
AttributeKey.longKey("scheduling.xxl-job.job.id");
@Override
public void onStart(
AttributesBuilder attributes,
Context parentContext,
XxlJobProcessRequest xxlJobProcessRequest) {
GlueTypeEnum glueType = xxlJobProcessRequest.getGlueType();
attributes.put(XXL_JOB_GLUE_TYPE, glueType.getDesc());
// store jobId in experimental attribute for script job.
if (glueType.isScript()) {
attributes.put(XXL_JOB_JOB_ID, xxlJobProcessRequest.getJobId());
}
}
@Override
public void onEnd(
AttributesBuilder attributes,
Context context,
XxlJobProcessRequest xxlJobProcessRequest,
@Nullable Void unused,
@Nullable Throwable error) {}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.common;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import java.util.function.Predicate;
public final class XxlJobHelper {
private final Instrumenter<XxlJobProcessRequest, Void> instrumenter;
private final Predicate<Object> failedStatusPredicate;
private XxlJobHelper(
Instrumenter<XxlJobProcessRequest, Void> instrumenter,
Predicate<Object> failedStatusPredicate) {
this.instrumenter = instrumenter;
this.failedStatusPredicate = failedStatusPredicate;
}
public static XxlJobHelper create(
Instrumenter<XxlJobProcessRequest, Void> instrumenter,
Predicate<Object> failedStatusPredicate) {
return new XxlJobHelper(instrumenter, failedStatusPredicate);
}
public Context startSpan(Context parentContext, XxlJobProcessRequest request) {
if (!instrumenter.shouldStart(parentContext, request)) {
return null;
}
return instrumenter.start(parentContext, request);
}
public void stopSpan(
Object result,
XxlJobProcessRequest request,
Throwable throwable,
Scope scope,
Context context) {
if (scope == null) {
return;
}
if (failedStatusPredicate.test(result)) {
request.setFailed();
}
scope.close();
instrumenter.end(context, request, null, throwable);
}
public void stopSpan(
XxlJobProcessRequest request, Throwable throwable, Scope scope, Context context) {
stopSpan(null, request, throwable, scope, context);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.common;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
public final class XxlJobInstrumenterFactory {
private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
InstrumentationConfig.get()
.getBoolean("otel.instrumentation.xxl-job.experimental-span-attributes", false);
public static Instrumenter<XxlJobProcessRequest, Void> create(String instrumentationName) {
XxlJobCodeAttributesGetter codeAttributesGetter = new XxlJobCodeAttributesGetter();
XxlJobSpanNameExtractor spanNameExtractor = new XxlJobSpanNameExtractor(codeAttributesGetter);
InstrumenterBuilder<XxlJobProcessRequest, Void> builder =
Instrumenter.<XxlJobProcessRequest, Void>builder(
GlobalOpenTelemetry.get(), instrumentationName, spanNameExtractor)
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
.setSpanStatusExtractor(
(spanStatusBuilder, xxlJobProcessRequest, response, error) -> {
if (error != null || xxlJobProcessRequest.isFailed()) {
spanStatusBuilder.setStatus(StatusCode.ERROR);
}
});
if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
builder.addAttributesExtractor(
AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "xxl-job"));
builder.addAttributesExtractor(new XxlJobExperimentalAttributeExtractor());
}
return builder.buildInstrumenter();
}
private XxlJobInstrumenterFactory() {}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.common;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.handler.IJobHandler;
import java.lang.reflect.Method;
public final class XxlJobProcessRequest {
private String methodName;
private int jobId;
private Class<?> declaringClass;
private boolean failed;
private final GlueTypeEnum glueType;
private XxlJobProcessRequest(GlueTypeEnum glueType) {
this.glueType = glueType;
}
public static XxlJobProcessRequest createRequestForMethod(
GlueTypeEnum glueType, Class<?> declaringClass, String methodName) {
XxlJobProcessRequest request = new XxlJobProcessRequest(glueType);
request.declaringClass = declaringClass;
request.methodName = methodName;
return request;
}
public static XxlJobProcessRequest createGlueJobRequest(IJobHandler handler) {
return createRequestForMethod(GlueTypeEnum.GLUE_GROOVY, handler.getClass(), "execute");
}
public static XxlJobProcessRequest createScriptJobRequest(GlueTypeEnum glueType, int jobId) {
XxlJobProcessRequest request = new XxlJobProcessRequest(glueType);
request.jobId = jobId;
return request;
}
public static XxlJobProcessRequest createSimpleJobRequest(IJobHandler handler) {
return createRequestForMethod(GlueTypeEnum.BEAN, handler.getClass(), "execute");
}
public static XxlJobProcessRequest createMethodJobRequest(Object target, Method method) {
return createRequestForMethod(
GlueTypeEnum.BEAN, target.getClass(), method != null ? method.getName() : null);
}
public void setFailed() {
failed = true;
}
public boolean isFailed() {
return failed;
}
public String getMethodName() {
return methodName;
}
public int getJobId() {
return jobId;
}
public Class<?> getDeclaringClass() {
return declaringClass;
}
public GlueTypeEnum getGlueType() {
return glueType;
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.xxljob.common;
import com.xxl.job.core.glue.GlueTypeEnum;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
class XxlJobSpanNameExtractor implements SpanNameExtractor<XxlJobProcessRequest> {
private final SpanNameExtractor<XxlJobProcessRequest> codeSpanNameExtractor;
XxlJobSpanNameExtractor(CodeAttributesGetter<XxlJobProcessRequest> getter) {
codeSpanNameExtractor = CodeSpanNameExtractor.create(getter);
}
@Override
public String extract(XxlJobProcessRequest request) {
GlueTypeEnum glueType = request.getGlueType();
if (glueType.isScript()) {
// TODO: need to discuss a better span name for script job in the future.
// for detail can refer to
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/10421#discussion_r1511532584
return glueType.getDesc();
}
return codeSpanNameExtractor.extract(request);
}
}

View File

@ -0,0 +1,11 @@
plugins {
id("otel.java-conventions")
}
dependencies {
implementation(project(":testing-common"))
compileOnly("com.xuxueli:xxl-job-core:2.1.2") {
exclude("org.codehaus.groovy", "groovy")
}
}

View File

@ -0,0 +1,170 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.xxljob;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static java.util.Arrays.asList;
import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.thread.JobThread;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public abstract class AbstractXxlJobTest {
@RegisterExtension
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@BeforeAll
static void setUp() {
XxlJobFileAppender.initLogPath("build/xxljob/log");
}
@Test
void testGlueJob() {
JobThread jobThread = new JobThread(1, getGlueJobHandler());
TriggerParam triggerParam = new TriggerParam();
triggerParam.setExecutorTimeout(0);
jobThread.pushTriggerQueue(triggerParam);
jobThread.start();
checkXxlJob(
"CustomizedGroovyHandler.execute",
StatusData.unset(),
GlueTypeEnum.GLUE_GROOVY,
"CustomizedGroovyHandler",
"execute");
jobThread.toStop("Test finish");
}
@Test
void testScriptJob() {
JobThread jobThread = new JobThread(2, getScriptJobHandler());
TriggerParam triggerParam = new TriggerParam();
triggerParam.setExecutorParams("");
triggerParam.setExecutorTimeout(0);
jobThread.pushTriggerQueue(triggerParam);
jobThread.start();
checkXxlJobWithoutCodeAttributes("GLUE(Shell)", StatusData.unset(), GlueTypeEnum.GLUE_SHELL, 2);
jobThread.toStop("Test finish");
}
@Test
void testSimpleJob() {
JobThread jobThread = new JobThread(3, getCustomizeHandler());
TriggerParam triggerParam = new TriggerParam();
triggerParam.setExecutorTimeout(0);
jobThread.pushTriggerQueue(triggerParam);
jobThread.start();
checkXxlJob(
"SimpleCustomizedHandler.execute",
StatusData.unset(),
GlueTypeEnum.BEAN,
getPackageName() + ".SimpleCustomizedHandler",
"execute");
jobThread.toStop("Test finish");
}
@Test
public void testMethodJob() {
// method handle is null if test is not supported by tested version of the library
Assumptions.assumeTrue(getMethodHandler() != null);
JobThread jobThread = new JobThread(4, getMethodHandler());
TriggerParam triggerParam = new TriggerParam();
triggerParam.setExecutorTimeout(0);
jobThread.pushTriggerQueue(triggerParam);
jobThread.start();
checkXxlJob(
"ReflectObject.echo",
StatusData.unset(),
GlueTypeEnum.BEAN,
"io.opentelemetry.instrumentation.xxljob.ReflectiveMethodsFactory$ReflectObject",
"echo");
jobThread.toStop("Test finish");
}
@Test
void testFailedJob() {
JobThread jobThread = new JobThread(5, getCustomizeFailedHandler());
TriggerParam triggerParam = new TriggerParam();
triggerParam.setExecutorTimeout(0);
jobThread.pushTriggerQueue(triggerParam);
jobThread.start();
checkXxlJob(
"CustomizedFailedHandler.execute",
StatusData.error(),
GlueTypeEnum.BEAN,
getPackageName() + ".CustomizedFailedHandler",
"execute");
jobThread.toStop("Test finish");
}
protected abstract IJobHandler getGlueJobHandler();
protected abstract IJobHandler getScriptJobHandler();
protected abstract IJobHandler getCustomizeHandler();
protected abstract IJobHandler getCustomizeFailedHandler();
protected abstract IJobHandler getMethodHandler();
protected abstract String getPackageName();
private static void checkXxlJob(
String spanName, StatusData statusData, List<AttributeAssertion> assertions) {
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasKind(SpanKind.INTERNAL)
.hasName(spanName)
.hasStatus(statusData)
.hasAttributesSatisfyingExactly(assertions)));
}
private static void checkXxlJob(
String spanName,
StatusData statusData,
GlueTypeEnum glueType,
String codeNamespace,
String codeFunction) {
List<AttributeAssertion> attributeAssertions = new ArrayList<>();
attributeAssertions.addAll(attributeAssertions(glueType));
attributeAssertions.add(equalTo(AttributeKey.stringKey("code.namespace"), codeNamespace));
attributeAssertions.add(equalTo(AttributeKey.stringKey("code.function"), codeFunction));
checkXxlJob(spanName, statusData, attributeAssertions);
}
private static void checkXxlJobWithoutCodeAttributes(
String spanName, StatusData statusData, GlueTypeEnum glueType, int jobId) {
List<AttributeAssertion> attributeAssertions = new ArrayList<>();
attributeAssertions.addAll(attributeAssertions(glueType));
attributeAssertions.add(equalTo(AttributeKey.longKey("scheduling.xxl-job.job.id"), jobId));
checkXxlJob(spanName, statusData, attributeAssertions);
}
private static List<AttributeAssertion> attributeAssertions(GlueTypeEnum glueType) {
return asList(
equalTo(AttributeKey.stringKey("job.system"), "xxl-job"),
equalTo(AttributeKey.stringKey("scheduling.xxl-job.glue.type"), glueType.getDesc()));
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.xxljob;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
public class CustomizedFailedHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String s) throws Exception {
return FAIL;
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.xxljob;
import com.xxl.job.core.biz.model.ReturnT;
import java.lang.reflect.Method;
class ReflectiveMethodsFactory {
private ReflectiveMethodsFactory() {}
public static class ReflectObject {
private ReflectObject() {}
public void initMethod() {}
public void destroyMethod() {}
public ReturnT<String> echo(String param) {
return new ReturnT<>("echo: " + param);
}
}
private static final Object SINGLETON_OBJECT = new ReflectObject();
static Object getTarget() {
return SINGLETON_OBJECT;
}
static Method getMethod() {
try {
return SINGLETON_OBJECT.getClass().getMethod("echo", String.class);
} catch (Throwable t) {
// Ignore
}
return null;
}
static Method getInitMethod() {
try {
return SINGLETON_OBJECT.getClass().getMethod("initMethod");
} catch (Throwable t) {
// Ignore
}
return null;
}
static Method getDestroyMethod() {
try {
return SINGLETON_OBJECT.getClass().getMethod("destroyMethod");
} catch (Throwable t) {
// Ignore
}
return null;
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.xxljob;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
public class SimpleCustomizedHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String s) throws Exception {
return new ReturnT<>("Hello World");
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.xxljob;
import java.lang.reflect.Method;
public class XxlJobTestingConstants {
private XxlJobTestingConstants() {}
public static final String GLUE_JOB_SHELL_SCRIPT = "echo 'hello'";
public static final long DEFAULT_GLUE_UPDATE_TIME = System.currentTimeMillis();
public static final Object METHOD_JOB_HANDLER_OBJECT = ReflectiveMethodsFactory.getTarget();
public static final Method METHOD_JOB_HANDLER_METHOD = ReflectiveMethodsFactory.getMethod();
public static final Method METHOD_JOB_HANDLER_INIT_METHOD =
ReflectiveMethodsFactory.getInitMethod();
public static final Method METHOD_JOB_HANDLER_DESTROY_METHOD =
ReflectiveMethodsFactory.getDestroyMethod();
public static final String GLUE_JOB_GROOVY_SOURCE_OLD =
"import com.xxl.job.core.handler.IJobHandler\n"
+ "import com.xxl.job.core.biz.model.ReturnT\n"
+ "class CustomizedGroovyHandler extends IJobHandler {\n"
+ " @Override\n"
+ " public ReturnT<String> execute(String s) throws Exception {\n"
+ " return new ReturnT<>(\"Hello World\")\n"
+ " }\n"
+ "}\n";
public static final String GLUE_JOB_GROOVY_SOURCE =
"import com.xxl.job.core.handler.IJobHandler\n"
+ "\n"
+ "class CustomizedGroovyHandler extends IJobHandler {\n"
+ " @Override\n"
+ " void execute() throws Exception {\n"
+ " }\n"
+ "}\n";
}

View File

@ -576,6 +576,11 @@ include(":instrumentation:wicket-8.0:common-testing")
include(":instrumentation:wicket-8.0:javaagent")
include(":instrumentation:wicket-8.0:wicket8-testing")
include(":instrumentation:wicket-8.0:wicket10-testing")
include(":instrumentation:xxl-job:xxl-job-1.9.2:javaagent")
include(":instrumentation:xxl-job:xxl-job-2.1.2:javaagent")
include(":instrumentation:xxl-job:xxl-job-2.3.0:javaagent")
include(":instrumentation:xxl-job:xxl-job-common:javaagent")
include(":instrumentation:xxl-job:xxl-job-common:testing")
include(":instrumentation:zio:zio-2.0:javaagent")
// benchmark