当前位置:网站首页>2022-06-07 VI. log implementation
2022-06-07 VI. log implementation
2022-07-28 06:35:00 【Don't like milkshakes】
Log implementation
Preface
Logging is an essential function in a project , Developing 、 test 、 Deploy 、 Online and other environments , Logs can help developers execute programs 、 Troubleshooting helps .
And the log is divided into system log and operation log :
- The system log is to understand the execution process of the application , Like request logs 、 stay if…else… Wait for which branch 、 Print out the information in a certain line of code , Log information helps developers understand the implementation of the system
- Operation logs are generally for users , For example, the intermediate steps of order creation , Where is the current progress 、 Or some information has been added or modified ; This requires the readability of the operation log
So how to implement it in the system ? The simplest operation log is to use log.info(" user {} Shipping address ‘{}’ It is amended as follows ‘{}’ ", userId, oldAddr, newAddr); Output the modification record . Here, the modification information is collected , But for the code, it increases the burden of code readability , Or intrusive . When it comes to log output, you need to call log.xxx(xxxx);, What can be done about it ?
Java Log introduction and implementation in the project
Open log
More commonly used logs Log4j、Log4j2、Logbak, stay SpringBoot Use facade mode to support different logs
SpringBoot Import... In project starter When you depend on , By default Logback journal . It uses facade mode Slf4j To develop the log function interface , The specific log only needs to realize the function , You can switch seamlessly
maven rely on :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
springboot By default logback, Here we use logback Explain , The first is the configuration file
# The default log is info Level , You can specify the log level for a specific path
logging:
level:
com.zsl.service.dao: debug
log Profile name :
| Logging System | Customization |
|---|---|
| Logback | logback-spring.xml, logback-spring.groovy, logback.xml, or logback.groovy |
| Log4j2 | log4j2-spring.xml or log4j2.xml |
| JDK (Java Util Logging) | logging.properties |
Custom configuration ‘logback-spring.xml’:
<?xml version="1.0" encoding="UTF-8"?>
<!-- The log level is from low to high TRACE < DEBUG < INFO < WARN < ERROR < FATAL, such as : If set to WARN, Less than WARN None of our messages will output -->
<!-- scan: When this property is set to true when , If the configuration document changes , Will be reloaded , The default value is true -->
<!-- scanPeriod: Set the time interval between changes in the monitoring configuration document , If no time unit is given , The default unit is milliseconds . When scan by true when , This property takes effect . The default time interval is 1 minute . -->
<!-- debug: When this property is set to true when , Will print out logback Internal log information , Real-time view logback Running state . The default value is false. -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<!-- name The value of is the name of the variable ,value The value of is the value defined by the variable . The value defined by will be inserted into logger In the context of . After the definition , You can make “${}” To use variables . -->
<property name="log.path" value="E:/logs"/>
<!--0. Log format and color rendering -->
<!-- Color log depends on rendering class -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- Color log format -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--1. Output to console -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- This log appender It's for development , Configure only the lowest level , The console output log level is greater than or equal to this level of log information -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- Set character set -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--2. Output to document -->
<!-- 2.1 level by DEBUG journal , Time scrolling output -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- The path and document name of the log document being recorded -->
<file>${log.path}/debug.log</file>
<!-- Log document output format -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- Set character set -->
</encoder>
<!-- Rolling policy for loggers , By date , Record by size -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- Log filing -->
<fileNamePattern>${log.path}/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1000MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- Log document retention days -->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- This log document only records debug Grade -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.2 level by INFO journal , Time scrolling output -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- The path and document name of the log document being recorded -->
<file>${log.path}/info.log</file>
<!-- Log document output format -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- Rolling policy for loggers , By date , Record by size -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- Daily log archive path and format -->
<fileNamePattern>${log.path}/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1000MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- Log document retention days -->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- This log document only records info Grade -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.3 level by WARN journal , Time scrolling output -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- The path and document name of the log document being recorded -->
<file>${log.path}/warn.log</file>
<!-- Log document output format -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- Set character set here -->
</encoder>
<!-- Rolling policy for loggers , By date , Record by size -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1000MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- Log document retention days -->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- This log document only records warn Grade -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.4 level by ERROR journal , Time scrolling output -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- The path and document name of the log document being recorded -->
<file>${log.path}/error.log</file>
<!-- Log document output format -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- Set character set here -->
</encoder>
<!-- Rolling policy for loggers , By date , Record by size -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- Log document retention days -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<!-- This log document only records ERROR Grade -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.5 all except DEBUG Other levels are higher than DEBUG Of journal , Record to a file -->
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- The path and document name of the log document being recorded -->
<file>${log.path}/all.log</file>
<!-- Log document output format -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- Set character set here -->
</encoder>
<!-- Rolling policy for loggers , By date , Record by size -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/all-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1000MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- Log document retention days -->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- This log document records except DEBUG Other levels are higher than DEBUG Of -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
</appender>
<!-- <logger> Used to set the log printing level of a package or a specific class 、 And the designation <appender>.<logger> There is only one name attribute , An optional one level And an optional addtivity attribute . name: Used to designate the recipient logger A package of constraints or a specific class . level: Used to set the print level , Case is irrelevant :TRACE, DEBUG, INFO, WARN, ERROR, ALL and OFF, There is also a special value INHERITED Or synonyms NULL, Represents the level of enforcement superior . If this property is not set , Then the current logger Will inherit the level of the superior . addtivity: Whether to report to the superior logger Transfer printed information . The default is true. <logger name="org.springframework.web" level="info"/> <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/> -->
<!-- Use mybatis When ,sql The sentence is debug Next will print , And here we only configure info, So I want to see sql In words , There are two operations : The first one <root level="info"> Change to <root level="DEBUG"> This will print sql, But there will be a lot of other news in the log The second is to give it alone dao Directory configuration debug Pattern , The code is as follows , This configuration sql The statement will print , Others are normal info Level : 【logging.level.org.mybatis=debug logging.level.dao=debug】 -->
<!-- root Nodes are required , Used to specify the most basic log output level , only one level attribute level: Used to set the print level , Case is irrelevant :TRACE, DEBUG, INFO, WARN, ERROR, ALL and OFF, Cannot be set to INHERITED Or synonyms NULL. The default is DEBUG Can contain zero or more elements , Identify this appender Will be added to this logger. -->
<!-- 4 The ultimate strategy : Basic strategy (root level ) + according to profile When it starts , logger Customized in the label package The level of logging ( Priority is higher than the above root level )-->
<springProfile name="dev">
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="ALL_FILE" />
</root>
<logger name="com.zsl" level="debug"/> <!-- development environment , Specify a package log as debug level -->
</springProfile>
<springProfile name="zsl_test">
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="ALL_FILE" />
</root>
<logger name="com.zsl" level="debug"/> <!-- Test environment , Specify a package log as info level -->
<!-- <logger name="com.zsl" level="info"/>--> <!-- Test environment , Specify a package log as info level -->
</springProfile>
<springProfile name="prod">
<root level="info">
<!-- The production environment should not be configured console Writing documents -->
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="ALL_FILE" />
</root>
<logger name="com.zsl" level="warn"/> <!-- Production environment , Specify a package log as warn level -->
<logger name="com.zsl.MyApplication" level="info"/> <!-- Print a specific class info journal , such as application Prompt after successful startup -->
</springProfile>
</configuration>
Log implementation
system log
system log , In order to understand the execution process , use Interceptor Log collection of requests in the form of , For the execution process of configuration , have access to log.info() Log output .
At present, only the prototype has been realized, and the follow-up will be continuously improved every day
Package structure :

Use here interceptor Realization :
package com.zsl.custombox.log.core.interceptor;
import com.zsl.custombox.common.util.SecurityContextHolder;
import com.zsl.custombox.common.util.ServletUtil;
import com.zsl.custombox.common.model.log.SystemLogContext;
import com.zsl.custombox.common.util.SystemLogContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/** * Access records ( system log ) * Realization @LogRecord Annotation implements operation log * * @Author zsl * @Date 2022/5/22 14:55 * @Email [email protected] */
public class AccessLogInterceptor implements HandlerInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Create a global logging context todo get data ( Implement tool class )
SystemLogContext systemLogContext = new SystemLogContext()
.setUserId(SecurityContextHolder.getAuth().getUserId())
.setRequestNo(0L)// You can use the snowflake algorithm to get 64 Only one id
.setIp(ServletUtil.getIp())
.setUri(request.getRequestURI())
.setMethod(request.getMethod())
.setStartTime(new Date(System.currentTimeMillis()));
// Store global logging context
SystemLogContextHolder.set(systemLogContext);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
SystemLogContext systemLogContext = SystemLogContextHolder.get();
systemLogContext.setRespTime(System.currentTimeMillis() - systemLogContext.getStartTime().getTime());
// format log
StringBuilder requestStr = new StringBuilder();
List<Object> requestArgs = new ArrayList<>();
requestStr.append("\n=========================== AccessLog ===========================\n");
requestStr.append(String.format(" %-10s: {}\n", "userId"));
requestArgs.add(systemLogContext.getUserId());
requestStr.append(String.format(" %-10s: {}\n", "requestNo"));
requestArgs.add(systemLogContext.getRequestNo());
requestStr.append(String.format(" %-10s: {}\n", "ip"));
requestArgs.add(systemLogContext.getIp());
requestStr.append(String.format(" %-10s: {}\n", "uri"));
requestArgs.add(systemLogContext.getUri());
requestStr.append(String.format(" %-10s: {}\n", "method"));
requestArgs.add(systemLogContext.getMethod());
requestStr.append(String.format(" %-10s: {}\n", "startTime"));
requestArgs.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(systemLogContext.getStartTime()));
requestStr.append(String.format(" %-10s: {} ms\n", "respTime"));
requestArgs.add(systemLogContext.getRespTime());
requestStr.append(String.format(" %-10s: {}\n", "respCode"));
requestArgs.add(systemLogContext.getRespCode());
requestStr.append(String.format(" %-10s: {}\n", "respMsg"));
requestArgs.add(systemLogContext.getRespMsg());
requestStr.append("=================================================================\n");
// todo Log in
log.info(requestStr.toString(), requestArgs.toArray());
// clear ThreadLocal
SystemLogContextHolder.clear();
}
}
Logging global context :
package com.zsl.custombox.common.model.log;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.Map;
/** * Logging context * * @Author zsl * @Date 2022/5/22 16:48 * @Email [email protected] */
@Data
@Accessors(chain = true)
public class SystemLogContext {
// =========================== request data ===========================
// user id( Anonymous as 0)
private Long userId;
// Request number , Ensure that the request number does not change when the request is called
private Long requestNo;
// ip Address
private String ip;
// Unified resource interface uri
private String uri;
// Parameters
// private String params;
// Request method (GET、POST...)
private String method;
// Request time
private Date startTime;
// =========================== response data ===========================
// response time
private Long respTime;
// Response code
private Integer respCode;
// Response information
private String respMsg;
// Response body
// private String respBody;
}
Logging context tool class :
package com.zsl.custombox.log.core.util;
import com.zsl.custombox.log.core.model.LogRecordContext;
/** * Thread safety logging * * @Author zsl * @Date 2022/5/22 16:57 * @Email [email protected] */
public class LogRecordContextHolder {
private static final ThreadLocal<LogRecordContext> LOG_RECORD_CONTEXT = new ThreadLocal<>();
public static LogRecordContext get() {
return LOG_RECORD_CONTEXT.get();
}
public static void set(LogRecordContext logRecordContext) {
LOG_RECORD_CONTEXT.set(logRecordContext);
}
public static void clear() {
LOG_RECORD_CONTEXT.remove();
}
}
Automatic configuration class :
package com.zsl.custombox.log.config;
import com.zsl.custombox.log.core.interceptor.AccessLogInterceptor;
import com.zsl.custombox.log.core.model.logrecord.LogRecordOperationSource;
import com.zsl.custombox.log.core.service.record.DefaultLogRecordServiceImpl;
import com.zsl.custombox.log.core.service.record.ILogRecordService;
import com.zsl.custombox.log.core.service.function.DefaultFunctionServiceImpl;
import com.zsl.custombox.log.core.service.function.IFunctionService;
import com.zsl.custombox.log.core.service.function.IParseFunction;
import com.zsl.custombox.log.core.service.function.ParseFunctionFactory;
import com.zsl.custombox.log.core.service.operator.DefaultOperatorGetServiceImpl;
import com.zsl.custombox.log.core.service.operator.IOperatorGetService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/** * Log auto configuration ,AccessLogInterceptor( Access log )、LogRecordAspect( The operation log ) * * @Author zsl * @Date 2022/5/22 21:04 * @Email [email protected] */
@Configuration
public class LogAutoConfiguration implements WebMvcConfigurer {
// ========================== Interceptor ==========================
@Bean
public AccessLogInterceptor accessLogInterceptor() {
return new AccessLogInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.accessLogInterceptor());
}
// ========================== Operation log container ==========================
// Processing annotation information operation source
@Bean
public LogRecordOperationSource logRecordOperationSource () {
return new LogRecordOperationSource();
}
// Custom function
@Bean
@ConditionalOnMissingBean(IFunctionService.class)
public IFunctionService functionService(ParseFunctionFactory parseFunctionFactory) {
return new DefaultFunctionServiceImpl(parseFunctionFactory);
}
@Bean
public ParseFunctionFactory parseFunctionFactory(@Autowired List<IParseFunction> list) {
return new ParseFunctionFactory(list);
}
@Bean
@ConditionalOnMissingBean(IParseFunction.class)
public IParseFunction defaultParseFunction() {
return new IParseFunction() {
@Override
public String functionName() {
return null;
}
@Override
public String apply(String value) {
return null;
}
};
}
// Persistence
@Bean
@ConditionalOnMissingBean(ILogRecordService.class)
public ILogRecordService logRecordService() {
return new DefaultLogRecordServiceImpl();
}
// Operator
@Bean
@ConditionalOnMissingBean(IOperatorGetService.class)
public IOperatorGetService operatorGetService() {
return new DefaultOperatorGetServiceImpl();
}
}
The operation log
Use AOP+ How to annotate , Conduct operation log :
/** * @Author zsl * @Date 2022/5/23 23:20 * @Email [email protected] */
@Aspect
@Component
public class LogRecordAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
ILogRecordService logRecordService;
@Autowired
IFunctionService functionService;
LogRecordValueParser logRecordValueParser = new LogRecordValueParser(new LogRecordExpressionEvaluator(), functionService);
@Around("@annotation(com.zsl.custombox.log.core.annotation.LogRecord)")
public Object logRecord(ProceedingJoinPoint point) throws Throwable {
return execute(point, point.getTarget().getClass(), ((MethodSignature) point.getSignature()).getMethod(), point.getArgs());
}
/** * v0.0.1 edition Easy to use SpEL analysis ( In later versions, it is provided to parse variables in a functional way ) */
private Object execute(ProceedingJoinPoint point, Class<?> targetClass, Method method, Object[] args) throws Throwable {
Object ret = null;
LogRecordContext.putEmptySpan();
MethodExceptionResult exceptionResult = new MethodExceptionResult(true, null, "");
// Split annotation information is stored as List Waiting for execution
// perform List Custom functions in
String spEL = getAnnotation(method).content();
try {
ret = point.proceed();
} catch (Throwable throwable) {
exceptionResult = new MethodExceptionResult(false, throwable, throwable.getMessage());
}
// analysis List in SpEL
try {
// Store logs
if (Strings.isNotBlank(spEL)) {
recordExecute(ret, method, args, spEL, targetClass,
exceptionResult.isSuccess(), exceptionResult.getErrorMsg());
}
} catch (Throwable t) {
log.error(" Failed to record operation log , Does not affect business execution !", t);
} finally {
// clear Context
LogRecordContext.clear();
}
// If the execution fails, an exception will be thrown
if (!exceptionResult.isSuccess()) {
throw exceptionResult.getThrowable();
}
return ret;
}
private void recordExecute(Object ret, Method method, Object[] args, String spEL, Class<?> targetClass, boolean success, String errorMsg) {
// Create context
EvaluationContext evaluationContext = logRecordValueParser.createEvaluationContext(method, args, ret, errorMsg);
// After obtaining the evaluation expressionString
String expression = logRecordValueParser.getExpression(spEL, new AnnotatedElementKey(method, targetClass), evaluationContext);
// Persistent operation log
logRecordService.record(new com.zsl.custombox.log.core.model.logrecord.LogRecord(expression));
}
/** * Access method */
private Method getMethod(ProceedingJoinPoint point) {
return ((MethodSignature) point.getSignature()).getMethod();
}
/** * Get comments */
private LogRecord getAnnotation(Method method) {
LogRecord annotation = method.getAnnotation(LogRecord.class);
return annotation;
}
}
example :
public class XxxxServiceImpl {
@LogRecord(content = "' Change the user name to ' + #userParam.username")
public int updateUser(UserParam userParam) {
return mapper.updateUser(userParam);
}
}
Finally record the operation log : Change the user name to xxx
This is just a simple version , The annotation parsing will be improved later , Expected effect " user xxx Change user name ‘aaa’ by ‘bbb’ "
Later, we will use SpEL Realize the improvement of log items and submit them to Github in , Welcome to your attention ~
https://github.com/zsl0/costom_box
Related articles
边栏推荐
猜你喜欢
随机推荐
Vscode中,无法打开源文件 “Adafruit_GFX.h“
qt自定义滑动按钮(美观且使用方便)
What's a gift for girls on Chinese Valentine's day? Selfie online and thoughtful gift recommendation
QT parse string into JSON data and parse
What about the insufficient memory of Clickhouse aggregation? Then improve the polymerization performance
Matlab simulation of radar imaging 1 - LFM signal and its spectrum
2022-07-17 达梦数据库安装
2022-07-19 达梦数据库 连接实例、执行脚本、系统命令
JSP should pass parameters to the background while realizing the file upload function
pyppeteer 下拉 selenium下拉
MySQL安装与使用
mysql join技巧
自定义组件--父子组件之间的通信
QT custom sliding button (beautiful and easy to use)
Exploration of Clickhouse aggregation internal mechanism of aggregation
小程序navigator无法跳转(debug)
The startup fails when switching Chinese when using wampserver3.2.6
Problems of font modification and line spacing in word automatic directory
npm yarn相关的操作
Listener









