Re: [D] Using Custom ClassLoader to Isolate Log42j and Slf4j . Console log: Properties contains an invalid element or attr ibute "property" [logging-log4j2]

zhanjinhao (via GitHub) <[email protected]>
Newsgroups gmane.comp.jakarta.log4j.devel
Message-ID <ghd-D_kwDOAKJSSM4AiiR0-f3353863-d6e3-4aa9-a736-2a97e6055dc0__11328.7363130198$1761301997$gmane$org@gitbox.apache.org>
GitHub user zhanjinhao added a comment to the discussion: Using Custom ClassLoader to Isolate Log42j and Slf4j. Console log: Properties contains an invalid element or attribute "property"

@vy thanks.

@jhl221123 I think I've found the real reason. Not about findResource()/findResources(). **It is about findLoadedClass().** 

### Problem Analysis

I have compared my implementation with yours. And adjusting my implementation to yours step by step . I found if I don't introduce findLoadedClass() when overriding loadClass(), the directory will not be created correctly.

<img width="2189" height="818" alt="image" src="https://github.com/user-attachments/assets/25c9857a-9d23-4b59-bad5-aad5cf8edd90" />

If I don't extend URLClassLoader. And I still load class and resources in my way except including a check for already-loaded classes. the directory will be created correctly.

```

/**
 * It will use commons-io package, so I package my project to a fat jar.
 **/
package cn.addenda.loglearn;

import lombok.Getter;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class LogClassLoader6 extends ClassLoader {

  @Getter
  private static LogClassLoader6 DEFAULT_LOADER;
  private final List<String> logPrefixList = new ArrayList<>();

  List<Jar> jars;

  static {
    ClassLoader.registerAsParallelCapable();
  }

  public LogClassLoader6(ClassLoader parent) {
    super(parent);

    jars = doGetJars();
    logPrefixList.add("org.slf4j.");
    logPrefixList.add("org.apache.logging.log4j.");
    logPrefixList.add("org.apache.logging.slf4j.");
  }

  @Override
  protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
      Class<?> loadedClass = findLoadedClass(name);
      if (loadedClass != null) {
        return loadedClass;
      }

      boolean shouldIsolate = false;
      for (String prefix : logPrefixList) {
        if (name.startsWith(prefix)) {
          shouldIsolate = true;
          break;
        }
      }

      if (shouldIsolate) {
        loadedClass = doFindClass(name);
        if (resolve) {
          resolveClass(loadedClass);
        }
        return loadedClass;
      }

      return super.loadClass(name, resolve);
    }
  }


  @Override
  public Enumeration<URL> getResources(String name) throws IOException {
    List<URL> allResources = new ArrayList<>();
    for (Jar jar : jars) {
      JarEntry jarEntry = jar.jarFile.getJarEntry(name);
      if (jarEntry == null) {
        continue;
      }
      try {
        URL url = new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + name);
        allResources.add(url);
      } catch (Exception e) {
        System.out.println(String.format("find file {} error", name));
        e.printStackTrace();
      }
    }
    Iterator<URL> iterator = allResources.iterator();
    return new Enumeration<URL>() {
      @Override
      public boolean hasMoreElements() {
        return iterator.hasNext();
      }

      @Override
      public URL nextElement() {
        return iterator.next();
      }
    };
  }

  @Override
  public URL getResource(String name) {
    // todo 设置为线程上下文类加载器,再从这里读取外部目录的log4j2.xml
    for (Jar jar : jars) {
      JarEntry jarEntry = jar.jarFile.getJarEntry(name);
      if (jarEntry == null) {
        continue;
      }
      try {
        // 返回第一个
        URL url = new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + name);
        return url;
      } catch (Exception e) {
        System.out.println(String.format("find file {} error", name));
        e.printStackTrace();
      }
    }
    return null;
  }

  public static synchronized void initDefaultLoader() {
    if (DEFAULT_LOADER == null) {
//      ClassLoader extensionClassLoader = ClassLoader.getSystemClassLoader().getParent();
      ClassLoader extensionClassLoader = ClassLoader.getSystemClassLoader();
      DEFAULT_LOADER = new LogClassLoader6(extensionClassLoader);
    }
  }

  private Class<?> doFindClass(String name) throws ClassNotFoundException {
    List<Jar> _allJarList = jars;

    String concat = name.replace(".", "/").concat(".class");
    for (Jar jar : _allJarList) {
      JarEntry jarEntry = jar.jarFile.getJarEntry(concat);
      if (jarEntry == null) {
        continue;
      }
      try {
        URL url = new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + concat);
        byte[] byteArray = IOUtils.toByteArray(url);
        return defineClass(name, byteArray, 0, byteArray.length);
      } catch (Exception e) {
        System.out.println(String.format("find class %s error", name));
      }
    }
    throw new ClassNotFoundException("can not find " + name);
  }


  private List<Jar> doGetJars() {
    List<Jar> _allJarList = new ArrayList<>();
    File agentJarDir = AgentPackagePath.getPath();
    System.out.println("Agent base directory: " + agentJarDir);
    File logLibDir = new File(new File(agentJarDir, "lib"), "log");

    if (logLibDir.exists() && logLibDir.isDirectory()) {
      String[] list = logLibDir.list((dir, name) -> name.endsWith(".jar"));
      if (list == null || list.length == 0) {
        return _allJarList;
      }
      for (String s : list) {
        File jarSourceFile = new File(logLibDir, s);
        try {
          Jar jar = new Jar(new JarFile(jarSourceFile), jarSourceFile);
          _allJarList.add(jar);
          System.out.println(String.format("load jar %s success.", jarSourceFile));
        } catch (Exception e) {
          System.out.println(String.format("jar %s load fail.", jarSourceFile));
          e.printStackTrace();
        }
      }
    }
    return _allJarList;
  }

  private static class Jar {
    /**
     * jar文件对对应的jarFile对象
     */
    private final JarFile jarFile;
    /**
     * jar文件
     */
    private final File sourceFile;

    public Jar(JarFile jarFile, File sourceFile) {
      this.jarFile = jarFile;
      this.sourceFile = sourceFile;
    }
  }

}


```


In my production project, I will extent URLClassLoader because I am indeed not clear about operating jar.


### Different Class

With my previous LogClassLoader, the codebelow will output : loggerContextClass != loggerContextClass2. 

```
        loggerFactoryClass = Class.forName("org.slf4j.LoggerFactory", true, defaultLoader);
        loggerFactoryClass2 = Class.forName("org.slf4j.LoggerFactory", true, defaultLoader);
        if (loggerContextClass == loggerFactoryClass2) {
          System.out.println("loggerContextClass == loggerContextClass2");
        } else {
          System.out.println("loggerContextClass != loggerContextClass2");
        }
```

**A class‘s binary is loaded from the jar multiple times, each time the class is different.**


### Conclusion

So I think the real reason is that I don't check the already-loaded classes

However, as for how it affects log4j2, it's too difficult for me to analyze. Log4j2 loaded too many classes at startup.




GitHub link: https://github.com/apache/logging-log4j2/discussions/3960#discussioncomment-14771588

----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.