Logback - 你能从 env 变量中定义 appender 名称和类吗?

Posted

技术标签:

【中文标题】Logback - 你能从 env 变量中定义 appender 名称和类吗?【英文标题】:Logback - can you define appender name and class from env variables? 【发布时间】:2020-06-01 14:43:45 【问题描述】:

我想要动态的 logback appender 属性,可以很容易地添加到 logback 配置文件中,但是尝试设置 appender 的类和名称(它们位于 xml 属性中,而不是 appender 元素下的元素中)。

这是我的 application.yml(硬编码示例的值,但在实际用例中,在部署到 K8s 集群期间,这些值将作为 env 变量从 Helm 传递):

log:
  config:
    appender:
      name: CONSOLE
      class: ch.qos.logback.core.ConsoleAppender

这是我尝试访问 logback-spring.xml 中的那些(spring boot version - 2.2.4.RELEASE

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- use Spring default values like patterns -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!-- declaration of ENV properties:   -->
    <springProperty name="LOG_CONFIG_APPENDER_NAME" source="log.config.appender.name"/>
    <springProperty name="LOG_CONFIG_APPENDER_CLASS" source="log.config.appender.class"/>

    <appender name="$LOG_CONFIG_APPENDER_NAME" class="$LOG_CONFIG_APPENDER_CLASS">
        <encoder>
            <pattern>$CONSOLE_LOG_PATTERN</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="$LOG_CONFIG_APPENDER_NAME"/>
    </root>
</configuration>

这会导致以下异常:

ERROR in ch.qos.logback.core.joran.action.AppenderAction - Could not create an Appender of type [$LOG_CONFIG_APPENDER_CLASS]. ch.qos.logback.core.util.DynamicClassLoadingException: Failed to instantiate type $LOG_CONFIG_APPENDER_CLASS

所以我的问题是:是否可以动态定义附加程序名称和类?

【问题讨论】:

这样做的动机是什么? @cassiomolin 对于本地环境,我想要简单的控制台日志附加程序。对于其他任何事情,我将使用 LayoutWrappingEncoderJacksonJsonFormatter 为我们的 ELK 堆栈添加自定义字段。这反过来又允许我在 Kibana 中搜索特定的错误消息。但是,在本地处理 JSON 日志消息很痛苦。 现在再考虑一下,我可以忍受“丑陋”的条件逻辑,这将允许我拥有不同的附加程序...... 看看我的answer。 Here 说如果你想要这样的功能,你应该迁移到 Groovy 配置文件 【参考方案1】:

对于本地环境,我想要简单的控制台日志附加程序。对于其他任何事情,我将使用 LayoutWrappingEncoderJacksonJsonFormatter 为我们的 ELK 堆栈添加自定义字段。

所以你想在logback-spring.xml 中使用&lt;springProfile&gt; 标签:

&lt;springProfile&gt; 标签允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。 &lt;configuration&gt; 元素中的任何位置都支持配置文件部分。使用name 属性指定哪个配置文件接受配置。 &lt;springProfile&gt; 标签可以包含一个简单的配置文件名称(例如staging)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如production &amp; (eu-central | eu-west)。查看reference guide了解更多详情。

请看下面的例子:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <springProfile name="staging">
        <!-- configuration to be enabled when the "staging" profile is active -->
    </springProfile>

    <springProfile name="dev | staging">
        <!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
    </springProfile>

    <springProfile name="!production">
        <!-- configuration to be enabled when the "production" profile is not active -->
    </springProfile>

</configuration>

【讨论】:

【参考方案2】:

我最终在 Groovy 中完成了它,因为将配置映射到 Spring 配置文件对我和我的团队不起作用,因为我们正在为本地和共享开发环境使用开发配置文件(托管在 K8s 集群上)。

我并不为此感到自豪,但以一个简单的 if 结束了它:

def loggingType = System.getenv('LOGGING_TYPE')
def loggingLevelEnvVar = System.getenv('CUSTOM_LOGGING_LEVEL')
def loggingLevel = loggingLevelEnvVar == null ? INFO : Level.valueOf(loggingLevelEnvVar)
// please do not use a coloured pattern for consoles that will be scrapped
def loggingPattern = System.getenv('LOGGING_LEVEL_PATTERN')
...
if ('JSON'.equalsIgnoreCase(loggingType)) 
    appender('CONSOLE', ConsoleAppender) 
        encoder(LayoutWrappingEncoder) 
            layout(JsonLayout) 
                timestampFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
                timestampFormatTimezoneId = 'Etc/UTC'
                appendLineSeparator = true
                jsonFormatter(JacksonJsonFormatter) 
                    prettyPrint = false
                
            
        
    
 else 
    appender('CONSOLE', ConsoleAppender) 
        encoder(PatternLayoutEncoder) 
            pattern = loggingPattern
        
    

...

【讨论】:

嗯...我希望您考虑过适合您本地环境的不同配置文件。而且,如果您可以为日志记录类型设置环境变量,您还可以使用环境变量设置活动的 Spring 配置文件:)

以上是关于Logback - 你能从 env 变量中定义 appender 名称和类吗?的主要内容,如果未能解决你的问题,请参考以下文章

Python:你能从windows中读取鼠标悬停文本吗?

1298. 你能从盒子里获得的最大糖果数

discord.py 你能从 discord 标签中获取用户对象吗?

你能从 Django 的模板中检查互联网协议吗?

你能从 Heroku dynos/workers 中获得多少性能?

你能从 sklearn 网格搜索 (GridSearchCV) 中获得所有估计器吗?