从日志回退配置文件中读取环境变量

我有这个 logback.xml 文件:

<configuration debug="true" scan="true" scanPeriod="60 seconds">


<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>


<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${MY_HOME}/logs/mylog.log</File>


<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/my.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>


<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level - %msg%n</Pattern>
</layout>


</appender>


<root level="TRACE">
<appender-ref ref="FILE"/>
</root>


</configuration>

${MY_HOME}是一个已定义的系统变量(Linux 上的 echo $MY_HOME显示了正确的路径)。

问题是 logback 似乎没有正确地读取它,它将日志存储在 MY_HOME_IS_UNDEFINED/logs/my.log

我做错了什么? 非常感谢!

编辑: 我犯了一个错误,把 OSC _ HOME 放在了我真正想放的地方。对不起

118931 次浏览

You perhaps mean MY_HOME. In your config file there is reference for OSC_HOME. See Variable substitution rules of Logback for details.

You can pass environment variable as a Java System property and then Logback will perform the variable substitution. You can pass this as JVM option in your command line. For example:

java -DMY_HOME=${MY_HOME} -cp ... MainClass

Or You can define MY_HOME in your config file itself.

<configuration debug="true" scan="true" scanPeriod="60 seconds">
<property name="MY_HOME" value="/home/my" />
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${MY_HOME}/logs/mylog.log</File>
</appender>
</configuration>

Things are actually working as designed: logback doesn't read environment variables at all when doing variable substitution. Quoting the documentation:

The value of the substituted variable can be defined in the configuration file itself, in an external properties file or as a system property.

So, either use one of the mentioned solutions or get OSC_HOME_IS_UNDEFINED :)

Contrary to what the others have said, the logback documentation explicitly states that "During substitution, properties are looked up in the local scope first, in the context scope second, in the system properties scope third, and in the OS environment fourth and last". So if the property is defined in the environment, logback will find it.

I was having the same issue when running my project in Eclipse. If that's the issue you're having, it can be fixed by going to Run Configurations -> Environment and adding MY_HOME to the environment variables.

Not really sure why it isn't loading the native environment by default. There's even an option called "Append environment to native environment" which doesn't seem to have any effect for me.

If you're using Eclipse you have to restart it to pick up environment variables, but you can't use: File -> Restart

Instead you actually have to fully shut it down and then start it back up again.

There is an alternative way to read environment variables from config file. you can put your custom variables to logback context with context listener.

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="30 seconds">


<!-- THIS IS OUR CUSTOM CONTEXT LISTENER -->
<contextListener class="com.myapp.logging.listener.LoggerStartupListener"/>


<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%-5level] %d{HH:mm:ss.SSS} [%.6thread] %logger - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>


<appender name="FILEOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${MY_HOME}/${LOG_FILE}.log</file>
<append>true</append>
<!-- Support multiple-JVM writing to the same log file -->
<prudent>true</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- Daily rollover -->
<fileNamePattern>${MY_HOME}/${LOG_FILE}.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- Keep 7 days' worth of history -->
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%-5level] %d{HH:mm:ss.SSS} [%.6thread] %logger - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>


<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILEOUT"/>
</root>
</configuration>

LoggerStartupListener.java

package com.myapp.logging.listener;


import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;


public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {


private static final String DEFAULT_LOG_FILE = "MYAPP";


private boolean started = false;


@Override
public void start() {
if (started) return;


String userHome = System.getProperty("user.home");


String logFile = System.getProperty("log.file"); // log.file is our custom jvm parameter to change log file name dynamicly if needed


logFile = (logFile != null && logFile.length() > 0) ? logFile : DEFAULT_LOG_FILE;


Context context = getContext();


context.putProperty("MY_HOME", userHome);
context.putProperty("LOG_FILE", logFile);


started = true;
}


@Override
public void stop() {
}


@Override
public boolean isStarted() {
return started;
}


@Override
public boolean isResetResistant() {
return true;
}


@Override
public void onStart(LoggerContext context) {
}


@Override
public void onReset(LoggerContext context) {
}


@Override
public void onStop(LoggerContext context) {
}


@Override
public void onLevelChange(Logger logger, Level level) {
}
}

Instead of using environmental variables, you can use tag to declare variables in logback.xml.