The log4j2 log level cannot be changed

SpringBoot dynamically sets the logback log level

The above two articles are merely technical in that dynamic logging configuration is possible. However, there is no solution suitable for production environment. Today we introduce a dynamic configuration log level based on Nacos configuration center.

0x01: Install the Nacos configuration center

Configuration center Nacos official website

Liverpoolfc.tv: nacos. IO/useful – cn/docs /… Downloading the Installation package

It is important to note that Nacos requires a 64-bit operating system and a 64-bit JDK, and the following exceptions will occur if it is not started 64-bit.

at com.alipay.sofa.jraft.core.NodeImpl.init(NodeImpl.java:138) at com.alipay.sofa.jraft.RaftServiceFactory.createAndInitRaftNode(RaftServiceFactory.java:47) at com.alipay.sofa.jraft.RaftGroupService.start(RaftGroupService.java:129) at com.alibaba.nacos.core.distributed.raft.JRaftServer.createMultiRaftGroup(JRaftServer.java:268) at com.alibaba.nacos.core.distributed.raft.JRaftProtocol.addRequestProcessors(JRaftProtocol.java:163) at com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl.<init>(PersistentClientOperationServi ceImpl.java:92) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:175) ... 57 common frames omitted Caused by: java.lang.UnsupportedOperationException: Cannot determine JNI library name for ARCH='x86' OS='windows 10' name='rocksdb' at org.rocksdb.util.Environment.getJniLibraryName(Environment.java:88) at org.rocksdb.NativeLibraryLoader.<clinit>(NativeLibraryLoader.java:19) ... 74 common frames omittedCopy the code

Start the command

startup.cmd -m standalone
Copy the code

Because the startup script starts in cluster mode by default, to avoid the need to use commands, modify the startup script as follows

If the startup is successful, the following log is displayed

2021-05-14 22:00:34, 5 INFO: Exposing to the brilliant 16 2021-05-14 22:00:38, 2038 INFO Tomcat started on port(s): 8848 (HTTP) with context path ‘/nacos’ 2021-05-14 22:00:34,473 INFO nacos started successfully in stand alone mode. Use The embedded Storage accesses the Nacos configuration center with the corresponding account and password Nacos/Nacos

http://192.168.10.6:8848/nacos/index.html

0x02: Create the configuration file dynamics-log.json in the configuration center

The following

{
    "logger": [
        {
            "name":"ROOT",
            "level":"debug"
        }
    ]
}
Copy the code

0x03: Pom.xml file introduces dependencies

The < project XMLNS = "http://maven.apache.org/POM/4.0.0" XMLNS: xsi = "http://www.w3.org/2001/XMLSchema-instance" Xsi: schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion > 4.0.0 < / modelVersion > < groupId > com. Olive < / groupId > < artifactId > valid - demo < / artifactId > < version > 0.0.1 - the SNAPSHOT < / version > < packaging > jar < / packaging > < the parent > < groupId > org. Springframework. Boot < / groupId > <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath /> </parent> <name>valid-demo</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> < the groupId > com. Alibaba. Nacos < / groupId > < artifactId > nacos - client < / artifactId > < version > 0.5.0 < / version > < / dependency > </dependencies> </project>Copy the code

Nacos client (NACOS-client) dependency is mainly introduced

0x04: Compiler listener

To achieve dynamic effects, there are generally two methods: PUSH and PULL. Nacos provides listeners that can listen on different data-ids as long as the relevant methods are implemented. The general code is as follows:

import java.util.concurrent.Executor; import javax.annotation.PostConstruct; import org.springframework.context.annotation.Configuration; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; @configuration public class LoggerDynamicsConfig {private String serverAddr = "127.0.0.1:"; @configuration public class LoggerDynamicsConfig {private String serverAddr = "127.0.0.1:"; private String dataId = "dynamics-log.json"; private String group = "DEFAULT_GROUP"; private long timeoutMs = 50000L; @PostConstruct public void init() { try { ConfigService configService = NacosFactory.createConfigService(serverAddr); String configInfo = configService.getConfig(dataId, group, timeoutMs); System.out.println("configInfo = " + configInfo); configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { System.out.println("recieve configInfo : " + configInfo); } @Override public Executor getExecutor() { return null; }}); } catch (NacosException e) { e.printStackTrace(); }}}Copy the code

After the service is started, the console displays the following log, indicating that the configuration items of the Nacos configuration center can be obtained.

The 2021-05-14 22:13:56. 265. [the main] INFO c.a. libaba. Nacos. Client. The identify. CredentialWatcher - [] [] [] No credential found configInfo = { "logger": [ { "name":"ROOT", "level":"debug" } ] } recieve configInfo : { "logger": [ { "name":"ROOT", "Level" : "debug"}}] 22:13:56. 2021-05-14, 563 [main] INFO O.S.S cheduling. Concurrent. ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'Copy the code

In the Nacos configuration center, you can see that the console prints the following log, indicating that the service can dynamically listen for configuration changes.

recieve configInfo : {
    "logger": [
        {
            "name":"ROOT",
            "level":"debug"
        }, {
            "name":"com.spring",
            "level":"info"
        }
    ]
}
Copy the code

0x05: Description of dynamic log transformation

In the receiveConfigInfo() method, construct the configuration JSON string as an array of objects, such as LoggerConfig[].

Change LoggerController’s printAllLogger() method to a method that gets all log objects, based on SpringBoot dynamically setting logback log levels

public List<Logger> getAllLogger(){
     //TODO

}
Copy the code

Dynamically update the LoggerConfig[] array constructed by configuration changes dynamically by traversing and comparing all log objects retrieved by calling getAllLogger()