目录
5、ConnectionFactory 连接工厂类,用于管理数据库连接
Spring Boot2+MyBatis-Plus+Log4j2
由于Spring Boot内置的日志框架是logback,会导致和log4j2冲突,所以要先排除项目中logback的依赖。同时引入log4j2。
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starterartifactId>
- <exclusions>
- <exclusion>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-loggingartifactId>
- exclusion>
- exclusions>
- dependency>
-
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-log4j2artifactId>
- dependency>
- "1.0" encoding="UTF-8"?>
- <Configuration status="WARN" monitorInterval="30">
-
- <Properties>
-
-
- <Property name="baseDir">./logsProperty>
-
-
-
- <Property name="logPattern">
- %d{yyyy-MM-dd HH:mm:ss} %highlight{%6p} %style{%5pid}{bright,magenta} --- [%15.15t]
- %style{%-40.40logger{39}}{bright,cyan} : %m%n
- Property>
-
-
- <Property name="fileLayout">
- %d{yyyy-MM-dd HH:mm:ss} %p --- [%t] %logger : %m%n"
- Property>
-
-
-
-
- <Property name="fileSize">10MBProperty>
-
- Properties>
-
-
-
-
-
-
-
-
-
-
- <CustomLevels>
- <CustomLevel name="CUSTOM_LOG" intLevel="90"/>
- <CustomLevel name="EXCEPTION_LOG" intLevel="91"/>
- <CustomLevel name="OPERATION_LOG" intLevel="92"/>
- <CustomLevel name="LOGIN_LOG" intLevel="93"/>
- CustomLevels>
-
- <Appenders>
-
-
- <Console name="Console" target="SYSTEM_OUT">
-
- <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
-
- <PatternLayout pattern="${logPattern}"/>
- Console>
-
-
- <RollingFile name="DebugAppender" fileName="${baseDir}/app_debug.log"
- filePattern="${baseDir}/debug_%d{yyyy-MM-dd}_%i.log">
-
- <Filters>
- <ThresholdFilter level="DEBUG"/>
- <ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
- Filters>
-
- <PatternLayout pattern="${fileLayout}"/>
- <Policies>
-
- <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
-
- <SizeBasedTriggeringPolicy size="${fileSize}"/>
- Policies>
- RollingFile>
-
-
- <RollingFile name="InfoAppender" fileName="${baseDir}/app_info.log"
- filePattern="${baseDir}/info_%d{yyyy-MM-dd}_%i.log">
- <Filters>
-
-
- <ThresholdFilter level="INFO"/>
- <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
- Filters>
-
-
- <PatternLayout pattern="${fileLayout}"/>
- <Policies>
-
- <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
-
- <SizeBasedTriggeringPolicy size="${fileSize}"/>
- Policies>
- RollingFile>
-
-
- <RollingFile name="WarnAppender" fileName="${baseDir}/app_warn.log"
- filePattern="${baseDir}/info_%d{yyyy-MM-dd}_%i.log">
- <Filters>
- <ThresholdFilter level="WARN"/>
- <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
- Filters>
-
- <PatternLayout pattern="${fileLayout}"/>
- <Policies>
-
- <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
-
- <SizeBasedTriggeringPolicy size="${fileSize}"/>
- Policies>
- RollingFile>
-
-
- <RollingFile name="ErrorAppender" fileName="${baseDir}/app_error.log"
- filePattern="${baseDir}/error_%d{yyyy-MM-dd}_%i.log">
-
- <Filters>
- <ThresholdFilter level="ERROR"/>
- <ThresholdFilter level="FATAL" onMatch="DENY" onMismatch="NEUTRAL"/>
- Filters>
-
-
- <PatternLayout pattern="${fileLayout}"/>
- <Policies>
-
- <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
-
- <SizeBasedTriggeringPolicy size="${fileSize}"/>
- Policies>
- RollingFile>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <JDBC name="LoginDatabase" tableName="sys_log_login">
- <ConnectionFactory class="com.cj.blog.common.logs.ConnectionFactory" method="getDatabaseConnection"/>
- <Filters>
- <ThresholdFilter level="LOGIN_LOG"/>
- <ThresholdFilter level="OPERATION_LOG" onMatch="DENY" onMismatch="NEUTRAL"/>
- Filters>
-
- <Column name="user_id" pattern="%X{user_id}"/>
- <Column name="user_name" pattern="%X{user_name}"/>
- <Column name="client_ip" pattern="%X{client_ip}"/>
- <Column name="device_info" pattern="%X{device_info}"/>
- <Column name="remarks" pattern="%X{remarks}"/>
- <Column name="login_time" pattern="%d{yyyy-MM-dd HH:mm:ss}"/>
- JDBC>
-
-
- <JDBC name="OperationDatabase" tableName="sys_log_operation">
- <ConnectionFactory class="com.cj.blog.common.logs.ConnectionFactory" method="getDatabaseConnection"/>
- <Filters>
- <ThresholdFilter level="OPERATION_LOG"/>
- <ThresholdFilter level="EXCEPTION_LOG" onMatch="DENY" onMismatch="NEUTRAL"/>
- Filters>
-
- <Column name="user_id" pattern="%X{user_id}"/>
- <Column name="user_name" pattern="%X{user_name}"/>
- <Column name="v_before" pattern="%X{v_before}"/>
- <Column name="v_after" pattern="%X{v_after}"/>
- <Column name="remarks" pattern="%X{remarks}"/>
- <Column name="operation_time" pattern="%d{yyyy-MM-dd HH:mm:ss}"/>
- JDBC>
-
-
- <JDBC name="ExceptionDatabase" tableName="sys_log_exception">
- <ConnectionFactory class="com.cj.blog.common.logs.ConnectionFactory" method="getDatabaseConnection"/>
- <Filters>
- <ThresholdFilter level="EXCEPTION_LOG"/>
- <ThresholdFilter level="CUSTOM_LOG" onMatch="DENY" onMismatch="NEUTRAL"/>
- Filters>
-
- <Column name="user_id" pattern="%X{user_id}"/>
- <Column name="user_name" pattern="%X{user_name}"/>
- <Column name="request_mode" pattern="%X{request_mode}"/>
- <Column name="absolute_uri" pattern="%X{absolute_uri}"/>
- <Column name="form_data" pattern="%X{form_data}"/>
- <Column name="source" pattern="%X{source}"/>
- <Column name="message" pattern="%X{message}"/>
-
- <Column name="exception_time" pattern="%d{yyyy-MM-dd HH:mm:ss}"/>
- JDBC>
- Appenders>
-
- <Loggers>
- <Root level="DEBUG">
-
- <AppenderRef ref="Console" level="INFO"/>
-
- <AppenderRef ref="ErrorAppender" level="ERROR"/>
- <AppenderRef ref="WarnAppender" level="WARN"/>
- <AppenderRef ref="InfoAppender" level="INFO"/>
- <AppenderRef ref="DebugAppender" level="DEBUG"/>
-
-
- <AppenderRef ref="LoginDatabase" level="LOGIN_LOG"/>
- <AppenderRef ref="OperationDatabase" level="OPERATION_LOG"/>
- <AppenderRef ref="ExceptionDatabase" level="EXCEPTION_LOG"/>
- Root>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <Logger name="org.apache.logging.log4j" level="DEBUG" additivity="false">
- <AppenderRef ref="Console"/>
- Logger>
-
-
-
-
- <Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR">
-
- Logger>
-
-
- <Logger name="org.apache.catalina.util.LifecycleBase" level="ERROR">
-
- Logger>
-
-
- <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN">
-
- Logger>
-
-
- <Logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN">
-
- Logger>
-
-
- <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN">
-
- Logger>
-
-
- <Logger name="org.crsh.plugin" level="WARN">
-
- Logger>
- <Logger name="org.crsh.ssh" level="WARN">
-
- Logger>
-
-
- <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR">
-
- Logger>
-
-
- <Logger name="org.hibernate.validator.internal.util.Version" level="WARN">
-
- Logger>
-
- <Logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="WARN">
-
- Logger>
- <Logger name="org.springframework.boot.actuate.endpoint.jmx" level="WARN">
-
- Logger>
-
- <Logger name="org.thymeleaf" level="WARN">
-
- Logger>
-
- Loggers>
-
- Configuration>
- 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;
- }
- 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;
- }
- }
-
- }
- 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);
- }
- }
-
- }
- import org.apache.logging.log4j.ThreadContext;
-
- /**
- * 日志工具类
- */
- public class LogUtils {
-
- /**
- * 清除登录日志上下文信息,以便在日志记录操作结束后不保留上下文。
- */
- public static void clearLogContext() {
- ThreadContext.clearMap();
- }
- }
- /**
- * 用于存储和获取用户信息的线程局部存储辅助类。
- */
- 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();
- }
- }
- 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块之后清除上下文,确保上下文信息不泄漏
- }
- }
-
- }
- LoginLogUtils.setLogLogin(
- customUser.getSysUser().getUserId().toString(),
- customUser.getUsername(),
- request.getRemoteAddr(),
- RequestUtils.getUserAgent(request),
- "登录成功"
- );