Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring(SpringMVC)项目能否支持日志配置 #2482

Closed
LiuQiangWangWanHong opened this issue Jul 26, 2019 · 10 comments
Closed

Spring(SpringMVC)项目能否支持日志配置 #2482

LiuQiangWangWanHong opened this issue Jul 26, 2019 · 10 comments
Labels
area/client apollo-client feature request Categorizes issue as related to a new feature.

Comments

@LiuQiangWangWanHong
Copy link

老的SpringMVC项目 想搞到apollo 配置中心,其他的配置都能弄上去 但是 如何让logback.xml文件获取到apollo配置中心的配置,
现在加载顺序优先级是logback.xml>application.properties>logback-spring.xml 但是logback-spring.xml只支持SpringBoot

@lawrencewu
Copy link

遇到同样的问题, #1614 #1211 等针对的是springboot项目下的配置

@nobodyiam
Copy link
Member

spring boot下logback-spring.xml通过stringProperty是可以解决的,spring环境下由于logback没有和spring集成,所以可能需要自己开发一下集成的代码

@LiuQiangWangWanHong
Copy link
Author

在logback.xml 配置这个listener

实现这个

public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {

private static boolean started = false;
private static String LOG_PATH = "logback.loghome";

@Override
public void start() {
    System.out.println("LoggerStartupListener-start");
    if (started) {
        return;
    }
    //从apollo中获取所有配置信息
    Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
    String logHome = config.getProperty(LOG_PATH, "/opt/spring/logs");
    Context context = getContext();
    context.putProperty("LOG_HOME", logHome);

    started = true;
}

@Override
public void stop() {
}

@Override
public boolean isStarted() {
    return started;
}

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

@Override
public void onStart(LoggerContext context) {

}

@Override
public void onReset(LoggerContext context) {

}

@Override
public void onStop(LoggerContext context) {

}

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

}

1 similar comment
@LiuQiangWangWanHong
Copy link
Author

在logback.xml 配置这个listener

实现这个

public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {

private static boolean started = false;
private static String LOG_PATH = "logback.loghome";

@Override
public void start() {
    System.out.println("LoggerStartupListener-start");
    if (started) {
        return;
    }
    //从apollo中获取所有配置信息
    Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
    String logHome = config.getProperty(LOG_PATH, "/opt/spring/logs");
    Context context = getContext();
    context.putProperty("LOG_HOME", logHome);

    started = true;
}

@Override
public void stop() {
}

@Override
public boolean isStarted() {
    return started;
}

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

@Override
public void onStart(LoggerContext context) {

}

@Override
public void onReset(LoggerContext context) {

}

@Override
public void onStop(LoggerContext context) {

}

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

}

@LiuQiangWangWanHong
Copy link
Author

这样就可以在启动时获取apollo配置进行初始化 但是 不确定 项目运行时apollo配置改变是否会随之改变,感觉不会.....

@nobodyiam nobodyiam added the tips label Aug 4, 2019
@lawrencewu
Copy link

lawrencewu commented Aug 6, 2019

这样子不具有灵活性,类似LOG_PATH = "logback.loghome"的属性需要提前定义, 所以我只好侵入了logback的代码,

private String lookupKey(String key) {
        String value = propertyContainer0.getProperty(key);
        if (value != null)
            return value;

        if (propertyContainer1 != null) {
            value = propertyContainer1.getProperty(key);
            if (value != null)
                return value;
        }

        value = OptionHelper.getSystemProperty(key, null);
        if (value != null)
            return value;

        value = OptionHelper.getEnv(key);
        if (value != null) {
            return value;
        }

        Config config = ConfigService.getConfig("application");
        value = config.getProperty(key, null);
        if (value != null){
            return value;
        }

        return null;
    }

这个的优势是可以不用提前定义logback.xml里的变量,任何变量都可以从配置中心拿, 但是需要侵入logback源代码.

@ljzforever
Copy link

非常感谢

@bugcodes
Copy link

Spring/SpringMVC项目apollo支持日志配置

Spring/SpringMVC项目apollo支持日志配置
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
 */
@Component
@Slf4j
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";

	@Override
	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() {
			@Override
			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;
	}

	@Override
	public void stop() {
	}

	@Override
	public boolean isStarted() {
		return started;
	}

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

	@Override
	public void onStart(LoggerContext context) {

	}

	@Override
	public void onReset(LoggerContext context) {

	}

	@Override
	public void onStop(LoggerContext context) {

	}

	@Override
	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配置

<?xml version="1.0" encoding="UTF-8"?>
<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>

1.启动顺序的问题:

logback.xml--->application.properties--->logback-spring.xml--->apollo

####2.项目运行中动态加载或者重新加载logback的问题


1.如何在logback启动的时候加载apollo的配置

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
 */
@Component
@Slf4j
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";

	@Override
	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;
	}

	@Override
	public void stop() {
	}

	@Override
	public boolean isStarted() {
		return started;
	}

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

	@Override
	public void onStart(LoggerContext context) {

	}

	@Override
	public void onReset(LoggerContext context) {

	}

	@Override
	public void onStop(LoggerContext context) {

	}

	@Override
	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中配置的值

####2:如何解决动态加载或者重新加载logback

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();
}

loggerContext.reset()之后就可以重新给logback中的属性赋值了,LoggerContext与Context的关系,Context是LoggerContext的父类。

还有一种方式:在logback中使用scanPeriod标签 (未测试)

3.如何检测apollo是不是有更新

Config config = ConfigService.getAppConfig();
config.addChangeListener(new ConfigChangeListener() {
			@Override
			public void onChange(ConfigChangeEvent configChangeEvent) {
				for (String key : configChangeEvent.changedKeys()) {
					log.warn("改变的key is {}",key);
				}
			}
});

在检测到apollo有更新值取重新加载logback,并且重新给logback中相关的属性赋于apollo中的新值

config.addChangeListener(new ConfigChangeListener() {
			@Override
			public void onChange(ConfigChangeEvent configChangeEvent) {
				for (String key : configChangeEvent.changedKeys()) {
					log.warn("改变的key is {}",key);
					ConfigChange change = configChangeEvent.getChange(key);
					reloadDefaultConfiguration(change);
				}
			}
});

启动项目测试,spring项目启动后去apollo修改个值测试下,发现log已更新

@nobodyiam
Copy link
Member

@bugcodes this looks great, would you please submit the demo to apollo-use-cases?

@bugcodes
Copy link

@bugcodes this looks great, would you please submit the demo to apollo-use-cases?

@nobodyiam 已提交pull requests

@Anilople Anilople added area/client apollo-client feature request Categorizes issue as related to a new feature. and removed tips labels Oct 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/client apollo-client feature request Categorizes issue as related to a new feature.
Projects
None yet
Development

No branches or pull requests

6 participants