Skip to content

Commit

Permalink
refactor: record custom spans with action (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
EddeCCC committed Oct 7, 2024
1 parent 6f85d9e commit e8f5abf
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 101 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/* (C) 2024 */
package rocks.inspectit.gepard.agent.instrumentation.hook;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rocks.inspectit.gepard.agent.instrumentation.hook.action.SpanAction;
import rocks.inspectit.gepard.bootstrap.context.InternalInspectitContext;
import rocks.inspectit.gepard.bootstrap.instrumentation.IMethodHook;

/**
Expand All @@ -14,68 +13,48 @@
* we just log our method calls.
*/
public class MethodHook implements IMethodHook {
private static final Logger log = LoggerFactory.getLogger(MethodHook.class);

// TODO Property entryActions mit StartSpanAction drinnen
private final String methodName;

// TODO Property exitActions mit EndSpanAction drinnen
private final SpanAction spanAction;

// TODO diese Listen abarbeiten und execute() aufrufen
public MethodHook(String methodName, SpanAction spanAction) {
this.methodName = methodName;
this.spanAction = spanAction;
}

@Override
public AutoCloseable onEnter(String methodName, Object[] instrumentedMethodArgs, Object thiz) {
// Using our log4j here will not be visible in the target application...
String message =
String.format(
"inspectIT: Enter MethodHook with %d args in %s",
instrumentedMethodArgs.length, thiz.getClass().getName());
System.out.println(message);
public InternalInspectitContext onEnter(Object[] instrumentedMethodArgs, Object thiz) {
String spanName = thiz.getClass().getSimpleName() + "." + methodName;
AutoCloseable spanScope = null;

AutoCloseable spanScope = startSpan(methodName);
try {
spanScope = spanAction.startSpan(spanName);
} catch (Throwable t) {
log.error("Could not execute start-span-action", t);
}

// Using our log4j here will not be visible in the target application...
System.out.println("HELLO GEPARD : " + methodName);
return spanScope;
return new InternalInspectitContext(this, spanScope);
}

@Override
public void onExit(
String methodName,
AutoCloseable spanScope,
InternalInspectitContext context,
Object[] instrumentedMethodArgs,
Object thiz,
Object returnValue,
Throwable thrown) {
AutoCloseable spanScope = context.getSpanScope();
try {
// Using our log4j here will not be visible in the target application...
String exceptionMessage = Objects.nonNull(thrown) ? thrown.getMessage() : "no exception";
String returnMessage = Objects.nonNull(returnValue) ? returnValue.toString() : "nothing";
String message =
String.format(
"inspectIT: Exit MethodHook who returned %s and threw %s",
returnMessage, exceptionMessage);
System.out.println(message);
endSpan(methodName, spanScope);

System.out.println("BYE GEPARD");
spanAction.endSpan(spanScope);
} catch (Throwable t) {
t.printStackTrace();
log.error("Could not execute end-span-action", t);
}
}

// TODO move to StartSpanAction
private AutoCloseable startSpan(String name) {
Span current = Span.current();
Tracer tracer = GlobalOpenTelemetry.getTracer("inspectit-gepard");
Span span = tracer.spanBuilder(name).setParent(Context.current()).startSpan();
return span.makeCurrent();
}

// TODO move to EndSpanAction
// TODO Zuerst scope schließen oder span beenden?
private void endSpan(String name, AutoCloseable spanScope) throws Exception {
Span current = Span.current();
System.out.println("END SPAN FOR " + name);

spanScope.close(); // TODO Exception handling
current.end();
// Using our log4j here will not be visible in the target application...
System.out.println("BYE GEPARD");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.bytebuddy.description.method.MethodDescription;
import rocks.inspectit.gepard.agent.instrumentation.hook.configuration.ClassHookConfiguration;
import rocks.inspectit.gepard.agent.instrumentation.hook.configuration.HookedMethods;
import rocks.inspectit.gepard.agent.instrumentation.hook.util.MethodHookGenerator;

/** Stores the method hook configurations of all instrumented classes. */
public class MethodHookState {
Expand Down Expand Up @@ -78,7 +79,8 @@ public int updateHooks(Class<?> clazz, ClassHookConfiguration classConfiguration
String signature = getSignature(method);
Optional<MethodHook> maybeHook = getCurrentHook(clazz, signature);
if (maybeHook.isEmpty()) {
setHook(clazz, signature, new MethodHook());
MethodHook hook = MethodHookGenerator.createHook(method);
setHook(clazz, signature, hook);
operationCounter.addAndGet(1);
}
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* (C) 2024 */
package rocks.inspectit.gepard.agent.instrumentation.hook.action;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import java.util.Objects;
import rocks.inspectit.gepard.agent.instrumentation.hook.action.exception.CouldNotCloseSpanScopeException;
import rocks.inspectit.gepard.agent.internal.otel.OpenTelemetryAccessor;

/** This action contains the logic to start and end a {@link Span}. */
public class SpanAction {

private static final String INSTRUMENTATION_SCOPE_NAME = "inspectit-gepard";

private final OpenTelemetry openTelemetry;

public SpanAction() {
this.openTelemetry = OpenTelemetryAccessor.getOpenTelemetry();
}

/**
* Starts a new {@link Span}. Should be called before {@link SpanAction#endSpan}.
*
* @param spanName the name of the created span
* @return the scope of the started span
*/
public AutoCloseable startSpan(String spanName) {
Tracer tracer = openTelemetry.getTracer(INSTRUMENTATION_SCOPE_NAME);
Span span = tracer.spanBuilder(spanName).setParent(Context.current()).startSpan();
return span.makeCurrent();
}

/**
* Ends the current span and closes its scope. Should be called after {@link
* SpanAction#startSpan}.
*
* @param spanScope the scope of the span, which should be finished
*/
public void endSpan(AutoCloseable spanScope) {
Span current = Span.current();

if (Objects.nonNull(spanScope)) {
try {
spanScope.close();
} catch (Exception e) {
throw new CouldNotCloseSpanScopeException(e);
}
}
current.end();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* (C) 2024 */
package rocks.inspectit.gepard.agent.instrumentation.hook.action.exception;

import io.opentelemetry.context.Scope;

/** Exception errors, while trying to close a {@link Scope} */
public class CouldNotCloseSpanScopeException extends RuntimeException {

public CouldNotCloseSpanScopeException(Throwable cause) {
super("Could not close span scope", cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* (C) 2024 */
package rocks.inspectit.gepard.agent.instrumentation.hook.util;

import net.bytebuddy.description.method.MethodDescription;
import rocks.inspectit.gepard.agent.instrumentation.hook.MethodHook;
import rocks.inspectit.gepard.agent.instrumentation.hook.action.SpanAction;

public class MethodHookGenerator {

private MethodHookGenerator() {}

/**
* Creates an executable method hook based on the given configuration.
*
* @param method the hooked method
* @return the created method hook
*/
public static MethodHook createHook(MethodDescription method) {
SpanAction spanAction = new SpanAction();
return new MethodHook(method.getName(), spanAction);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* (C) 2024 */
package rocks.inspectit.gepard.agent.internal.otel;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import java.util.Objects;

/**
* Singleton to access the {@link OpenTelemetry} instance. We use this accessor, because according
* to the documentation of {@link GlobalOpenTelemetry}, the method should only be called once during
* the application.
*/
public class OpenTelemetryAccessor {

private static OpenTelemetry openTelemetry;

private OpenTelemetryAccessor() {}

/**
* Get the global {@link OpenTelemetry} instance. The global instance should have been set by the
* OpenTelemetry Java agent beforehand.
*
* @return the global {@link OpenTelemetry} instance
*/
public static OpenTelemetry getOpenTelemetry() {
if (Objects.isNull(openTelemetry)) {
openTelemetry = GlobalOpenTelemetry.get();
}
return openTelemetry;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* (C) 2024 */
package rocks.inspectit.gepard.agent.transformation;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.utility.JavaModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Callbacks, which are executed before or after {@link DynamicTransformer#transform}. Currently,
* just used for debugging transformation.
*/
public class InspectitListener implements AgentBuilder.Listener {
private static final Logger log = LoggerFactory.getLogger(InspectitListener.class);

@Override
public void onError(
String typeName,
ClassLoader classLoader,
JavaModule module,
boolean loaded,
Throwable throwable) {
log.debug("Dynamic transformation failed for type '{}': {}", typeName, throwable.getMessage());
}

@Override
public void onDiscovery(
String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {}

@Override
public void onTransformation(
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule module,
boolean loaded,
DynamicType dynamicType) {}

@Override
public void onIgnored(
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule module,
boolean loaded) {}

@Override
public void onComplete(
String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public static TransformationManager create(InstrumentationState instrumentationS
*/
public AgentBuilder modify(AgentBuilder agentBuilder) {
DynamicTransformer transformer = new DynamicTransformer(instrumentationState);
InspectitListener listener = new InspectitListener();

// In the future, we might add a white- or black-list for types
return agentBuilder.type(any()).transform(transformer);
return agentBuilder.type(any()).transform(transformer).with(listener);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ public static InternalInspectitContext onEnter(
@Advice.AllArguments Object[] args,
@Advice.This Object thiz,
@Advice.Origin("#t") Class<?> declaringClass,
@Advice.Origin("#m#s") String signature,
@Advice.Origin("#m") String method) {
@Advice.Origin("#m#s") String signature) {
System.out.println(
"Executing ENTRY Advice in method: "
+ signature
+ " of class: "
+ thiz.getClass().getName());
IMethodHook hook = Instances.hookManager.getHook(declaringClass, signature);
AutoCloseable spanScope = hook.onEnter(method, args, thiz);

return new InternalInspectitContext(hook, spanScope);
IMethodHook hook = Instances.hookManager.getHook(declaringClass, signature);
return hook.onEnter(args, thiz);
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
Expand All @@ -35,17 +33,15 @@ public static void onExit(
@Advice.This Object thiz,
@Advice.Thrown Throwable throwable,
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returnValue,
@Advice.Origin("#m#s") String signature,
@Advice.Origin("#m") String method,
@Advice.Enter InternalInspectitContext inspectitContext) {
@Advice.Enter InternalInspectitContext context,
@Advice.Origin("#m#s") String signature) {
System.out.println(
"Executing EXIT Advice in method: "
+ signature
+ " of class: "
+ thiz.getClass().getName());

IMethodHook hook = inspectitContext.getHook();
AutoCloseable spanScope = inspectitContext.getScope();
hook.onExit(method, spanScope, args, thiz, returnValue, throwable);
IMethodHook hook = context.getHook();
hook.onExit(context, args, thiz, returnValue, throwable);
}
}
Loading

0 comments on commit e8f5abf

Please sign in to comment.