Java ASM menggunakan kaedah pensuisan dinamik tahap log balik
May 09, 2023 pm 01:25 PMLatar Belakang
Semuanya ada sebab dan akibat, dan semuanya didorong oleh peristiwa. Penukaran tahap log penyelesaian ini datang dari latar belakang ini:
Pada satu persekitaran pengeluaran, terdapat ratusan dan hampir seribu perkhidmatan mikro
Menukar tahap log tidak memerlukan memulakan semula perkhidmatan, memerlukan kesan segera
Terpulang kepada pembangun perniagaan untuk mengubah suai kod atau menambah konfigurasi pergantungan yang berkaitan, dsb. Ia melibatkan pelbagai jenis isu dan kemajuannya perlahan
Penapisan dinamik dan masa nyata log sampah kemudian untuk mengurangkan kos ruang IO dan cakera
Pengenalan log balik
Sebelum melancarkan perang dengan musuh, hanya yang pertama Hanya dengan memahami situasi musuh kita boleh mencapai kemenangan dalam setiap pertempuran. Jika anda ingin menukar tahap log balik secara dinamik, anda mesti mempunyai sekurang-kurangnya pemahaman awal tentang log balik dan lihat jika ia menyediakan penyelesaian pelaksanaan siap sedia. Di bawah ialah pengenalan ringkas kepada log balik yang berkaitan dengan keperluan ini.
Logback ialah komponen log sumber terbuka untuk Java, yang ditulis oleh pengasas log4j Ia kini dibahagikan kepada 3 modul
logback-core: modul kod teras <.>
- logback-classic: versi log4j yang dipertingkatkan, yang turut melaksanakan antara muka
slf4j
- logback-access: modul akses dan Servlet integrasi kontena disediakan Fungsi mengakses log melalui Http
- Kelas ContextInitializer ialah pelaksanaan logik proses konfigurasi automatik log balik
- Tahap log dikekalkan dan digunakan oleh Logger. Tahap pembolehubah ahlinya dikekalkan oleh Logger
- Logger mempunyai tiga kaedah keluaran log penapis dengan parameter berbeza: filterAndLog_0_Or3Plus, filterAndLog_1, filterAndLog_2
- setLevel in Logger adalah untuk mengekalkan tahap log
<configuration scan="true" scanPeriod="30 seconds" debug="false">
- Perkhidmatan perlu dimulakan semula setiap kali selang imbasan dilaraskan
- Lebih daripada 90 % daripada imbasan Semuanya sia-sia, kerana tahap log dalam pengeluaran tidak boleh ditukar dengan kerap, dan ia tidak dibenarkan untuk melakukannya
- Kesannya bukan masa nyata ia ditetapkan untuk mengimbas setiap minit atau setiap beberapa minit, Kemudian membuat pelarasan tahap log berkuat kuasa tidak akan berkuat kuasa serta-merta, tetapi ini boleh diabaikan
- Penyelesaian ini tidak dapat memenuhi log sampah kami membuang keperluan, seperti membuang log berdasarkan keluaran kata kunci tertentu. Atas sebab sejarah ini, banyak log sampah dicetak Memandangkan kos masa, adalah mustahil untuk penyelidikan dan pembangunan perniagaan untuk mengoptimumkannya.
<dependencies> <dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>7.1</version> </dependency> <dependency> <artifactId>asm-commons</artifactId> <groupId>org.ow2.asm</groupId> <version>7.1</version> </dependency> <dependency> <groupId>com.sun</groupId> <artifactId>tools</artifactId> <version>1.8</version> <scope>system</scope> <systemPath>/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/tools.jar</systemPath> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.2.0</version> <configuration> <archive> <manifestEntries> <!-- 主程序启动类 --> <Agent-Class> agent.LogbackAgentMain </Agent-Class> <!-- 允许重新定义类 --> <Can-Redefine-Classes>true</Can-Redefine-Classes> <!-- 允许转换并重新加载类 --> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> <compilerArguments> <verbose /> <!-- 将jdk的依赖jar打入项目中--> <bootclasspath>${java.home}/lib/rt.jar</bootclasspath> </compilerArguments> </configuration> </plugin> </plugins> </build>
package agent; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; /** * @author dengbp * @ClassName LogbackAgentMain * @Description attach 启动器 * @date 3/25/22 6:27 PM */ public class LogbackAgentMain { private static String FILTER_CLASS = "ch.qos.logback.classic.Logger"; public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException { System.out.println("agentArgs:" + agentArgs); inst.addTransformer(new LogBackFileTransformer(agentArgs), true); Class[] classes = inst.getAllLoadedClasses(); for (int i = 0; i < classes.length; i++) { if (FILTER_CLASS.equals(classes[i].getName())) { System.out.println("----重新加载Logger开始----"); inst.retransformClasses(classes[i]); System.out.println("----重新加载Logger完毕----"); break; } } } }
package agent; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassVisitor; import jdk.internal.org.objectweb.asm.ClassWriter; import java.lang.instrument.ClassFileTransformer; import java.security.ProtectionDomain; /** * @author dengbp * @ClassName LogBackFileTransformer * @Description 字节码文件转换器 * @date 3/25/22 6:25 PM */ public class LogBackFileTransformer implements ClassFileTransformer { private final String level; private static String CLASS_NAME = "ch/qos/logback/classic/Logger"; public LogBackFileTransformer(String level) { this.level = level; } @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (!CLASS_NAME.equals(className)) { return classfileBuffer; } ClassReader cr = new ClassReader(classfileBuffer); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); ClassVisitor cv1 = new LogBackClassVisitor(cw, level); /*ClassVisitor cv2 = new LogBackClassVisitor(cv1);*/ // asm框架使用到访问模式和责任链模式 // ClassReader 只需要 accept 责任链中的头节点处的 ClassVisitor即可 cr.accept(cv1, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); System.out.println("end..."); return cw.toByteArray(); } }
package agent; import jdk.internal.org.objectweb.asm.ClassVisitor; import jdk.internal.org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * @author dengbp * @ClassName LogBackClassVisitor * @Description Logger类元素访问者 * @date 3/25/22 5:01 PM */ public class LogBackClassVisitor extends ClassVisitor { private final String level; /** * asm版本 */ private static final int ASM_VERSION = Opcodes.ASM4; public LogBackClassVisitor(ClassVisitor classVisitor, String level) { super(ASM_VERSION, classVisitor); this.level = level; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new LogFilterMethodVisitor(api, mv, access, name, descriptor, level); } }
package agent; import jdk.internal.org.objectweb.asm.Label; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.commons.AdviceAdapter; import org.objectweb.asm.Opcodes; /** * @author dengbp * @ClassName LogFilterMethodVisitor * @Description Logger类日志过滤方法元素访问者 * @date 3/25/22 5:01 PM */ public class LogFilterMethodVisitor extends AdviceAdapter { private String methodName; private final String level; private static final String filterAndLog_1 = "filterAndLog_1"; private static final String filterAndLog_2 = "filterAndLog_2"; private static final String filterAndLog_0_Or3Plus = "filterAndLog_0_Or3Plus"; protected LogFilterMethodVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor, String level) { super(api, methodVisitor, access, name, descriptor); this.methodName = name; this.level = level; } /** * Description 在访问方法的头部时被访问 * @param * @return void * @Author dengbp * @Date 3:36 PM 4/1/22 **/ @Override public void visitCode() { System.out.println("visitCode method"); super.visitCode(); } @Override protected void onMethodEnter() { System.out.println("开始重写日志级别为:"+level); System.out.println("----准备修改方法----"); if (filterAndLog_1.equals(methodName)) { modifyLogLevel_1(); } if (filterAndLog_2.equals(methodName)) { modifyLogLevel_2(); } if (filterAndLog_0_Or3Plus.equals(methodName)) { modifyLogLevel_3(); } System.out.println("重写日志级别成功...."); }
/** * Description 修改目标方法:filterAndLog_1 * @param * @return void * @Author dengbp * @Date 2:20 PM 3/31/22 **/ private void modifyLogLevel_1(){ Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(390, l0); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitLdcInsn(level); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "ch/qos/logback/classic/Level", "toLevel", "(Ljava/lang/String;)Lch/qos/logback/classic/Level;", false); mv.visitFieldInsn(Opcodes.GETFIELD, "ch/qos/logback/classic/Level", "levelInt", "I"); mv.visitFieldInsn(Opcodes.PUTFIELD, "ch/qos/logback/classic/Logger", "effectiveLevelInt", "I"); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLineNumber(392, l1); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, "ch/qos/logback/classic/Logger", "loggerContext", "Lch/qos/logback/classic/LoggerContext;"); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 3); mv.visitVarInsn(Opcodes.ALOAD, 4); mv.visitVarInsn(Opcodes.ALOAD, 5); mv.visitVarInsn(Opcodes.ALOAD, 6); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "ch/qos/logback/classic/LoggerContext", "getTurboFilterChainDecision_1", "(Lorg/slf4j/Marker;Lch/qos/logback/classic/Logger;Lch/qos/logback/classic/Level;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Throwable;)Lch/qos/logback/core/spi/FilterReply;", false); mv.visitVarInsn(Opcodes.ASTORE, 7); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLineNumber(394, l2); mv.visitVarInsn(Opcodes.ALOAD, 7); mv.visitFieldInsn(Opcodes.GETSTATIC, "ch/qos/logback/core/spi/FilterReply", "NEUTRAL", "Lch/qos/logback/core/spi/FilterReply;"); Label l3 = new Label(); mv.visitJumpInsn(Opcodes.IF_ACMPNE, l3); Label l4 = new Label(); mv.visitLabel(l4); mv.visitLineNumber(395, l4); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, "ch/qos/logback/classic/Logger", "effectiveLevelInt", "I"); mv.visitVarInsn(Opcodes.ALOAD, 3); mv.visitFieldInsn(Opcodes.GETFIELD, "ch/qos/logback/classic/Level", "levelInt", "I"); Label l5 = new Label(); mv.visitJumpInsn(Opcodes.IF_ICMPLE, l5); Label l6 = new Label(); mv.visitLabel(l6); mv.visitLineNumber(396, l6); mv.visitInsn(Opcodes.RETURN); mv.visitLabel(l3); mv.visitLineNumber(398, l3); mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"ch/qos/logback/core/spi/FilterReply"}, 0, null); mv.visitVarInsn(Opcodes.ALOAD, 7); mv.visitFieldInsn(Opcodes.GETSTATIC, "ch/qos/logback/core/spi/FilterReply", "DENY", "Lch/qos/logback/core/spi/FilterReply;"); mv.visitJumpInsn(Opcodes.IF_ACMPNE, l5); Label l7 = new Label(); mv.visitLabel(l7); mv.visitLineNumber(399, l7); mv.visitInsn(Opcodes.RETURN); mv.visitLabel(l5); mv.visitLineNumber(402, l5); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitVarInsn(Opcodes.ALOAD, 3); mv.visitVarInsn(Opcodes.ALOAD, 4); mv.visitInsn(Opcodes.ICONST_1); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); mv.visitInsn(Opcodes.DUP); mv.visitInsn(Opcodes.ICONST_0); mv.visitVarInsn(Opcodes.ALOAD, 5); mv.visitInsn(Opcodes.AASTORE); mv.visitVarInsn(Opcodes.ALOAD, 6); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "ch/qos/logback/classic/Logger", "buildLoggingEventAndAppend", "(Ljava/lang/String;Lorg/slf4j/Marker;Lch/qos/logback/classic/Level;Ljava/lang/String;[Ljava/lang/Object;Ljava/lang/Throwable;)V", false); Label l8 = new Label(); mv.visitLabel(l8); mv.visitLineNumber(403, l8); mv.visitInsn(Opcodes.RETURN); Label l9 = new Label(); mv.visitLabel(l9); mv.visitLocalVariable("this", "Lch/qos/logback/classic/Logger;", null, l0, l9, 0); mv.visitLocalVariable("localFQCN", "Ljava/lang/String;", null, l0, l9, 1); mv.visitLocalVariable("marker", "Lorg/slf4j/Marker;", null, l0, l9, 2); mv.visitLocalVariable("level", "Lch/qos/logback/classic/Level;", null, l0, l9, 3); mv.visitLocalVariable("msg", "Ljava/lang/String;", null, l0, l9, 4); mv.visitLocalVariable("param", "Ljava/lang/Object;", null, l0, l9, 5); mv.visitLocalVariable("t", "Ljava/lang/Throwable;", null, l0, l9, 6); mv.visitLocalVariable("decision", "Lch/qos/logback/core/spi/FilterReply;", null, l2, l9, 7); mv.visitMaxs(9, 8); mv.visitEnd(); }
import com.sun.tools.attach.VirtualMachine; import java.io.IOException; import java.io.UnsupportedEncodingException; /** * @author dengbp * @ClassName MyAttachMain * @Description jar 执行命令: * @date 3/25/22 4:12 PM */ public class MyAttachMain { private static final int ARGS_SIZE = 2; public static void main(String[] args) { if (args == null || args.length != ARGS_SIZE) { System.out.println("请输入进程id和日志级别(ALL、TRACE、DEBUG、INFO、WARN、ERROR、OFF),如:31722 info"); return; } VirtualMachine vm = null; try { System.out.println("修改的进程id:" + args[0]); vm = VirtualMachine.attach(args[0]); System.out.println("调整日志级别为:" + args[1]); vm.loadAgent(getJar(), args[1]); } catch (Exception e) { e.printStackTrace(); } finally { if (vm != null) { try { vm.detach(); } catch (IOException e) { e.printStackTrace(); } } } } private static String getJar() throws UnsupportedEncodingException { String jarFilePath = MyAttachMain.class.getProtectionDomain().getCodeSource().getLocation().getFile(); jarFilePath = java.net.URLDecoder.decode(jarFilePath, "UTF-8"); int beginIndex = 0; int endIndex = jarFilePath.length(); if (jarFilePath.contains(".jar")) { endIndex = jarFilePath.indexOf(".jar") + 4; } if (jarFilePath.startsWith("file:")) { beginIndex = jarFilePath.indexOf("file:") + 5; } jarFilePath = jarFilePath.substring(beginIndex, endIndex); System.out.println("jar path:" + jarFilePath); return jarFilePath; } }
- Cari program sasaran
- Execute jar
- Kesan
java -Xbootclasspath/a:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/tools.jar -cp change-log-agent-1.0.1.jar MyAttachMain 52433 DEBUG
java -Xbootclasspath/a:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/tools.jar -cp change-log-agent-1.0.1.jar MyAttachMain 52433 ERROR
java -Xbootclasspath/a:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/tools.jar -cp change-log-agent-1.0.1.jar MyAttachMain 52433 INFO
PS: Jika pengesahan gagal (disebabkan oleh: java.lang.verifyerror), sila berikan parameter jvm :-noverify
Atas ialah kandungan terperinci Java ASM menggunakan kaedah pensuisan dinamik tahap log balik. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Artikel Panas

Alat panas Tag

Artikel Panas

Tag artikel panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas

Cuti atau kembali dari Java 8 Stream Foreach?
