I have deployed springboot application in PCF . I want to log the message based on the environment variable .What should I do so that the run time log level change will work without restarting the application?
Changing the log level in Spring Boot 1.5+ can be done with a http-endpoint
Add
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
and than you can use
curl -X "POST" "http://localhost:8080/loggers/de.springbootbuch" \
-H "Content-Type: application/json; charset=utf-8" \
-d $'{
"configuredLevel": "WARN"
}'
Where everything beyond /loggers/ is the name of the logger.
If you running this in PCF it get's even better: This is directly supported from their backend.
Since Spring Boot 1.5.x, you can use logger endpoint to POST desired logging level.
This is an extension of @Michael Simons answer. With this method you will have a UI for doing that:
This method is a bit longer but it solves much much more. We are going to use a tool called Spring Boot Admin Server.
First you need to include some dependencies
<!--Dependency for registering your app as a Spring Boot Admin Server--> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-server</artifactId> <version>1.3.3</version> </dependency> <!--Provide a nice looking ui--> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-server-ui</artifactId> <version>1.3.3</version> </dependency> <!--Dependency for registering your app as a Spring Boot Admin Client--> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.jolokia</groupId> <artifactId>jolokia-core</artifactId> </dependency>
Enable your app to be a Spring Boot Admin Server using the annotation
@EnableAdminServer
.@SpringBootApplication @EnableAdminServer public class Application { public static void main(String[] args) { // ... your code as before ... } }
In your
application.properties
add the following:Register your app to the Spring Boot Admin Server which is still your app
spring.boot.admin.url=http://localhost:8031
Instruct Spring Boot Admin Server where to find the client
// For versions 2.*.* spring.boot.admin.client.url=http://localhost:8031 // For versions 1.*.* spring.boot.admin.client.service-url=http://localhost:8031 spring.boot.admin.client.management-url=http://localhost:8031 spring.boot.admin.client.health-url=http://localhost:8031/health
In your
logback.xml
just add the following line<jmxConfigurator/>
. This allows configuration of logback via JMX. More info here
... and voila you are done. Now you can change the debug level for any logger at runtime.
i. Just visit the url for your Spring Boot Admin Server-in our case here (http:/localhost:8031
).
ii. A list of applications (clients) registered will be displayed on the home page.
iii. Click Details
against the registered clients which will take you to another page.
iv. Click the Logging
tab which will list all loggers registered in your application.
v. You can change the log levels it will change your logging level at runtime. Here is a snippet of what you expect
If you are using logback
api to configure logging in the project then you can use the AutoScan feature of logback
api.
As per documentation
logback-classic will scan for changes in its configuration file and automatically reconfigure itself when the configuration file changes. In order to instruct logback-classic to scan for changes in its configuration file and to automatically re-configure itself set the scan attribute of the element to true.
<configuration scan="true">
...
</configuration>
Scan frequency: "By default, the configuration file will be scanned for changes once every minute
". See the logback
API documentation for more details.
For Spring Boot 2.1.5+:
First, you need the actuator Plugin:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Second, you need to expose the endpoint like Dennis said in his comment (loggers
is disabled by default):
management.endpoints.web.exposure.include=health,info,loggers
Finally, you can use the Rest Endpoints to get Information about the loggers and set the logging levels.
curl -X "GET" "http://localhost:8080/actuator/loggers"
To set the Root
logging Level you can use
curl -X "POST" "http://localhost:8080/actuator/loggers/ROOT" -H "Content-Type: application/json; charset=utf-8" -d $'{ "configuredLevel": "INFO" }'
The default logging provider is logback. To setup your system so that the logging level can be changed at runtime you need to perform the following steps:
Firstly in src/main/resources
create a custom logback configuration named logback-spring.xml
that includes spring's default configurator and then adds the directive that exposes logback configuration over JMX:
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<jmxConfigurator />
</configuration>
Now add a dependency on the Jolokia JMX-over-HTTP bridge: org.jolokia:jolokia-core
.
You should now be able to hit /jolokia
endpoints on your spring boot application. The protocol is documented here. It's not pretty. To get you started, here's a few GET
examples that you can hit straight from a browser:
Show ROOT logger level:
/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/getLoggerLevel/ROOT
Change ROOT logger level to debug:
/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/setLoggerLevel/ROOT/debug
spring-boot-actuator is aware of the /jolokia
endpoint and it is marked sensitive=true
so if you have spring-security on the classpath then it will require authentication.
You can also add a settings page in the web service to update the log level. This can then be done using ajax. The following example includes login and csrf token:
First, add some form to specify the new log level. Can be improved for example by using a select
element.
<form>
<input type="text" id="logClassName" name="logClassName"/>
<input type="text" id="logLevel" name="logLevel" />
<button onclick="submitLogLevelChange(); return false;">Submit</button>
</form>
Then, the request is sent:
function submitLogLevelChange() {
var className = document.getElementById('logClassName').value;
var logLevel = document.getElementById("logLevel").value;
$.ajax({
// Set up security, see below.
beforeSend: setHeader,
type: 'POST',
// specify the logger to be modified
url: "/loggers/" + className,
// specify the new log level
data: '{"configuredLevel":"' + logLevel + '"}',
contentType: 'application/json',
processData: false,
}).done(function(data, textStatus, jqXHR) {
if (jqXHR.status === 200) {
// Happy
} else if (jqXHR.status === 401) {
// Logged out or not enough user rights
} else {
//Some other problem
}
})
.fail(function(jqXHR, textStatus ) {
if (jqXHR.status === 200) {
// Actually was successful, FireFox has some issues...
} else {
// Failure
}
});
}
The following function injects the csrf token to the POST request:
function setHeader(xhr) {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
xhr.setRequestHeader(header, token);
}
If you use Log4j 2 for logging you can easily configuration it to set the log level to use based on an environment variable or system property. If you do it this way you won't need to modify the file just because the environment changed.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR" monitorInterval="300">
<properties>
<property name="LOG_DIR">${sys:user.dir}/logs/</property>
<property name="log_env">${sys:env:-lab}</property>
<property name="flow_lab">${sys:flow_match:-ACCEPT}</property>
<property name="flow_prod">NEUTRAL</property>
<property name="level_lab">DEBUG</property>
<property name="level_prod">INFO</property>
</properties>
<MarkerFilter marker="FLOW" onMatch="${flow_${log_env}}" onMismatch="NEUTRAL"/>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ABSOLUTE} %-5level # %class.%method %m%n" />
</Console>
<RollingFile name="log4j" fileName="${LOG_DIR}/log4j.txt" filePattern="${LOG_DIR}/archive/log4j.txt.%d{yyyyMMdd_HHmmss}-%i">
<PatternLayout>
<MarkerPatternSelector defaultPattern="%d [%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress, corpAcctNumber} %C{1.}.%M:%L - %m%n">
<PatternMatch key="FLOW" pattern="%d [%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress, corpAcctNumber} -------- %C{1.}.%M:%L %msg --------%n"/>
</MarkerPatternSelector>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="30 MB"/>
</Policies>
<!-- A max of 20 will allow 20 files per second with the date pattern specified on the RollingFile declaration.
Hopefully that is a ridiculous value -->
<DefaultRolloverStrategy min="1" max="20">
<Delete basePath="${LOG_DIR}/archive">
<!-- Nested conditions: the inner condition is only evaluated on files for which the outer conditions are true. -->
<IfFileName glob="log4j.txt.*">
<!-- Only allow 1 GB of files to accumulate -->
<IfAccumulatedFileSize exceeds="1 GB"/>
</IfFileName>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="com.mycorp.package1" level="${level_${log_env}}" additivity="false">
<AppenderRef ref="log4j"/>
</Logger>
<Logger name="com.mycorp.package2" level="info" additivity="false">
<AppenderRef ref="log4j"/>
</Logger>
<Root level="${level_${log_env}}">
<AppenderRef ref="log4j" />
</Root>
</Loggers>
来源:https://stackoverflow.com/questions/33844580/how-do-i-change-log-level-in-runtime-without-restarting-spring-boot-application