SpringBoot使用log4j2将日志记录到文件及自定义数据库

目录

一、环境说明     

二、进行配置

1、pom.xml

2、log4j2.xml

3、CustomDataSourceProperties

4、ConfigReader

5、ConnectionFactory 连接工厂类,用于管理数据库连接

三、进行简单测试配置

1、LogUtils

2、LoginUserInfoHelper

3、LoginLogUtils

4、写日志


一、环境说明     

        Spring Boot2+MyBatis-Plus+Log4j2

二、进行配置

1、pom.xml

        由于Spring Boot内置的日志框架是logback,会导致和log4j2冲突,所以要先排除项目中logback的依赖。同时引入log4j2。

        
            org.springframework.boot
            spring-boot-starter
             
                
                    org.springframework.boot
                    spring-boot-starter-logging
                
            
        

         
            org.springframework.boot
            spring-boot-starter-log4j2
        

2、log4j2.xml





    

        
        ./logs

        

        
            %d{yyyy-MM-dd HH:mm:ss} %highlight{%6p} %style{%5pid}{bright,magenta} --- [%15.15t]
            %style{%-40.40logger{39}}{bright,cyan} : %m%n
        

        
        
            %d{yyyy-MM-dd HH:mm:ss} %p --- [%t] %logger : %m%n"
        

        

        
        10MB

    

    
    
    
    
    
    
    
    

    
        
        
        
        
    

    

        
        
            
            
            
            
        

        
        
            
            
                
                
            
            
            
            
                
                
                
                
            
        

        
        
            
                
                
                
                
            

            
            
            
                
                
                
                
            
        

        
        
            
                
                
            
            
            
            
                
                
                
                
            
        

        
        
            
            
                
                
            

            
            
            
                
                
                
                
            
        

        
        
        
        
        
        
        
        
        
        
        
        

        

        
        
            
            
                
                
            

            
            
            
            
            
            
        

        
        
            
            
                
                
            

            
            
            
            
            
            
        

        
        
            
            
                
                
            

            
            
            
            
            
            
            
            
            
        
    

    
        
            
            
            
            
            
            
            

            
            
            
            
        

        
        
        
        
        
        
        
        
        
        

        
        
        
        


        
            
        

        
        
        
        
            
        

        
        
            
        

        
        
            
        

        
        
            
        

        
        
            
        

        
        
            
        
        
            
        

        
        
            
        

        
        
            
        
        
        
            
        
        
            
        
        
        
            
        

    


3、CustomDataSourceProperties

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;


@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource")
@Scope("singleton")
public class CustomDataSourceProperties {
    public String url;
    public String username;
    public String password;
    public String driverClassName;
}

4、ConfigReader

import com.cj.blog.model.base.CustomDataSourceProperties;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.InputStream;
import java.util.Map;

/**
 * 用于从YAML文件中读取配置属性的实用工具类。
 */
public class ConfigReader {

    private static final Logger logger = LogManager.getLogger(ConfigReader.class);


    /**
     * 从YAML配置文件中获取数据源属性。
     *
     * @return CustomDataSourceProperties对象,包含数据源属性,如果未找到属性则返回null。
     */
    public static CustomDataSourceProperties getDataSourceProperties() {
        String defaultActive = "dev"; // 默认活动配置
        String defaultConfigFile = "/application.yml"; // 默认配置文件名

        // 从默认配置文件中获取Spring属性
        Map properties = getSpringProperties(defaultActive, defaultConfigFile);
        if (properties != null) {
            Map springProperties = (Map) properties.get("spring");
            if (springProperties != null) {
                Map datasourceProperties = (Map) springProperties.get("datasource");
                if (datasourceProperties != null) {
                    CustomDataSourceProperties dataSourceProperties = new CustomDataSourceProperties();
                    dataSourceProperties.setUrl((String) datasourceProperties.get("url"));
                    dataSourceProperties.setUsername((String) datasourceProperties.get("username"));
                    dataSourceProperties.setPassword((String) datasourceProperties.get("password"));
                    dataSourceProperties.setDriverClassName((String) datasourceProperties.get("driver-class-name"));
                    logger.info("获取数据源属性成功!");
                    logger.info("数据源属性:" + dataSourceProperties);

                    return dataSourceProperties;
                }
            }
        }

        return null;
    }

    /**
     * 从指定的配置文件中获取Spring属性。
     *
     * @param active     要使用的活动配置。
     * @param configFile 配置文件的路径。
     * @return 包含Spring属性的Map,如果未找到属性则返回null。
     */
    private static Map getSpringProperties(String active, String configFile) {
        // 读取配置文件
        Map data = readConfigFile(configFile);
        if (data != null) {
            Map springProperties = (Map) data.get("spring");
            if (springProperties != null) {
                Map applicationProperties = (Map) springProperties.get("profiles");
                if (applicationProperties != null) {
                    active = (String) applicationProperties.get("active");
                    System.out.println("spring.application.active: " + active);
                }
            }
        }
        logger.info("spring.application.active: " + active);
        // 读取活动配置的配置文件
        return readConfigFile("/application-" + active + ".yml");
    }

    /**
     * 读取指定的YAML配置文件并将其解析为Map。
     *
     * @param fileName YAML文件的路径。
     * @return 包含解析后的YAML数据的Map,如果未找到文件或无法解析则返回null。
     */
    private static Map readConfigFile(String fileName) {
        try {
            // 创建用于解析YAML文件的ObjectMapper对象
            ObjectMapper mapper = new ObjectMapper(new YAMLFactory());

            // 使用类加载器加载YAML文件
            InputStream inputStream = ConfigReader.class.getResourceAsStream(fileName);

            // 读取YAML文件并解析为Map对象
            return mapper.readValue(inputStream, new TypeReference>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

5、ConnectionFactory 连接工厂类,用于管理数据库连接

import com.cj.blog.common.utils.ConfigReader;
import com.cj.blog.model.base.CustomDataSourceProperties;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 连接工厂类,用于管理数据库连接。通过Apache Commons DBCP提供数据库连接池功能。
 */
@Component
public class ConnectionFactory {

    // 数据源,用于获取数据库连接
    private final DataSource dataSource;

    /**
     * 构造函数,初始化数据库连接池配置,并创建数据源。
     *
     * @param dataSourceProperties 数据源属性对象,包含数据库连接信息
     */
    public ConnectionFactory(CustomDataSourceProperties dataSourceProperties) {
        // 初始化数据库连接池配置
        Properties properties = new Properties();
        properties.setProperty("user", dataSourceProperties.getUsername());
        properties.setProperty("password", dataSourceProperties.getPassword());

        // 创建基于DriverManager的连接工厂和连接池
        GenericObjectPool pool = new GenericObjectPool<>();
        pool.setMaxActive(10); // 设置最大连接数
        pool.setMinIdle(2); // 设置最小空闲连接数
        DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(dataSourceProperties.getUrl(), properties);

        // 创建可池化的数据库连接工厂
        new PoolableConnectionFactory(connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED);

        // 创建数据源
        this.dataSource = new PoolingDataSource(pool);
    }

    /**
     * 获取数据库连接。
     *
     * @return 数据库连接
     * @throws SQLException 如果获取连接时发生错误
     */
    public static Connection getDatabaseConnection() throws SQLException {
        return Singleton.INSTANCE.dataSource.getConnection();
    }

    /**
     * 单例类,确保只有一个连接工厂实例。
     */
    private static class Singleton {
        static ConnectionFactory INSTANCE;

        static {
            // TODO 这里需要修改,在创建数据库连接工厂时,需要从配置文件中读取数据源属性
            // 从配置文件中读取数据源属性,并创建连接工厂实例
            CustomDataSourceProperties dataSourceProperties = ConfigReader.getDataSourceProperties();

            dataSourceProperties.setDriverClassName(dataSourceProperties.getDriverClassName());
            dataSourceProperties.setUrl(dataSourceProperties.getUrl());
            dataSourceProperties.setUsername(dataSourceProperties.getUsername());
            dataSourceProperties.setPassword(dataSourceProperties.getPassword());

            INSTANCE = new ConnectionFactory(dataSourceProperties);
        }
    }

}

三、进行简单测试配置

1、LogUtils

import org.apache.logging.log4j.ThreadContext;

/**
 * 日志工具类
 */
public class LogUtils {

    /**
     * 清除登录日志上下文信息,以便在日志记录操作结束后不保留上下文。
     */
    public static void clearLogContext() {
        ThreadContext.clearMap();
    }
}

2、LoginUserInfoHelper

/**
 * 用于存储和获取用户信息的线程局部存储辅助类。
 */
public class LoginUserInfoHelper {

    // 使用 ThreadLocal 来存储用户ID和用户名
    private static final ThreadLocal userId = new ThreadLocal<>();
    private static final ThreadLocal userName = new ThreadLocal<>();

    /**
     * 获取当前线程的用户ID。
     *
     * @return 用户ID
     */
    public static Long getUserId() {
        return userId.get();
    }

    /**
     * 设置当前线程的用户ID。
     *
     * @param _userId 用户ID
     */
    public static void setUserId(Long _userId) {
        userId.set(_userId);
    }

    /**
     * 从当前线程中移除用户ID。
     */
    public static void removeUserId() {
        userId.remove();
    }

    /**
     * 获取当前线程的用户名。
     *
     * @return 用户名
     */
    public static String getUsername() {
        return userName.get();
    }

    /**
     * 设置当前线程的用户名。
     *
     * @param _username 用户名
     */
    public static void setUsername(String _username) {
        userName.set(_username);
    }

    /**
     * 从当前线程中移除用户名。
     */
    public static void removeUsername() {
        userName.remove();
    }
}

3、LoginLogUtils

import com.cj.blog.common.utils.RequestUtils;
import com.cj.blog.model.auth.LoginUserInfoHelper;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.Optional;


/**
 * 登录日志工具类。
 */
public class LoginLogUtils {

    private static final Logger logger = LogManager.getLogger(LoginLogUtils.class);

    /**
     * 设置用户登录日志,根据HttpServletRequest设置日志上下文信息。
     *
     * @param request 请求对象,用于获取远程地址和用户代理信息。
     * @param remarks 登录日志的备注信息。
     */
    public static void setLogLogin(HttpServletRequest request, String remarks) {
        // 使用 LoginUserInfoHelper 中的用户信息设置登录日志
        setLogLogin(
                LoginUserInfoHelper.getUserId().toString(),
                LoginUserInfoHelper.getUsername(),
                request.getRemoteAddr(),
                RequestUtils.getUserAgent(request),
                remarks
        );
    }

    /**
     * 设置用户登录日志,根据指定的客户端IP、设备信息和备注信息。
     *
     * @param clientIp   客户端IP地址。
     * @param deviceInfo 设备信息。
     * @param remarks    登录日志的备注信息。
     */
    public static void setLogLogin(String clientIp, String deviceInfo, String remarks) {
        // 使用 LoginUserInfoHelper 中的用户信息设置登录日志
        setLogLogin(
                LoginUserInfoHelper.getUserId().toString(),
                LoginUserInfoHelper.getUsername(),
                clientIp,
                deviceInfo,
                remarks
        );
    }

    /**
     * 设置用户登录日志,包含用户ID、用户名、客户端IP、设备信息和备注信息。
     *
     * @param userId     用户ID。
     * @param userName   用户名。
     * @param clientIp   客户端IP地址。
     * @param deviceInfo 设备信息。
     * @param remarks    登录日志的备注信息。
     */
    public static void setLogLogin(String userId, String userName, String clientIp, String deviceInfo, String remarks) {
        logLogin(userId, userName, clientIp, deviceInfo, remarks);
    }

    /**
     * 设置用户登录日志,包含用户ID、用户名、客户端IP、设备信息和备注信息。
     *
     * @param userId     用户ID。
     * @param userName   用户名。
     * @param clientIp   客户端IP地址。
     * @param deviceInfo 设备信息。
     * @param remarks    登录日志的备注信息。
     */
    public static void logLogin(String userId, String userName, String clientIp, String deviceInfo, String remarks) {
        try {
            // 设置上下文信息,这些信息将与日志消息关联
            ThreadContext.put("user_id", Objects.toString(userId, "0"));
            ThreadContext.put("user_name", Objects.toString(userName, "0"));
            ThreadContext.put("client_ip", clientIp);
            ThreadContext.put("device_info", deviceInfo);
            ThreadContext.put("remarks", remarks);

            // 使用自定义的 Login 级别记录消息
            logger.log(Level.getLevel("LOGIN_LOG"), remarks);
        } finally {
            LogUtils.clearLogContext(); // 在try块之后清除上下文,确保上下文信息不泄漏
        }
    }

    /**
     * 设置用户登录日志,包含用户ID、用户名、客户端IP、设备信息和备注信息。
     *
     * @param clientIp   客户端IP地址。
     * @param deviceInfo 设备信息。
     * @param remarks    登录日志的备注信息。
     * @param userParams 用户ID、用户名。
     */
    public static void setLogLogin(String clientIp, String deviceInfo, String remarks, String... userParams) {
        String userId = Optional.ofNullable(userParams.length > 0 ? userParams[0] : null).orElse("0");
        String userName = Optional.ofNullable(userParams.length > 1 ? userParams[1] : null).orElse("0");

        try {
            // 设置上下文信息,这些信息将与日志消息关联
            ThreadContext.put("user_id", userId);
            ThreadContext.put("user_name", userName);
            ThreadContext.put("client_ip", clientIp);
            ThreadContext.put("device_info", deviceInfo);
            ThreadContext.put("remarks", remarks);

            // 使用自定义的 Login 级别记录消息
            logger.log(Level.getLevel("LOGIN_LOG"), remarks);
        } finally {
            LogUtils.clearLogContext(); // 在try块之后清除上下文,确保上下文信息不泄漏
        }
    }

}

4、写日志

        LoginLogUtils.setLogLogin(
                customUser.getSysUser().getUserId().toString(),
                customUser.getUsername(),
                request.getRemoteAddr(),
                RequestUtils.getUserAgent(request),
                "登录成功"
        );

你可能感兴趣的:(Springboot,spring,boot,log4j,java)