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

文章目录

  • 背景
  • 使用SystemPropertyArbiter和JVM启动参数实现
  • 使用Property Substitution和JVM启动参数实现
  • 使用自定义的LookUp Plugin来实现
  • 使用ScriptAppenderSelector实现
  • 备注

背景

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

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

使用SystemPropertyArbiter和JVM启动参数实现

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


<Configuration status="debug">

    <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也可以实现此目的


<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">

            <AppenderRef ref="${Console}"/>
        Root>
    Loggers>
Configuration>

关于上述Properties的写法,默认值是test
Log4j2的使用(三)不同的环境使用不同的配置_第1张图片
通过提前声明好两个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配置


<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">
            
            <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

这一部分打算在使用Log4j2以ECS规范的格式将日志发送到Logstash中中介绍,这里不再赘述。

备注

  • StackOverflow上关于如何实现上述目标的回答: How do I conditionally add log4j2 appender depending on java system property?
  • 源码仓库

你可能感兴趣的:(Jakarta,EE,elk,logback,slf4j,log4j2,java)