package org.stagemonitor.tracing;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import org.stagemonitor.core.Stagemonitor;
import org.stagemonitor.core.instrument.StagemonitorByteBuddyTransformer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class AbstractMonitorRequestsTransformer extends StagemonitorByteBuddyTransformer {
@Override
protected Class<? extends StagemonitorByteBuddyTransformer> getAdviceClass() {
return AbstractMonitorRequestsTransformer.class;
}
@Advice.OnMethodEnter(inline = false)
public static void monitorStart(@ParameterNames String parameterNames, @Advice.AllArguments Object[] args,
@RequestName String requestName, @Advice.Origin("#t") String className,
@Advice.Origin("#m") String methodName, @Advice.This(optional = true) Object thiz) {
final String[] paramNames = parameterNames.split(",");
Map<String, Object> params = new LinkedHashMap<String, Object>();
for (int i = 0; i < args.length; i++) {
params.put(paramNames[i], args[i]);
}
final MonitoredMethodRequest monitoredRequest = new MonitoredMethodRequest(Stagemonitor.getConfiguration(), requestName, null, params);
final TracingPlugin tracingPlugin = Stagemonitor.getPlugin(TracingPlugin.class);
tracingPlugin.getRequestMonitor().monitorStart(monitoredRequest);
if (requestName == null) {
TracingPlugin
.getCurrentSpan()
.setOperationName(getBusinessTransationName(thiz != null ? thiz.getClass().getName() : className, methodName));
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, inline = false)
public static void monitorStop(@Advice.Thrown Throwable exception) {
final RequestMonitor requestMonitor = Stagemonitor.getPlugin(TracingPlugin.class).getRequestMonitor();
if (exception != null && exception instanceof Exception) {
requestMonitor.recordException((Exception) exception);
}
requestMonitor.monitorStop();
}
@Override
protected int getOrder() {
return Integer.MIN_VALUE;
}
@Override
protected List<StagemonitorDynamicValue<?>> getDynamicValues() {
return Arrays.asList(new RequestNameDynamicValue(), new ParameterNamesDynamicValue());
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface RequestName {
}
public static class RequestNameDynamicValue extends StagemonitorDynamicValue<RequestName> {
@Override
public Class<RequestName> getAnnotationClass() {
return RequestName.class;
}
@Override
protected Object doResolve(TypeDescription instrumentedType,
MethodDescription instrumentedMethod,
ParameterDescription.InDefinedShape target,
AnnotationDescription.Loadable<RequestName> annotation,
Assigner assigner, boolean initialized) {
return getRequestName(instrumentedMethod);
}
}
public static String getRequestName(MethodDescription instrumentedMethod) {
final AnnotationDescription.Loadable<MonitorRequests> monitorRequestsLoadable = instrumentedMethod
.getDeclaredAnnotations()
.ofType(MonitorRequests.class);
if (monitorRequestsLoadable != null) {
final MonitorRequests monitorRequests = monitorRequestsLoadable.loadSilent();
if (!monitorRequests.requestName().isEmpty()) {
return monitorRequests.requestName();
}
if (monitorRequests.resolveNameAtRuntime()) {
return null;
}
}
return getBusinessTransationName(instrumentedMethod.getDeclaringType().getTypeName(), instrumentedMethod.getName());
}
private static String getBusinessTransationName(String className, String methodName) {
return configuration.getConfig(TracingPlugin.class)
.getBusinessTransactionNamingStrategy()
.getBusinessTransationName(className, methodName);
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ParameterNames {
}
public static class ParameterNamesDynamicValue extends StagemonitorDynamicValue<ParameterNames>{
@Override
public Class<ParameterNames> getAnnotationClass() {
return ParameterNames.class;
}
@Override
protected Object doResolve(TypeDescription instrumentedType,
MethodDescription instrumentedMethod,
ParameterDescription.InDefinedShape target,
AnnotationDescription.Loadable<ParameterNames> annotation,
Assigner assigner, boolean initialized) {
StringBuilder params = new StringBuilder();
for (ParameterDescription param : instrumentedMethod.getParameters()) {
params.append(param.getName()).append(',');
}
return params.toString();
}
}
}