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。

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions> <!-- 去掉springboot默认配置 --><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 标签用于配置 Log4j2,其中 status 属性设置为 "WARN",表示仅记录警告级别的日志用于运行时状态。monitorInterval 属性设置为 "30",表示每隔 30 秒自动检查配置文件更改。
-->
<Configuration status="WARN" monitorInterval="30"><Properties><!--定义名为 "baseDir" 的属性,用于指定日志文件的基本目录。其他地方可以使用 "${baseDir}" 引用这个值。--><Property name="baseDir">./logs</Property><!--定义名为 "logPattern" 的属性,用于指定日志的格式,以便在后续配置中引用。--><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" 表示即使没有日志产生,也会在下一天创建新文件 --><TimeBasedTriggeringPolicy interval="1" modulate="true"/><!-- 当日志文件大小达到指定大小时触发滚动 --><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" 表示即使没有日志产生,也会在下一天创建新文件 --><TimeBasedTriggeringPolicy interval="1" modulate="true"/><!-- 当日志文件大小达到指定大小时触发滚动 --><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" 表示即使没有日志产生,也会在下一天创建新文件 --><TimeBasedTriggeringPolicy interval="1" modulate="true"/><!-- 当日志文件大小达到指定大小时触发滚动 --><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" 表示即使没有日志产生,也会在下一天创建新文件 --><TimeBasedTriggeringPolicy interval="1" modulate="true"/><!-- 当日志文件大小达到指定大小时触发滚动 --><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"> --><!--     &lt;!&ndash; 使用定义的日志格式 &ndash;&gt; --><!--     <PatternLayout pattern="${fileLayout}"/> --><!--     <Policies> --><!--         &lt;!&ndash; 每隔 1 天滚动一次日志,"modulate" 表示即使没有日志产生,也会在下一天创建新文件 &ndash;&gt; --><!--         <TimeBasedTriggeringPolicy interval="1" modulate="true"/> --><!--         &lt;!&ndash; 当日志文件大小达到指定大小时触发滚动 &ndash;&gt; --><!--         <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"/><!-- 数据库源 --><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"> --><!--     &lt;!&ndash; 引用控制台输出和文件输出的 appender &ndash;&gt; --><!--     <AppenderRef ref="Console"/> --><!--     <AppenderRef ref="DebugAppender"/> --><!--     <AppenderRef ref="InfoAppender"/> --><!--     <AppenderRef ref="WarnAppender"/> --><!--     <AppenderRef ref="ErrorAppender"/> --><!--     <AppenderRef ref="LoginDatabase"/> --><!--     <AppenderRef ref="OperationDatabase" /> --><!--     &lt;!&ndash; <AppenderRef ref="ExceptionDatabase" level="Exception"/> &ndash;&gt; --><!-- </Root> --><Logger name="org.apache.logging.log4j" level="DEBUG" additivity="false"><AppenderRef ref="Console"/></Logger><!--额外配置的logger--><!--log4j2 自带过滤日志--><!-- Apache Tomcat Web服务器配置加载的日志 --><Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"><!-- 仅记录错误级别的日志,通常用于记录严重的配置错误 --></Logger><!-- 与 Apache Tomcat 相关的组件生命周期信息 --><Logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"><!-- 仅记录错误级别的信息 --></Logger><!-- Apache Tomcat HTTP 协议处理器的日志记录 --><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 相关的 NIO 选择器池的日志记录 --><Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"><!-- 记录警告级别及更高级别的信息 --></Logger><!-- 与 CRaSH(Common Reusable SHell)相关的库和 SSH 支持的日志记录 --><Logger name="org.crsh.plugin" level="WARN"><!-- 记录警告级别及更高级别的信息 --></Logger><Logger name="org.crsh.ssh" level="WARN"><!-- 记录警告级别及更高级别的信息 --></Logger><!-- Eclipse Jetty Web服务器的组件生命周期的日志记录 --><Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"><!-- 仅记录错误级别的信息 --></Logger><!-- Hibernate Validator相关日志 --><Logger name="org.hibernate.validator.internal.util.Version" level="WARN"><!-- 记录警告级别及更高级别的信息 --></Logger><!-- 与 Spring Boot Actuator 相关的日志记录 --><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文件中读取配置属性的实用工具类。*/
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<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("获取数据源属性成功!");logger.info("数据源属性:" + 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数据的Map,如果未找到文件或无法解析则返回null。*/private static Map<String, Object> 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<Map<String, Object>>() {});} 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<PoolableConnection> 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<Long> userId = new ThreadLocal<>();private static final ThreadLocal<String> 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),"登录成功");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/748212.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Numpy】(1)创建数组

NumPy 是 Python 的一个开源数值计算扩展库,用于高性能的科学计算和数据分析。它提供了一个强大的N维数组对象ndarray,以及用于对这些数组进行操作的各种函数。 通过pip安装: pip install numpy创建数组 从列表创建 numpy.array可以通过多种方式创建,最直接的方式是使用…

Pulsar 社区周报 | No.2024.03.15 LTS 3.0.3 新版发布

“ 各位热爱 Pulsar 的小伙伴们&#xff0c;Pulsar 社区周报更新啦&#xff01;这里将记录 Pulsar 社区每周的重要更新&#xff0c;周五发布。 ” 本期主题&#xff1a;LTS 3.0.3 发布 Apache Pulsar 新版发布&#xff1a; LTS&#xff08;稳定性长期支持版&#xff09;新版&am…

mysql统计数据库大小

ps:亲测可行,时间2024-03-15 15:18 mysql统计数据库大小 要统计MySQL数据库的大小&#xff0c;你可以使用以下SQL查询&#xff1a; SELECT table_schema AS "Database",ROUND(SUM(data_length index_length) / 1024 / 1024, 2) AS "Size (MB)"FROM info…

SpringMVC基础之工作流程

文章目录 SpringMVC 的工作流程1. 总图2. DispatcherServlet3. 必需的配置4. 加载配置文件的两个时机5. 定义控制器6. 创建 JSP 视图 SpringMVC 的工作流程 1. 总图 如上图&#xff0c;Spring MVC 程序的完整执行流程如下&#xff1a; 用户通过浏览器发送请求&#xff0c;请求…

Docker 系列2【docker安装mysql】【开启远程连接】

文章目录 前言开始步骤1.增加mysql挂载目录2.下载镜像2.启动容器具体步骤4.无法连接5.测试连接 总结 前言 本文开始&#xff0c;默认已经安装docker&#xff0c;如果你还没有完成这个步骤&#xff0c;请查看这一篇文章【docker安装与使用】 开始步骤 1.增加mysql挂载目录 m…

【Unity】进度条和血条的三种做法

前言 在使用Unity开发的时候&#xff0c;进度条和血条是必不可少的&#xff0c;本篇文章将简单介绍一下几种血条的制作方法。 1.使用Slider Slider组件由两部分组成&#xff1a;滑动区域和滑块。滑动区域用于显示滑动条的背景&#xff0c;而滑块则表示当前的数值位置。用户可…

HQL 55 题【持续更新】

前言 今天开始为期一个多月的 HQL 练习&#xff0c;共 55 道 HQL 题&#xff0c;大概每天两道&#xff0c;从初级函数到中级函数。这次的练习不再是基础的 join 那种通用 SQL 语法了&#xff0c;而是引入了更多 Hive 的函数&#xff08;单行函数、窗口函数等&#xff09;。 我…

双线性插值缩放算法原理以及matlab与verilog的实现(二)

系列文章目录 双线性插值缩放算法原理以及matlab与verilog的实现&#xff08;一&#xff09; 文章目录 系列文章目录前言一、前提回顾二、FPGA实现步骤2.1 找到源图像四个像素点求目标像素点2.2 FPGA实现步骤2.3 总体框架2.4 ROM缓存模块2.5 VGA模块2.6 双线性算法模块 三、下…

mysql实战开发之 mysql 删除一张表某个字段的sql语句

有一张表, 我需要删除这张表其中的某一个或者某几个字段, 相信大家在日常开发中应该会遇到这种情况, 然后刚好自己接触的项目安装的mysql关闭了允许远程连接的设置, 也就是说不允许使用类似于navicat 等可视化工具连接, 那么就没办法通过可视化工具直接去通过鼠标操作就可以 完…

Lua 学习

参照 注释 -- 这是单行注释--[[这是多行注释--]]if语句 if true thenprint(true) endif else语句 nil是false if nil thenprint("nil被当作true处理") elseprint("nil被当作false处理") end运算符 % 取余 ^ 乘幂 A10,A^2100 // 整除运算符&#xff0…

【 JS 】闭包

“在黑暗的时刻&#xff0c;不要忘记你内心的光芒&#xff0c;它将指引你找到前进的方向&#xff0c;迈向胜利的辉煌。” 定义 MDN 对闭包的定义为&#xff1a; 闭包是指那些能够访问自由变量的函数。 那什么是自由变量呢&#xff1f; 自由变量是指在函数中使用的&#xff0c;…

linux系统docker容器的容器卷

容器卷 容器卷宿主机和容器之间映射添加容器卷读写规则映射添加卷的继承和共享 容器卷 挂载容器卷加上--privileged参数避免权限原因报错 --privilegedturn //开启权限Docker挂载主机目录访问如果出现cannot open directory .: Permission denied 解决办法&#xf…

【MySQL 系列】MySQL 语句篇_DDL 语句

DDL&#xff08; Data Definition Language&#xff0c;数据定义语言&#xff09;用在定义或改变表的结构数据类型、表之间的链接和约束等初始化工作上。常用的语句关键字包括 CREATE、 DROP、 ALTER 等。 文章目录 1、MySQL 中的 DQL 语句2、MySQL 中库表的 DQL 语句详解2.1、…

【大数据面试题】014 Flink CDC 用过吗,请简要描述

一步一个脚印&#xff0c;一天一道面试题。 完成比完美更重要&#xff0c;明天更新完&#xff01; Flink CDC 的诞生背景 Flink CDC 的全称是 Change Data Capture&#xff08;变更数据捕获&#xff09; 每一项技术的诞生都是为了解决某个问题&#xff0c;某个痛点。而 Flink…

【算法与数据结构】深入解析二叉树(二)之堆结构实现

文章目录 &#x1f4dd;二叉树的顺序结构及实现&#x1f320; 二叉树的顺序结构&#x1f320; 堆的实现&#x1f320; 堆的实现&#x1f309;堆向下调整算法&#x1f309;堆的创建&#x1f309;建堆时间复杂度&#x1f309;堆的插入&#x1f309;堆的删除 &#x1f320;堆向上调…

mysql判断指定字符串在字符串中是否存在

相关函数有&#xff1a;locate、FIND_IN_SET。这两者都可以用来在字符串中查找子串&#xff0c;均为两个参数。 LOCATE&#xff1a;主要是针对普通的字符串查找&#xff0c;参数1在参数2中是否存在&#xff0c;若存在则返回首次出现的索引位置&#xff08;第一个位置索引为1&am…

详细分析Java中Stream流和for循环的差异之处

目录 前言1. 基本知识2. Demo 前言 事情起因是遍历大数据的时候&#xff0c;数据卡顿很严重 对于Java的基本知识推荐阅读&#xff1a;java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; 1. 基本知识 在Java中&#xff0c;Stream API提供…

dockers拉取MySQL及Redis并挂载文件

目录 一 . MySQL拉取 1、进入 MySQL 容器内部。 2、登录 MySQL。 3、修改远程连接 4、刷新 二 . Redis拉取 1 . redis/conf中新建文件redis.conf&#xff0c;内容如下&#xff1a; 2 . 容器运行 一 . MySQL拉取 docker run -d --restartalways --name mysql \ -v /…

hive逗号分割行列转换

select * from ( select back_receipt_nos,order_no,reject_no from ods_one.ods_us_wms_reject_order_match_all_d where order_no 10150501385980001 ) t1 lateral view explode(split(t1.back_receipt_nos, ,)) t as back_receipt_no where 1 1;

19 OpenCV 霍夫曼变换检测圆

文章目录 cv::HoughCircles算子参数示例 cv::HoughCircles 因为霍夫圆检测对噪声比较敏感&#xff0c;所以首先要对图像做中值滤波。 基于效率考虑&#xff0c;Opencv中实现的霍夫变换圆检测是基于图像梯度的实现&#xff0c;分为两步&#xff1a; 检测边缘&#xff0c;发现可能…