springboot中apollo支持logback配置的热加载,老的spring项目中该如何解决
完整实现:
package com.bugcodes;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.net.URL;
/**
* @author bugcoder
* @date 2021/3/17
*/
4j
public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
private static boolean started = false;
private static String APOLLO_SETTING_NAME = "application.name";
private static String LOGBACK_SETTING_NAME = "appname";
public void start() {
if (started) {
return;
}
//从apollo中获取所有配置信息
Config config = ConfigService.getAppConfig();
String apolloValue = config.getProperty(APOLLO_SETTING_NAME, "这里设置默认值");
Context context = getContext();
config.addChangeListener(new ConfigChangeListener() {
public void onChange(ConfigChangeEvent configChangeEvent) {
for (String key : configChangeEvent.changedKeys()) {
log.warn("改变的key is {}",key);
ConfigChange change = configChangeEvent.getChange(key);
reloadDefaultConfiguration(change);
}
}
});
context.putProperty(LOGBACK_SETTING_NAME, apolloValue);
started = true;
}
public void stop() {
}
public boolean isStarted() {
return started;
}
public boolean isResetResistant() {
return true;
}
public void onStart(LoggerContext context) {
}
public void onReset(LoggerContext context) {
}
public void onStop(LoggerContext context) {
}
public void onLevelChange(Logger logger, Level level) {
}
private void reloadDefaultConfiguration(ConfigChange change) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ContextInitializer ci = new ContextInitializer(loggerContext);
URL url = ci.findURLOfDefaultConfigurationFile(true);
loggerContext.reset();
loggerContext.putProperty(LOGBACK_SETTING_NAME,change.getNewValue());
try {
ci.configureByResource(url);
} catch (JoranException e) {
e.printStackTrace();
}
}
}
logback.xml配置
<configuration>
<contextListener class="com.bugcodes.LoggerStartupListener" />
<property name="app_name" value="${appname}"/>
<property name="logPattern" value="[app_name=${appname}][timestamp=%d{yyyy-MM-dd HH:mm:ss.SSS}][level=%p][msg=%m] %n"/>
</configuration>
logback.xml--->application.properties--->logback-spring.xml--->apollo
自定义监听器
package com.bugcodes;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.net.URL;
/**
* @author bugcoder
* @date 2021/3/17
*/
4j
public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
private static boolean started = false;
private static String APOLLO_SETTING_NAME = "application.name";
private static String LOGBACK_SETTING_NAME = "appname";
public void start() {
if (started) {
return;
}
//从apollo中获取所有配置信息
Config config = ConfigService.getAppConfig();
String apolloValue = config.getProperty(APOLLO_SETTING_NAME, "这里设置默认值");
Context context = getContext();
context.putProperty(LOGBACK_SETTING_NAME, apolloValue);
started = true;
}
public void stop() {
}
public boolean isStarted() {
return started;
}
public boolean isResetResistant() {
return true;
}
public void onStart(LoggerContext context) {
}
public void onReset(LoggerContext context) {
}
public void onStop(LoggerContext context) {
}
public void onLevelChange(Logger logger, Level level) {
}
}
在logback.xml中添加
<contextListener class="com.bugcodes.LoggerStartupListener" />
在logback.xml中使用
<property name="logPattern" value="[app_name=${appname}][timestamp=%d{yyyy-MM-dd HH:mm:ss.SSS}][level=%p][msg=%m] %n"/>
启动项目可以在日志中看到app_name的属性值就是apollo中配置的值
LoggerContext loggerContext =(LoggerContext)LoggerFactory.getILoggerFactory();
ContextInitializer ci = new ContextInitializer(loggerContext);
URL url = ci.findURLOfDefaultConfigurationFile(true);
loggerContext.reset();
try {
ci.configureByResource(url);
} catch (JoranException e) {
e.printStackTrace();
}
Config config = ConfigService.getAppConfig();
config.addChangeListener(new ConfigChangeListener() {
public void onChange(ConfigChangeEvent configChangeEvent) {
for (String key : configChangeEvent.changedKeys()) {
log.warn("改变的key is {}",key);
}
}
});
在检测到apollo有更新值取重新加载logback,并且重新给logback中相关的属性赋于apollo中的新值
config.addChangeListener(new ConfigChangeListener() {
public void onChange(ConfigChangeEvent configChangeEvent) {
for (String key : configChangeEvent.changedKeys()) {
log.warn("改变的key is {}",key);
ConfigChange change = configChangeEvent.getChange(key);
reloadDefaultConfiguration(change);
}
}
});
参考链接:
Spring(SpringMVC)项目能否支持日志配置: https://github.com/ctripcorp/apollo/issues/2482
增加EnvironmentPostProcessor处理,将Apollo配置加载提到初始化日志系统之前 : https://github.com/ctripcorp/apollo/pull/1614
logback的contextListener日志系统初始化: https://blog.csdn.net/wzygis/article/details/51861743
动态修改Logback日志级别的两种实现: http://www.hsuns.com/2019/03/20/%E5%8A%A8%E6%80%81%E4%BF%AE%E6%94%B9Logback%E6%97%A5%E5%BF%97%E7%BA%A7%E5%88%AB%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%AE%9E%E7%8E%B0/
logback源码解析之LoggerContext:https://blog.csdn.net/yinlongfei_love/article/details/80807388
How do I programmatically tell Logback to Reload Configuration : https://stackoverflow.com/questions/9320133/how-do-i-programmatically-tell-logback-to-reload-configuration