Skip to content

Commit

Permalink
support biz multi context for logback (#807)
Browse files Browse the repository at this point in the history
Co-authored-by: yuanyuan <fengjun.zfj@antgroup.com>

(cherry picked from commit 52910e5)
  • Loading branch information
yuanyuancin authored and lvjing2 committed Jan 11, 2024
1 parent eac9a5d commit 783153f
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.ark.common.adapter;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.selector.ContextSelector;
import ch.qos.logback.classic.spi.LoggerContextListener;
import com.alipay.sofa.ark.common.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ArkLogbackContextSelector implements ContextSelector {

private static final Map<ClassLoader, LoggerContext> CLASS_LOADER_LOGGER_CONTEXT = new HashMap<>();

private static final String BIZ_CLASS_LOADER = "com.alipay.sofa.ark.container.service.classloader.BizClassLoader";
private static final String CONTAINER_CLASS_LOADER = "com.alipay.sofa.ark.bootstrap.ContainerClassLoader";

private LoggerContext defaultLoggerContext;

public ArkLogbackContextSelector(LoggerContext loggerContext) {
this.defaultLoggerContext = loggerContext;
}

@Override
public LoggerContext getLoggerContext() {
ClassLoader classLoader = this.findClassLoader();
if (classLoader == null) {
return defaultLoggerContext;
}
return getContext(classLoader);
}

private ClassLoader findClassLoader() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader != null && CONTAINER_CLASS_LOADER.equals(classLoader.getClass().getName())) {
return null;
}
if (classLoader != null && BIZ_CLASS_LOADER.equals(classLoader.getClass().getName())) {
return classLoader;
}

Class<?>[] context = new SecurityManager() {
@Override
public Class<?>[] getClassContext() {
return super.getClassContext();
}
}.getClassContext();
if (context == null || context.length == 0) {
return null;
}
for (Class<?> cls : context) {
if (cls.getClassLoader() != null
&& BIZ_CLASS_LOADER.equals(cls.getClassLoader().getClass().getName())) {
return cls.getClassLoader();
}
}

return null;
}

private LoggerContext getContext(ClassLoader cls) {
LoggerContext loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(cls);
if (null == loggerContext) {
synchronized (ArkLogbackContextSelector.class) {
loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(cls);
if (null == loggerContext) {
loggerContext = new LoggerContext();
loggerContext.setName(Integer.toHexString(System.identityHashCode(cls)));
loggerContext.addListener(new ArkLoggerContextListener(this));
CLASS_LOADER_LOGGER_CONTEXT.put(cls, loggerContext);
}
}
}
return loggerContext;
}

@Override
public LoggerContext getLoggerContext(String name) {
if (StringUtils.isEmpty(name)) {
return defaultLoggerContext;
}
for (ClassLoader classLoader : CLASS_LOADER_LOGGER_CONTEXT.keySet()) {
LoggerContext loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(classLoader);
if (name.equals(loggerContext.getName())) {
return loggerContext;
}
}
return defaultLoggerContext;
}

@Override
public LoggerContext getDefaultLoggerContext() {
return defaultLoggerContext;
}

@Override
public LoggerContext detachLoggerContext(String loggerContextName) {
if (StringUtils.isEmpty(loggerContextName)) {
return null;
}
for (ClassLoader classLoader : CLASS_LOADER_LOGGER_CONTEXT.keySet()) {
LoggerContext loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(classLoader);
if (loggerContextName.equals(loggerContext.getName())) {
return removeContext(classLoader);
}
}
return null;
}

public LoggerContext removeContext(ClassLoader cls) {
if (cls == null) {
return null;
}
return CLASS_LOADER_LOGGER_CONTEXT.remove(cls);
}

@Override
public List<String> getContextNames() {
return CLASS_LOADER_LOGGER_CONTEXT.values().stream().map(LoggerContext::getName).collect(Collectors.toList());
}

public static class ArkLoggerContextListener implements LoggerContextListener {

private ArkLogbackContextSelector contextSelector;

public ArkLoggerContextListener(ArkLogbackContextSelector contextSelector) {
this.contextSelector = contextSelector;
}

@Override
public boolean isResetResistant() {
return true;
}

@Override
public void onStart(LoggerContext context) {

}

@Override
public void onReset(LoggerContext context) {

}

@Override
public void onStop(LoggerContext context) {
contextSelector.detachLoggerContext(context.getName());
}

@Override
public void onLevelChange(Logger logger, Level level) {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.ark.common.adapter;

import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.selector.ContextSelector;
import ch.qos.logback.classic.util.ContextSelectorStaticBinder;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
* @author: yuanyuan
* @date: 2023/12/12 5:02 下午
*/
public class ArkLogbackContextSelectorTest {

@Test
public void testContextSelector() throws NoSuchMethodException, InvocationTargetException,
IllegalAccessException {
System.setProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR,
"com.alipay.sofa.ark.common.adapter.ArkLogbackContextSelector");
Logger logger = LoggerFactory.getLogger(ArkLogbackContextSelectorTest.class);
System.clearProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);

ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
LoggerContext loggerContext = (LoggerContext) iLoggerFactory;
ContextSelectorStaticBinder selectorStaticBinder = ContextSelectorStaticBinder
.getSingleton();
ContextSelector contextSelector = selectorStaticBinder.getContextSelector();
Assert.assertTrue(contextSelector instanceof ArkLogbackContextSelector);
Assert.assertEquals(loggerContext, contextSelector.getDefaultLoggerContext());

URL url = ArkLogbackContextSelectorTest.class.getClassLoader().getResource("");
URLClassLoader loader = new URLClassLoader(new URL[] { url }, null);
String contextName = Integer.toHexString(System.identityHashCode(loader));

Method getContext = ArkLogbackContextSelector.class.getDeclaredMethod("getContext",
ClassLoader.class);
getContext.setAccessible(true);
Object invoke = getContext.invoke(contextSelector, loader);
Assert.assertNotNull(invoke);
Assert.assertEquals(invoke, contextSelector.getLoggerContext(contextName));
Assert.assertTrue(contextSelector.getContextNames().contains(contextName));
contextSelector.getLoggerContext(contextName).stop();
Assert.assertTrue(contextSelector.getContextNames().isEmpty());

}
}

0 comments on commit 783153f

Please sign in to comment.