[英]Logback - can you define appender name and class from env variables?
I want to have dynamic logback appender properties which can easily be added to the logback config file, however trying to set the class and name of the appender(which are in a xml attributes instead of elements under the appender element).我想要动态 logback appender 属性,可以轻松地将其添加到 logback 配置文件中,但是尝试设置 appender 的类和名称(它们位于 xml 属性中,而不是 appender 元素下的元素中)。
So here is my application.yml(hardcoded the values for the example, but in the real world use case those will be passed as env variables from Helm, during the deploy to K8s cluster):所以这是我的 application.yml(对示例的值进行硬编码,但在实际用例中,在部署到 K8s 集群期间,这些值将作为来自 Helm 的环境变量传递):
log:
config:
appender:
name: CONSOLE
class: ch.qos.logback.core.ConsoleAppender
And here is how I try to access those in logback-spring.xml(spring boot version - 2.2.4.RELEASE这是我尝试访问 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>
This leads to the following exception:这会导致以下异常:
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}
So my question is: is it possible to define the appender name and class dynamically ?所以我的问题是:是否可以动态定义附加程序名称和类?
For the local env I want to have simple console log appender.
对于本地环境,我想要简单的控制台日志附加程序。 For anything else I will use the
LayoutWrappingEncoder
andJacksonJsonFormatter
to add custom fields for our ELK stack.对于其他任何事情,我将使用
LayoutWrappingEncoder
和JacksonJsonFormatter
为我们的 ELK 堆栈添加自定义字段。
So you want to use the <springProfile>
tag in your logback-spring.xml
:所以你想在你的
logback-spring.xml
使用<springProfile>
标签:
The
<springProfile>
tag lets you optionally include or exclude sections of configuration based on the active Spring profiles.<springProfile>
标记允许您根据活动的 Spring 配置文件选择性地包含或排除配置部分。 Profile sections are supported anywhere within the<configuration>
element.<configuration>
元素中的任何地方都支持<configuration>
文件部分。 Use thename
attribute to specify which profile accepts the configuration.使用
name
属性指定哪个配置文件接受配置。 The<springProfile>
tag can contain a simple profile name (for examplestaging
) or a profile expression.<springProfile>
标签可以包含一个简单的配置文件名称(例如staging
)或配置文件表达式。 A profile expression allows for more complicated profile logic to be expressed, for exampleproduction & (eu-central | eu-west)
.配置文件表达式允许表达更复杂的配置文件逻辑,例如
production & (eu-central | eu-west)
。 Check the reference guide for more details.查看参考指南了解更多详情。
See the example below:请参阅下面的示例:
<?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>
I have ended up doing it in Groovy, as mapping the config to the Spring profiles does not work for me and my team, as we are using the dev profile for both our local and our shared dev env(hosted on K8s cluster).我最终在 Groovy 中完成了这项工作,因为将配置映射到 Spring 配置文件对我和我的团队不起作用,因为我们将开发配置文件用于本地和共享开发环境(托管在 K8s 集群上)。
I am not proud of it, but ended doing it with a simple if:我并不为此感到自豪,但以一个简单的 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
}
}
}
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.