-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit of log method weaving hook
- Loading branch information
0 parents
commit f0026f0
Showing
11 changed files
with
497 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.classpath | ||
.project | ||
.settings/ | ||
target/ | ||
.DS_Store | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Sling Observability Weaving Hooks | ||
|
||
Library of weaving hooks to ease the process of observability in out-of-the-box code | ||
|
||
## Log Method Weaving Hook | ||
|
||
When all else fails, and you have no logs available because there are no log statements in the out-of-the-box classes, use this Log Method Weaving Hook to add a dynamic log statements | ||
|
||
### Usage | ||
|
||
Install the bundle in start level 1 and add an OSGi config for every method log you would like to add. | ||
|
||
Example, search for your classname, method name and amount of parameters you want to log and add an OSGi config `be.orbinson.sling.observability.weavinghooks.logmethod.LogMethodWeavingHookConfiguration~MyClass-doGet.cfg.json` | ||
|
||
```json | ||
{ | ||
"className": "my.package.MyClass", | ||
"methodName": "doGet", | ||
"amountOfParameters": 2 | ||
} | ||
``` | ||
|
||
To make the weaving hook work, either a framework restart of an entire java process restart is required. | ||
|
||
## Future | ||
|
||
- Add weaving hooks to create custom spans and metrics using OpenTelemetry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project | ||
xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
|
||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>be.orbinson.sling</groupId> | ||
<artifactId>sling-observability-weavinghooks</artifactId> | ||
<packaging>bundle</packaging> | ||
<version>0.0.1-SNAPSHOT</version> | ||
|
||
<name>Sling Observability Weaving Hooks</name> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.felix</groupId> | ||
<artifactId>maven-bundle-plugin</artifactId> | ||
<version>5.1.9</version> | ||
<extensions>true</extensions> | ||
<configuration> | ||
<outputDirectory>${basedir}/target/classes</outputDirectory> | ||
<instructions> | ||
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName> | ||
<Bundle-Version>${project.version}</Bundle-Version> | ||
<Embed-Dependency>asm,asm-util,asm-tree,asm-analysis</Embed-Dependency> | ||
</instructions> | ||
</configuration> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<configuration> | ||
<source>1.8</source> | ||
<target>1.8</target> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
<properties> | ||
<sling.host>localhost</sling.host> | ||
<sling.port>8080</sling.port> | ||
<sling.user>admin</sling.user> | ||
<sling.password>admin</sling.password> | ||
</properties> | ||
|
||
<profiles> | ||
<profile> | ||
<id>auto-deploy</id> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.sling</groupId> | ||
<artifactId>sling-maven-plugin</artifactId> | ||
<version>2.4.0</version> | ||
<configuration> | ||
<slingUrl> | ||
http://${sling.host}:${sling.port}/apps/sling-observability-weaving-hooks/install/1 | ||
</slingUrl> | ||
<deploymentMethod>SlingPostServlet</deploymentMethod> | ||
<user>${sling.user}</user> | ||
<password>${sling.password}</password> | ||
</configuration> | ||
<executions> | ||
<execution> | ||
<id>install-bundle</id> | ||
<goals> | ||
<goal>install</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</profile> | ||
</profiles> | ||
|
||
<dependencies> | ||
<!-- OSGi --> | ||
<dependency> | ||
<groupId>org.osgi</groupId> | ||
<artifactId>osgi.core</artifactId> | ||
<version>7.0.0</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.osgi</groupId> | ||
<artifactId>osgi.cmpn</artifactId> | ||
<version>7.0.0</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<!-- Bytecode transformation --> | ||
<dependency> | ||
<groupId>org.ow2.asm</groupId> | ||
<artifactId>asm</artifactId> | ||
<version>9.7</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.ow2.asm</groupId> | ||
<artifactId>asm-util</artifactId> | ||
<version>9.7</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.ow2.asm</groupId> | ||
<artifactId>asm-tree</artifactId> | ||
<version>9.7</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.ow2.asm</groupId> | ||
<artifactId>asm-analysis</artifactId> | ||
<version>9.7</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<!-- Logging --> | ||
<dependency> | ||
<groupId>org.slf4j</groupId> | ||
<artifactId>slf4j-api</artifactId> | ||
<version>1.7.36</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<!-- Testing --> | ||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter</artifactId> | ||
<version>5.10.2</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
</project> |
48 changes: 48 additions & 0 deletions
48
src/main/java/be/orbinson/sling/observability/weavinghooks/logmethod/LogMethodAdapter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package be.orbinson.sling.observability.weavinghooks.logmethod; | ||
|
||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Type; | ||
|
||
import static org.objectweb.asm.Opcodes.*; | ||
|
||
/** | ||
* Method visitor that will add a LoggerFactory log statement call of all parameters when entering a method | ||
*/ | ||
public class LogMethodAdapter extends MethodVisitor { | ||
|
||
private final String classDescriptor; | ||
private final String methodName; | ||
private final String methodDescriptor; | ||
private final String logLevel; | ||
|
||
public LogMethodAdapter(MethodVisitor mv, String classDescriptor, String methodName, String methodDescriptor, String logLevel) { | ||
super(ASM9, mv); | ||
this.classDescriptor = classDescriptor; | ||
this.methodName = methodName; | ||
this.methodDescriptor = methodDescriptor; | ||
this.logLevel = logLevel; | ||
} | ||
|
||
|
||
@Override | ||
public void visitCode() { | ||
mv.visitVarInsn(ALOAD, 0); | ||
mv.visitLdcInsn(Type.getType(classDescriptor)); | ||
mv.visitMethodInsn(INVOKESTATIC, "org/slf4j/LoggerFactory", "getLogger", "(Ljava/lang/Class;)Lorg/slf4j/Logger;", false); | ||
StringBuilder logString = new StringBuilder(methodName); | ||
Type[] types = Type.getArgumentTypes(methodDescriptor); | ||
for (int i = 0; i < types.length; i++) { | ||
logString.append(String.format(", param_%s: <{}>", i + 1)); | ||
} | ||
mv.visitLdcInsn(logString.toString()); | ||
mv.visitInsn(ICONST_0 + types.length); | ||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); | ||
for (int i = 0; i < types.length; i++) { | ||
mv.visitInsn(DUP); | ||
mv.visitInsn(ICONST_0 + i); | ||
mv.visitVarInsn(ALOAD, i + 1); | ||
mv.visitInsn(AASTORE); | ||
} | ||
mv.visitMethodInsn(INVOKEINTERFACE, "org/slf4j/Logger", logLevel.toLowerCase(), "(Ljava/lang/String;[Ljava/lang/Object;)V", true); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
...in/java/be/orbinson/sling/observability/weavinghooks/logmethod/LogMethodClassVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package be.orbinson.sling.observability.weavinghooks.logmethod; | ||
|
||
import org.objectweb.asm.ClassVisitor; | ||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Opcodes; | ||
import org.objectweb.asm.Type; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* ClassVisitor that will add the {@link LogMethodAdapter} when the method name matches with the requested method name | ||
*/ | ||
public class LogMethodClassVisitor extends ClassVisitor { | ||
|
||
private final Logger log = LoggerFactory.getLogger(getClass()); | ||
|
||
private final String methodName; | ||
private final String className; | ||
private final String logLevel; | ||
private String classDescriptor; | ||
|
||
public LogMethodClassVisitor(ClassVisitor cv, String className, String methodName, String logLevel) { | ||
super(Opcodes.ASM9, cv); | ||
this.cv = cv; | ||
this.className = className; | ||
this.methodName = methodName; | ||
this.logLevel = logLevel; | ||
} | ||
|
||
@Override | ||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
cv.visit(version, access, name, signature, superName, interfaces); | ||
this.classDescriptor = Type.getObjectType(name).getDescriptor(); | ||
} | ||
|
||
@Override | ||
public MethodVisitor visitMethod( | ||
int access, | ||
String name, | ||
String desc, | ||
String signature, | ||
String[] exceptions) { | ||
|
||
MethodVisitor mv; | ||
mv = cv.visitMethod(access, name, desc, signature, exceptions); | ||
if (mv != null) { | ||
if (name.equals(methodName)) { | ||
log.debug("Adding dynamic log method to class {} and method {}", className, methodName); | ||
mv = new LogMethodAdapter(mv, classDescriptor, methodName, desc, logLevel); | ||
} | ||
} | ||
return mv; | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
...ain/java/be/orbinson/sling/observability/weavinghooks/logmethod/LogMethodWeavingHook.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package be.orbinson.sling.observability.weavinghooks.logmethod; | ||
|
||
import org.objectweb.asm.ClassReader; | ||
import org.objectweb.asm.ClassVisitor; | ||
import org.objectweb.asm.ClassWriter; | ||
import org.objectweb.asm.util.TraceClassVisitor; | ||
import org.osgi.framework.hooks.weaving.WeavingHook; | ||
import org.osgi.framework.hooks.weaving.WovenClass; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.PrintWriter; | ||
|
||
public class LogMethodWeavingHook implements WeavingHook { | ||
|
||
private final Logger log = LoggerFactory.getLogger(getClass()); | ||
|
||
private final PrintWriter printWriter = new PrintWriter(System.out); | ||
|
||
private final String className; | ||
private final String methodName; | ||
private final String logLevel; | ||
private final boolean enableTraceVisitor; | ||
|
||
public LogMethodWeavingHook(LogMethodWeavingHookConfiguration config) { | ||
this.className = config.getClassName(); | ||
this.methodName = config.getMethodName(); | ||
this.logLevel = config.getLogLevel(); | ||
this.enableTraceVisitor = config.isEnableTraceVisitor(); | ||
} | ||
|
||
@Override | ||
public void weave(WovenClass wovenClass) { | ||
if (wovenClass.getClassName().equals(className)) { | ||
log.debug("Adding dynamic log methods to class {}", wovenClass.getClassName()); | ||
addLogMethodToClass(wovenClass); | ||
addDynamicImports(wovenClass); | ||
} | ||
} | ||
|
||
private void addLogMethodToClass(WovenClass wovenClass) { | ||
final ClassReader cr = new ClassReader(wovenClass.getBytes()); | ||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); | ||
ClassVisitor logMethodClassVisitor; | ||
if (enableTraceVisitor) { | ||
final TraceClassVisitor tcv = new TraceClassVisitor(cw, printWriter); | ||
logMethodClassVisitor = new LogMethodClassVisitor(tcv, className, methodName, logLevel); | ||
} else { | ||
logMethodClassVisitor = new LogMethodClassVisitor(cw, className, methodName, logLevel); | ||
} | ||
cr.accept(logMethodClassVisitor, 0); | ||
wovenClass.setBytes(cw.toByteArray()); | ||
} | ||
|
||
/** | ||
* Required to add sl4fj as dynamic import if it would not be used in the bundle | ||
*/ | ||
private void addDynamicImports(WovenClass wovenClass) { | ||
wovenClass.getDynamicImports().add("org.slf4j"); | ||
} | ||
|
||
} |
Oops, something went wrong.