apollo配置中心实现logback.xml配置管理与热更新

因为apollo配置中心不支持.xml配置的写入,文档说明了当logback.xml文件放在apollo配置中心时,如何通过读取apollo缓存在本地的文件,实现日志配置的写入与热更新,实现通过apollo对logback.xml配置进行管理。

分步指南

1.首先把本地的logback.xml配置文件放到apollo配置中心,新建logback.xml配置文件的namespace,写入内容。

在这里插入图片描述
2.修改项目中的apollo配置,增加参数

apollo.bootstrap.namespaces=application,logback.xml

这个参数是指定项目用了哪些namespace的,后面的值要加上所有项目中用到的namespace,因为不加这个参数时apollo只会默认加载.properties的namespace,因为要用到logback.xml所以要加这个参数

3.apollo会在本地缓存配置文件,
默认的目录对于Mac/Linux,文件位置为

/opt/data/+appId+/config-cache/

对于Windows,文件位置为

C:/opt/data/+appId+/config-cache/

apollo在本地缓存的配置文件名称格式为

{appId}+{cluster}+{namespace}.properties

因此logback.xml这个namespace在本地的名称格式是{appId}+{cluster}+logback.xml.properties
首先通过代码去获取当前系统名称,根据apollo缓存文件默认规则去获取到logback.xml缓存在本地的配置文件
新建LogBackConfigLoader类,增加getPathName方法

private String getPathName(){
    String pathName;
    String system = System.getProperty("os.name");
    if(system.toLowerCase().startsWith("win")){
        pathName = "C:/opt/data/"+appId+"/config-cache/";
    }else {
        //除了win其他系统路径一样
        pathName = "/opt/data/"+appId+"/config-cache/";
    }
 
    String cluster = "";
    List<String> inputArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
    for (String in : inputArgs){
        if(in.contains("Dapollo") && in.contains("cluster")){
            String[] clusters = in.split("=");
            cluster = clusters[1].replaceAll(" ","");
        }
    }
    pathName += appId+"+"+cluster+"+"+"logback.xml.properties";
    return pathName;
}

4.因为apollo缓存在本地的xml配置文件是经过转义与添加了一些内容的,所以要通过代码去反转义与去掉多余的内容,并生成正确的logback.xml文件,代码中增加方法

private String prop2Xml(String path) throws Exception{
    StringBuffer fileContent = new StringBuffer();
    File filename = new File(path);
    InputStreamReader reader = new InputStreamReader(new FileInputStream(filename));
    BufferedReader br = new BufferedReader(reader);
    int f = 0;
    String line = "";
    line = br.readLine();
    while(line != null) {
        if(f>1){
            //前两行注释不要
            fileContent.append(line);
        }
        line = br.readLine();
        f++;
    }
 
    //去掉content=
    fileContent.replace(0,8,"");
    //java反转义
    String outContent = StringEscapeUtils.unescapeJava(fileContent.toString());
 
    //生成xml文件
    String outPath = path.replaceAll(".properties","");
    File file=new File(outPath);
    if(!file.exists()){
        file.createNewFile();
    }else {
        //先删除再重新创建不然会报错
        file.delete();
        file.createNewFile();
    }
    FileOutputStream out=new FileOutputStream(file,true);
    out.write(outContent.getBytes("utf-8"));
    out.close();
    return outPath;
}

5.已经获取到正确的logback.xml文件后,需要通过代码让logback重新加载配置,增加方法load

private void load (String externalConfigFileLocation) throws IOException, JoranException{
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
 
    File externalConfigFile = new File(externalConfigFileLocation);
    if(!externalConfigFile.exists()){
        throw new IOException("Logback配置文件不存在");
    }else{
        if(!externalConfigFile.isFile()){
            throw new IOException("Logback配置文件不正确");
        }else{
            if(!externalConfigFile.canRead()){
                throw new IOException("Logback配置文件不能被读取");
            }else{
                JoranConfigurator configurator = new JoranConfigurator();
                configurator.setContext(lc);
                lc.reset();
                configurator.doConfigure(externalConfigFileLocation);
                StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
            }
        }
    }
}

6.在spring启动时,apollo已经启动完成了,因此可以在spring启动时对logback配置进行重新加载,调用上述方法,代码如下

@PostConstruct
private void  initLog(){
    try {
        String pathName = getPathName();
        String xmlPath = prop2Xml(pathName);
        load(xmlPath);
    }catch (Exception e){
        logger.warn("获取apollo日志logback.xml配置失败,logback使用默认配置!  原因:"+e.getMessage());
    }
}

7.要实现logback配置的热更新,就要实时监听apollo中logback.xml的变化,因此在spring中使用@ApolloConfigChangeListener注解来实现,当配置内容发生变化时再执行一遍上述操作。

@ApolloConfigChangeListener("logback.xml")
private void anotherOnChange(ConfigChangeEvent changeEvent) {
    //当logback.xml文件改变的时候动态更新
    try {
        String pathName = getPathName();
        String xmlPath = prop2Xml(pathName);
        load(xmlPath);
    }catch (Exception e){
        logger.warn("apollo日志logback.xml配置热更新失败!  原因:"+e.getMessage());
    }
}

8.完整的LogBackConfigLoader的代码如下。

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import org.apache.commons.lang.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.util.List;
 
@Component
public class LogBackConfigLoader {
    /**
     * logger:日志对象.
     * @since JDK 1.7
     */
    private static Logger logger = LoggerFactory.getLogger(LogBackConfigLoader.class);
 
    @Value("${app.id}")
    private String appId;
 
    private void load (String externalConfigFileLocation) throws IOException, JoranException{
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
 
    File externalConfigFile = new File(externalConfigFileLocation);
    if(!externalConfigFile.exists()){
        throw new IOException("Logback配置文件不存在");
    }else{
        if(!externalConfigFile.isFile()){
            throw new IOException("Logback配置文件不正确");
        }else{
            if(!externalConfigFile.canRead()){
                throw new IOException("Logback配置文件不能被读取");
            }else{
                JoranConfigurator configurator = new JoranConfigurator();
                configurator.setContext(lc);
                lc.reset();
                configurator.doConfigure(externalConfigFileLocation);
                StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
            }
        }
    }
}
 
    @PostConstruct
    private void  initLog(){
        try {
            String pathName = getPathName();
            String xmlPath = prop2Xml(pathName);
            load(xmlPath);
        }catch (Exception e){
            logger.warn("获取apollo日志logback.xml配置失败,logback使用默认配置!  原因:"+e.getMessage());
        }
    }
 
    @ApolloConfigChangeListener("logback.xml")
    private void anotherOnChange(ConfigChangeEvent changeEvent) {
        //当logback.xml文件改变的时候动态更新
        try {
            String pathName = getPathName();
            String xmlPath = prop2Xml(pathName);
            load(xmlPath);
        }catch (Exception e){
            logger.warn("apollo日志logback.xml配置热更新失败!  原因:"+e.getMessage());
        }
    }
 
    private String getPathName(){
        String pathName;
        String system = System.getProperty("os.name");
        if(system.toLowerCase().startsWith("win")){
            pathName = "C:/opt/data/"+appId+"/config-cache/";
        }else {
            //除了win其他系统路径一样
            pathName = "/opt/data/"+appId+"/config-cache/";
        }
 
        String cluster = "";
        List<String> inputArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
        for (String in : inputArgs){
            if(in.contains("Dapollo") && in.contains("cluster")){
                String[] clusters = in.split("=");
                cluster = clusters[1].replaceAll(" ","");
            }
        }
        pathName += appId+"+"+cluster+"+"+"logback.xml.properties";
        return pathName;
    }
 
    private String prop2Xml(String path) throws Exception{
        StringBuffer fileContent = new StringBuffer();
        File filename = new File(path);
        InputStreamReader reader = new InputStreamReader(new FileInputStream(filename));
        BufferedReader br = new BufferedReader(reader);
        int f = 0;
        String line = "";
        line = br.readLine();
        while(line != null) {
            if(f>1){
                //前两行注释不要
                fileContent.append(line);
            }
            line = br.readLine();
            f++;
        }
 
        //去掉content=
        fileContent.replace(0,8,"");
        //java反转义
        String outContent = StringEscapeUtils.unescapeJava(fileContent.toString());
 
        //生成xml文件
        String outPath = path.replaceAll(".properties","");
        File file=new File(outPath);
        if(!file.exists()){
            file.createNewFile();
        }else {
            //先删除再重新创建不然会报错
            file.delete();
            file.createNewFile();
        }
        FileOutputStream out=new FileOutputStream(file,true);
        out.write(outContent.getBytes("utf-8"));
        out.close();
        return outPath;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值