SpringBoot使用log4j2將日志記錄到文件及自定義數(shù)據(jù)庫的配置方法
一、環(huán)境說明
Spring Boot2+MyBatis-Plus+Log4j2
二、進(jìn)行配置
1、pom.xml
由于Spring Boot內(nèi)置的日志框架是logback,會導(dǎo)致和log4j2沖突,所以要先排除項(xiàng)目中l(wèi)ogback的依賴。同時引入log4j2。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <!-- 去掉springboot默認(rèn)配置 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <!-- 引入log4j2依賴 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
2、log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Configuration 標(biāo)簽用于配置 Log4j2,其中 status 屬性設(shè)置為 "WARN",表示僅記錄警告級別的日志用于運(yùn)行時狀態(tài)。 monitorInterval 屬性設(shè)置為 "30",表示每隔 30 秒自動檢查配置文件更改。 --> <Configuration status="WARN" monitorInterval="30"> <Properties> <!-- 定義名為 "baseDir" 的屬性,用于指定日志文件的基本目錄。其他地方可以使用 "${baseDir}" 引用這個值。 --> <Property name="baseDir">./logs</Property> <!-- 定義名為 "logPattern" 的屬性,用于指定日志的格式,以便在后續(xù)配置中引用。 --> <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="logPattern">%highlight{%d{HH:mm:ss:SSS} [%-5p] - %l - %m%n}{FATAL=red, ERROR=red, WARN=yellow, INFO=default, DEBUG=default}</Property> --> <!-- 定義名為 "fileSize" 的屬性,用于指定日志文件的最大大小。其他地方可以使用 "${fileSize}" 引用這個值。 --> <Property name="fileSize">10MB</Property> </Properties> <!-- OFF 0 --> <!-- FATAL 100 --> <!-- ERROR 200 --> <!-- WARN 300 --> <!-- INFO 400 --> <!-- DEBUG 500 --> <!-- TRACE 600 --> <!-- ALL Integer.MAX_VALUE --> <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 Appender 用于將日志輸出到控制臺。--> <Console name="Console" target="SYSTEM_OUT"> <!-- 只輸出 DEBUG 級別及以上的日志 --> <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- 使用定義的日志格式 --> <PatternLayout pattern="${logPattern}"/> </Console> <!-- DEBUG 級別的日志文件輸出 --> <RollingFile name="DebugAppender" fileName="${baseDir}/app_debug.log" filePattern="${baseDir}/debug_%d{yyyy-MM-dd}_%i.log"> <!-- 僅接受 DEBUG 級別的日志 --> <Filters> <ThresholdFilter level="DEBUG"/> <ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!-- 使用定義的日志格式 --> <PatternLayout pattern="${fileLayout}"/> <Policies> <!-- 每隔 1 天滾動一次日志,"modulate" 表示即使沒有日志產(chǎn)生,也會在下一天創(chuàng)建新文件 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 當(dāng)日志文件大小達(dá)到指定大小時觸發(fā)滾動 --> <SizeBasedTriggeringPolicy size="${fileSize}"/> </Policies> </RollingFile> <!-- INFO 級別的日志文件輸出 --> <RollingFile name="InfoAppender" fileName="${baseDir}/app_info.log" filePattern="${baseDir}/info_%d{yyyy-MM-dd}_%i.log"> <Filters> <!-- 僅接受 INFO 級別的日志 --> <!-- onMatch="ACCEPT" onMismatch="DENY" --> <ThresholdFilter level="INFO"/> <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!-- 使用定義的日志格式 --> <PatternLayout pattern="${fileLayout}"/> <Policies> <!-- 每隔 1 天滾動一次日志,"modulate" 表示即使沒有日志產(chǎn)生,也會在下一天創(chuàng)建新文件 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 當(dāng)日志文件大小達(dá)到指定大小時觸發(fā)滾動 --> <SizeBasedTriggeringPolicy size="${fileSize}"/> </Policies> </RollingFile> <!--處理 WARN 級別的日志--> <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> <!-- 每隔 1 天滾動一次日志,"modulate" 表示即使沒有日志產(chǎn)生,也會在下一天創(chuàng)建新文件 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 當(dāng)日志文件大小達(dá)到指定大小時觸發(fā)滾動 --> <SizeBasedTriggeringPolicy size="${fileSize}"/> </Policies> </RollingFile> <!-- ERROR 級別的日志文件輸出 --> <RollingFile name="ErrorAppender" fileName="${baseDir}/app_error.log" filePattern="${baseDir}/error_%d{yyyy-MM-dd}_%i.log"> <!-- 僅接受 ERROR 級別的日志 --> <Filters> <ThresholdFilter level="ERROR"/> <ThresholdFilter level="FATAL" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!-- 使用定義的日志格式 --> <PatternLayout pattern="${fileLayout}"/> <Policies> <!-- 每隔 1 天滾動一次日志,"modulate" 表示即使沒有日志產(chǎn)生,也會在下一天創(chuàng)建新文件 --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- 當(dāng)日志文件大小達(dá)到指定大小時觸發(fā)滾動 --> <SizeBasedTriggeringPolicy size="${fileSize}"/> </Policies> </RollingFile> <!-- 配置 HikariSqlFile Appender,將 SQL 日志輸出到文件的日志記錄追加器 --> <!-- <RollingFile name="HikariSqlAppender" fileName="${baseDir}/app_sql.log" --> <!-- filePattern="${baseDir}/app_sql_%d{yyyy-MM-dd}_%i.log"> --> <!-- <!– 使用定義的日志格式 –> --> <!-- <PatternLayout pattern="${fileLayout}"/> --> <!-- <Policies> --> <!-- <!– 每隔 1 天滾動一次日志,"modulate" 表示即使沒有日志產(chǎn)生,也會在下一天創(chuàng)建新文件 –> --> <!-- <TimeBasedTriggeringPolicy interval="1" modulate="true"/> --> <!-- <!– 當(dāng)日志文件大小達(dá)到指定大小時觸發(fā)滾動 –> --> <!-- <SizeBasedTriggeringPolicy size="${fileSize}"/> --> <!-- </Policies> --> <!-- </RollingFile> --> <!-- 配置JDBC Appender --> <!-- 登錄日志表 --> <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="stack_trace" pattern="%X{stack_trace}"/> --> <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"/> <!-- 數(shù)據(jù)庫源 --> <AppenderRef ref="LoginDatabase" level="LOGIN_LOG"/> <AppenderRef ref="OperationDatabase" level="OPERATION_LOG"/> <AppenderRef ref="ExceptionDatabase" level="EXCEPTION_LOG"/> </Root> <!-- <Logger name="com.cj.config.logs.ConnectionFactory" level="INFO"> --> <!-- <AppenderRef ref="DatabaseAppender" level="INFO"/> --> <!-- </Logger> --> <!-- <Root level="TRACE"> --> <!-- <!– 引用控制臺輸出和文件輸出的 appender –> --> <!-- <AppenderRef ref="Console"/> --> <!-- <AppenderRef ref="DebugAppender"/> --> <!-- <AppenderRef ref="InfoAppender"/> --> <!-- <AppenderRef ref="WarnAppender"/> --> <!-- <AppenderRef ref="ErrorAppender"/> --> <!-- <AppenderRef ref="LoginDatabase"/> --> <!-- <AppenderRef ref="OperationDatabase" /> --> <!-- <!– <AppenderRef ref="ExceptionDatabase" level="Exception"/> –> --> <!-- </Root> --> <Logger name="org.apache.logging.log4j" level="DEBUG" additivity="false"> <AppenderRef ref="Console"/> </Logger> <!--額外配置的logger--> <!--log4j2 自帶過濾日志--> <!-- Apache Tomcat Web服務(wù)器配置加載的日志 --> <Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"> <!-- 僅記錄錯誤級別的日志,通常用于記錄嚴(yán)重的配置錯誤 --> </Logger> <!-- 與 Apache Tomcat 相關(guān)的組件生命周期信息 --> <Logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"> <!-- 僅記錄錯誤級別的信息 --> </Logger> <!-- Apache Tomcat HTTP 協(xié)議處理器的日志記錄 --> <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"> <!-- 記錄警告級別及更高級別的信息,通常用于記錄一般警告信息 --> </Logger> <!-- Apache SSHD(SSH Server)庫的日志記錄 --> <Logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"> <!-- 記錄警告級別及更高級別的信息,通常用于記錄一般警告信息 --> </Logger> <!-- 與 Apache Tomcat 相關(guān)的 NIO 選擇器池的日志記錄 --> <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"> <!-- 記錄警告級別及更高級別的信息 --> </Logger> <!-- 與 CRaSH(Common Reusable SHell)相關(guān)的庫和 SSH 支持的日志記錄 --> <Logger name="org.crsh.plugin" level="WARN"> <!-- 記錄警告級別及更高級別的信息 --> </Logger> <Logger name="org.crsh.ssh" level="WARN"> <!-- 記錄警告級別及更高級別的信息 --> </Logger> <!-- Eclipse Jetty Web服務(wù)器的組件生命周期的日志記錄 --> <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"> <!-- 僅記錄錯誤級別的信息 --> </Logger> <!-- Hibernate Validator相關(guān)日志 --> <Logger name="org.hibernate.validator.internal.util.Version" level="WARN"> <!-- 記錄警告級別及更高級別的信息 --> </Logger> <!-- 與 Spring Boot Actuator 相關(guān)的日志記錄 --> <Logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="WARN"> <!-- 記錄警告級別及更高級別的信息 --> </Logger> <Logger name="org.springframework.boot.actuate.endpoint.jmx" level="WARN"> <!-- 記錄警告級別及更高級別的信息 --> </Logger> <!-- Thymeleaf模板引擎的日志記錄 --> <Logger name="org.thymeleaf" level="WARN"> <!-- 記錄警告級別及更高級別的信息 --> </Logger> </Loggers> </Configuration>
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文件中讀取配置屬性的實(shí)用工具類。 */ public class ConfigReader { private static final Logger logger = LogManager.getLogger(ConfigReader.class); /** * 從YAML配置文件中獲取數(shù)據(jù)源屬性。 * * @return CustomDataSourceProperties對象,包含數(shù)據(jù)源屬性,如果未找到屬性則返回null。 */ public static CustomDataSourceProperties getDataSourceProperties() { String defaultActive = "dev"; // 默認(rèn)活動配置 String defaultConfigFile = "/application.yml"; // 默認(rèn)配置文件名 // 從默認(rèn)配置文件中獲取Spring屬性 Map<String, Object> properties = getSpringProperties(defaultActive, defaultConfigFile); if (properties != null) { Map<String, Object> springProperties = (Map<String, Object>) properties.get("spring"); if (springProperties != null) { Map<String, Object> datasourceProperties = (Map<String, Object>) 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("獲取數(shù)據(jù)源屬性成功!"); logger.info("數(shù)據(jù)源屬性:" + dataSourceProperties); return dataSourceProperties; } } } return null; } /** * 從指定的配置文件中獲取Spring屬性。 * * @param active 要使用的活動配置。 * @param configFile 配置文件的路徑。 * @return 包含Spring屬性的Map,如果未找到屬性則返回null。 */ private static Map<String, Object> getSpringProperties(String active, String configFile) { // 讀取配置文件 Map<String, Object> data = readConfigFile(configFile); if (data != null) { Map<String, Object> springProperties = (Map<String, Object>) data.get("spring"); if (springProperties != null) { Map<String, Object> applicationProperties = (Map<String, Object>) 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數(shù)據(jù)的Map,如果未找到文件或無法解析則返回null。 */ private static Map<String, Object> readConfigFile(String fileName) { try { // 創(chuàng)建用于解析YAML文件的ObjectMapper對象 ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); // 使用類加載器加載YAML文件 InputStream inputStream = ConfigReader.class.getResourceAsStream(fileName); // 讀取YAML文件并解析為Map對象 return mapper.readValue(inputStream, new TypeReference<Map<String, Object>>() { }); } catch (Exception e) { e.printStackTrace(); return null; } } }
5、ConnectionFactory 連接工廠類,用于管理數(shù)據(jù)庫連接
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; /** * 連接工廠類,用于管理數(shù)據(jù)庫連接。通過Apache Commons DBCP提供數(shù)據(jù)庫連接池功能。 */ @Component public class ConnectionFactory { // 數(shù)據(jù)源,用于獲取數(shù)據(jù)庫連接 private final DataSource dataSource; /** * 構(gòu)造函數(shù),初始化數(shù)據(jù)庫連接池配置,并創(chuàng)建數(shù)據(jù)源。 * * @param dataSourceProperties 數(shù)據(jù)源屬性對象,包含數(shù)據(jù)庫連接信息 */ public ConnectionFactory(CustomDataSourceProperties dataSourceProperties) { // 初始化數(shù)據(jù)庫連接池配置 Properties properties = new Properties(); properties.setProperty("user", dataSourceProperties.getUsername()); properties.setProperty("password", dataSourceProperties.getPassword()); // 創(chuàng)建基于DriverManager的連接工廠和連接池 GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<>(); pool.setMaxActive(10); // 設(shè)置最大連接數(shù) pool.setMinIdle(2); // 設(shè)置最小空閑連接數(shù) DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(dataSourceProperties.getUrl(), properties); // 創(chuàng)建可池化的數(shù)據(jù)庫連接工廠 new PoolableConnectionFactory(connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED); // 創(chuàng)建數(shù)據(jù)源 this.dataSource = new PoolingDataSource(pool); } /** * 獲取數(shù)據(jù)庫連接。 * * @return 數(shù)據(jù)庫連接 * @throws SQLException 如果獲取連接時發(fā)生錯誤 */ public static Connection getDatabaseConnection() throws SQLException { return Singleton.INSTANCE.dataSource.getConnection(); } /** * 單例類,確保只有一個連接工廠實(shí)例。 */ private static class Singleton { static ConnectionFactory INSTANCE; static { // TODO 這里需要修改,在創(chuàng)建數(shù)據(jù)庫連接工廠時,需要從配置文件中讀取數(shù)據(jù)源屬性 // 從配置文件中讀取數(shù)據(jù)源屬性,并創(chuàng)建連接工廠實(shí)例 CustomDataSourceProperties dataSourceProperties = ConfigReader.getDataSourceProperties(); dataSourceProperties.setDriverClassName(dataSourceProperties.getDriverClassName()); dataSourceProperties.setUrl(dataSourceProperties.getUrl()); dataSourceProperties.setUsername(dataSourceProperties.getUsername()); dataSourceProperties.setPassword(dataSourceProperties.getPassword()); INSTANCE = new ConnectionFactory(dataSourceProperties); } } }
三、進(jìn)行簡單測試配置
1、LogUtils
import org.apache.logging.log4j.ThreadContext; /** * 日志工具類 */ public class LogUtils { /** * 清除登錄日志上下文信息,以便在日志記錄操作結(jié)束后不保留上下文。 */ public static void clearLogContext() { ThreadContext.clearMap(); } }
2、LoginUserInfoHelper
/** * 用于存儲和獲取用戶信息的線程局部存儲輔助類。 */ public class LoginUserInfoHelper { // 使用 ThreadLocal 來存儲用戶ID和用戶名 private static final ThreadLocal<Long> userId = new ThreadLocal<>(); private static final ThreadLocal<String> userName = new ThreadLocal<>(); /** * 獲取當(dāng)前線程的用戶ID。 * * @return 用戶ID */ public static Long getUserId() { return userId.get(); } /** * 設(shè)置當(dāng)前線程的用戶ID。 * * @param _userId 用戶ID */ public static void setUserId(Long _userId) { userId.set(_userId); } /** * 從當(dāng)前線程中移除用戶ID。 */ public static void removeUserId() { userId.remove(); } /** * 獲取當(dāng)前線程的用戶名。 * * @return 用戶名 */ public static String getUsername() { return userName.get(); } /** * 設(shè)置當(dāng)前線程的用戶名。 * * @param _username 用戶名 */ public static void setUsername(String _username) { userName.set(_username); } /** * 從當(dāng)前線程中移除用戶名。 */ 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); /** * 設(shè)置用戶登錄日志,根據(jù)HttpServletRequest設(shè)置日志上下文信息。 * * @param request 請求對象,用于獲取遠(yuǎn)程地址和用戶代理信息。 * @param remarks 登錄日志的備注信息。 */ public static void setLogLogin(HttpServletRequest request, String remarks) { // 使用 LoginUserInfoHelper 中的用戶信息設(shè)置登錄日志 setLogLogin( LoginUserInfoHelper.getUserId().toString(), LoginUserInfoHelper.getUsername(), request.getRemoteAddr(), RequestUtils.getUserAgent(request), remarks ); } /** * 設(shè)置用戶登錄日志,根據(jù)指定的客戶端IP、設(shè)備信息和備注信息。 * * @param clientIp 客戶端IP地址。 * @param deviceInfo 設(shè)備信息。 * @param remarks 登錄日志的備注信息。 */ public static void setLogLogin(String clientIp, String deviceInfo, String remarks) { // 使用 LoginUserInfoHelper 中的用戶信息設(shè)置登錄日志 setLogLogin( LoginUserInfoHelper.getUserId().toString(), LoginUserInfoHelper.getUsername(), clientIp, deviceInfo, remarks ); } /** * 設(shè)置用戶登錄日志,包含用戶ID、用戶名、客戶端IP、設(shè)備信息和備注信息。 * * @param userId 用戶ID。 * @param userName 用戶名。 * @param clientIp 客戶端IP地址。 * @param deviceInfo 設(shè)備信息。 * @param remarks 登錄日志的備注信息。 */ public static void setLogLogin(String userId, String userName, String clientIp, String deviceInfo, String remarks) { logLogin(userId, userName, clientIp, deviceInfo, remarks); } /** * 設(shè)置用戶登錄日志,包含用戶ID、用戶名、客戶端IP、設(shè)備信息和備注信息。 * * @param userId 用戶ID。 * @param userName 用戶名。 * @param clientIp 客戶端IP地址。 * @param deviceInfo 設(shè)備信息。 * @param remarks 登錄日志的備注信息。 */ public static void logLogin(String userId, String userName, String clientIp, String deviceInfo, String remarks) { try { // 設(shè)置上下文信息,這些信息將與日志消息關(guān)聯(lián) 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塊之后清除上下文,確保上下文信息不泄漏 } } /** * 設(shè)置用戶登錄日志,包含用戶ID、用戶名、客戶端IP、設(shè)備信息和備注信息。 * * @param clientIp 客戶端IP地址。 * @param deviceInfo 設(shè)備信息。 * @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 { // 設(shè)置上下文信息,這些信息將與日志消息關(guān)聯(lián) 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), "登錄成功" );
到此這篇關(guān)于SpringBoot使用log4j2將日志記錄到文件及自定義數(shù)據(jù)庫的文章就介紹到這了,更多相關(guān)SpringBoot使用log4j2內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計代碼實(shí)例
這篇文章主要介紹了Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11如何在Netty中注解使用Service或者M(jìn)apper
這篇文章主要介紹了如何在Netty中注解使用Service或者M(jìn)apper,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02Spring?Boot中常用的參數(shù)傳遞注解示例詳解
這篇文章主要介紹了Spring?Boot中常用的參數(shù)傳遞注解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-11-11java中File轉(zhuǎn)為MultipartFile的四種實(shí)現(xiàn)方式
這篇文章主要介紹了java中File轉(zhuǎn)為MultipartFile的四種實(shí)現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06SpringBoot使用前綴樹過濾敏感詞的方法實(shí)例
Trie也叫做字典樹、前綴樹(Prefix Tree)、單詞查找樹,特點(diǎn):查找效率高,消耗內(nèi)存大,這篇文章主要給大家介紹了關(guān)于SpringBoot使用前綴樹過濾敏感詞的相關(guān)資料,需要的朋友可以參考下2022-01-01詳解基于MVC的數(shù)據(jù)查詢模塊進(jìn)行模糊查詢
這篇文章主要介紹了Java基于MVC的數(shù)據(jù)查詢模塊進(jìn)行模糊查詢,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01java組件commons-fileupload實(shí)現(xiàn)文件上傳
這篇文章主要介紹了java借助commons-fileupload組件實(shí)現(xiàn)文件上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10