Log4j2的使用(三)不同的环境使用不同的配置

背景

假设现在需要在不同的环境中使用不同的日志格式,不使用Spring Profile

  • 在本地开发环境和Test环境,由于需要很方便的排查错误,所以使用PatternLayout并且控制台输出
  • 在生产环境,由于接入ELK,查看日志都是在Kibana中查看,所以日志的格式就要对ELK解析友好,所以使用JsonTemplateLayout
  • 还有一点要考虑的是,是新项目还是老项目?是否需要运维配合?整个流程的部署周期是多长?

使用SystemPropertyArbiter和JVM启动参数实现

官方文档里给的解决方案是使用Arbiter

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
<!-- 官方文档   https://logging.apache.org/log4j/2.x/manual/configuration.html#Arbiters-->
    <Appenders>
        <SystemPropertyArbiter propertyName="env" propertyValue="test">
            <Console name="Out" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} [%t] %level %logger - %msg%n"/>
            </Console>
        </SystemPropertyArbiter>
        <SystemPropertyArbiter propertyName="env" propertyValue="prod">
            <Console name="Out" target="SYSTEM_OUT">
                <JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
            </Console>
        </SystemPropertyArbiter>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Out"/>
        </Root>
    </Loggers>
</Configuration>

启动时,设置SystemProperty,例如

java -jar Log4jApplication.jar -Denv=prod

使用Property Substitution和JVM启动参数实现

通过官方提供的Property Substitution也可以实现此目的

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
    <Properties>
        <Property name="Console">Console_${sys:log4jLayOut:-test}</Property>
    </Properties>
    <Appenders>
        <Console name="Console_prod" target="SYSTEM_OUT">
            <JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
        </Console>
        <Console name="Console_test" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} [%t] %level %logger - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
<!-- ${name}语法,官方文档 https://logging.apache.org/log4j/2.x/manual/configuration.html#property-substitution-->
            <AppenderRef ref="${Console}"/>
        </Root>
    </Loggers>
</Configuration>

关于上述Properties的写法,默认值是test
在这里插入图片描述
通过提前声明好两个Appender,根据每个Appender的名字匹配。

启动时,设置SystemProperty,例如

java -jar Log4jApplication.jar -Dlog4jLayOut=prod

使用自定义的LookUp Plugin来实现

上面两种方式适合新项目适和部署周期较短的项目。

由于上面两种方式需要在JVM启动的时候添加额外的参数,那这一部分工作是由运维来负责,虽然只是添加一个参数,假设服务器数量较多,那么代码需要等到JVM参数添加到所有服务器之后才可以,也就相当于部署了两次。

在这种背景下,我在想,能不能把设置查找SystemProperty这一部分工作也在代码里完成呢?

好在Log4j2提供了Plugins机制,允许我们可以自己拓展

@Plugin(
    name="sys",
    category = StrLookup.CATEGORY
)
public class CustomerSystemPropertiesLookUp extends SystemPropertiesLookup {
    
    private static final String LOOK_UP_KEY = "log4jLayOut";
    
    @Override
    public String lookup(final LogEvent event, String key) {
        if (LOOK_UP_KEY.equals(key)) {
           return getValueBasedOnEnv();
        }else {
            return super.lookup(key);
        }
    }
    
    private String getValueBasedOnEnv() {
        String isTest = System.getProperty("isTest");
        return "true".equals(isTest) ? "test" : "prod";
    }
}

这里重写了SystemPropertiesLookup,当查找特定key时,在我们自定义的方法里查找;其他key时则使用父类的查找方法。

看到代码可能有人会有疑问,你的getValueBasedOnEnv方法还不是在SystemProperties拿的吗?不还是一样要添加JVM参数?

这是因为,我们当前工程里的System.properties文件中已经包含里可以区分测试环境和生产环境的属性了,所以直接使用,这里这是为了演示好理解。

Log4j2.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
    <Properties>
        <Property name="Console">Console_${sys:log4jLayOut:-test}</Property>
    </Properties>
    <Appenders>
        <Console name="Console_prod" target="SYSTEM_OUT">
            <JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
        </Console>
        <Console name="Console_test" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} [%t] %level %logger - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <!-- ${name}语法,官方文档 https://logging.apache.org/log4j/2.x/manual/configuration.html#property-substitution-->
            <AppenderRef ref="${Console}"/>
        </Root>
    </Loggers>
</Configuration>

使用ScriptAppenderSelector实现

ScriptAppenderSelector文档

When the configuration is built, the ScriptAppenderSelector appender calls a Script to compute an appender name. Log4j then creates one of the appender named listed under AppenderSet using the name of the ScriptAppenderSelector

Log4j only builds the one selected appender from the configuration tree, and ignores other AppenderSet child nodes

  1. 这里使用groovy,引入groovy-jsr223依赖
  2. log42.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="debug">
    <Appenders>
        <ScriptAppenderSelector name="SelectIt">
            <Script language="groovy" name="selectScripts"><![CDATA[
                    var result;
                    if (System.getProperty("os.name").equalsIgnoreCase("Windows")) {
                        result = "Null";
                    } else {
                        result = "Console";
                    }
                    return result;
            ]]>
            </Script>
            <AppenderSet>
                <Console name="Console" target="SYSTEM_OUT">
                    <PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z'} [%t] %level %logger - %msg%n"/>
                </Console>
                <Null name="Null" />
            </AppenderSet>
        </ScriptAppenderSelector>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="SelectIt"/>
        </Root>
    </Loggers>
    </Configuration>
    
  3. 设置JVM启动参数,-Dlog4j2.Script.enableLanguages=groovy

备注

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值