根据甲方规定,基于用户操作行为的日志采集的相关技术要求,对用户登录应用系统和资源库后查询、新增、修改、删除信息资源等操作的事件记录。
针对记录用户操作日志,面向切面编程无疑是最好的解决方法:
import com.shxp.project.sys.constants.FunctionModuleEnum;
import com.shxp.project.sys.constants.OperateTypeEnum;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogTail {
/**
* 方法名称
*/
String name();
/**
* 所属模块
*/
FunctionModuleEnum module();
/**
* 方法的操作类型
*/
OperateTypeEnum operateType();
}
根据项目中的模块创建所属模块
/**
* 方法所属模块
*/
public enum FunctionModuleEnum {
STATISTICS("统计分析"),
DATA_MANAGEMENT("数据管理"),
SYSTEM("系统配置")
;
private String name;
FunctionModuleEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
根据甲方文档要求创建接口操作类型枚举
/**
* 接口操作类型
*/
public enum OperateTypeEnum {
LOG_IN(0, "登录"),
QUERY(1, "查询"),
ADD(2, "新增"),
UPDATE(3, "更新"),
DELETE(4, "删除"),
LOG_OUT(5, "退出"),
;
private Integer code;
private String title;
OperateTypeEnum(Integer code, String title) {
this.code = code;
this.title = title;
}
public Integer getCode() {
return code;
}
public String getTitle() {
return title;
}
public static OperateTypeEnum getByCode(Integer code) {
for (OperateTypeEnum value : values()) {
if (value.code.equals(code)) {
return value;
}
}
return null;
}
}
/**
* 相关常量
*/
public class Constants {
/**
* 分隔符
*/
public static final String SEPARATOR = "|";
/**
* 换行符
*/
public static final String LINE = "\n";
}
import com.alibaba.fastjson.JSON;
import com.google.common.base.Joiner;
import com.shxp.project.common.KoalClient;
import com.shxp.project.common.LogUtils;
import com.shxp.project.common.entity.vo.UserMessage;
import com.shxp.project.common.util.DateUtils;
import com.shxp.project.common.util.StringTools;
import com.shxp.project.common.util.UserUtil;
import com.shxp.project.common.util.Util;
import com.shxp.project.shiro.entity.ShiroUserEntity;
import com.shxp.project.sys.annotation.LogParam;
import com.shxp.project.sys.annotation.LogTail;
import com.shxp.project.sys.constants.Constants;
import com.shxp.project.sys.constants.DeptEnum;
import com.shxp.project.sys.entity.LogBean;
import com.shxp.project.sys.mapper.LogMapper;
import com.shxp.project.sys.service.LogRecordService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 日志切面
*/
@Aspect
@Component
public class LogTailAspect {
private static final Logger LOG = LoggerFactory.getLogger("operate");
private static Field[] fields;
@Resource
private LogRecordService logRecordService;
@Value("${product.id:310000000000}")
private String productId;
@Value("${product.name:机场打击整治非法客运管理}")
private String productName;
@Pointcut("execution(* com.shxp.project.*.controller..*.*(..))")
public void excudeService() {
}
static {
Class<LogBean> clazz = LogBean.class;
fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
}
}
@Around("@annotation(com.shxp.project.sys.annotation.LogTail)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Date start = new Date();
Object object = joinPoint.proceed();
Date end = new Date();
execute(joinPoint, object, start, end);
return object;
}
private void execute(ProceedingJoinPoint joinPoint, Object result, Date start, Date end) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
MethodSignature methodSignature = Optional.of(joinPoint)
.map(ProceedingJoinPoint::getSignature)
.map(e -> (MethodSignature) e)
.orElse(null);
LogTail logTail = Optional.of(methodSignature)
.map(MethodSignature::getMethod)
.map(e -> e.getAnnotation(LogTail.class))
.orElse(null);
Method method = methodSignature.getMethod();
Annotation[][] annotations = method.getParameterAnnotations();
Class[] classes = methodSignature.getParameterTypes();
String[] names = methodSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
LogBean logBean = new LogBean();
/**
* 获取用户信息
*/
UserMessage user = getUser(names, args);
if (Objects.nonNull(user)) {
logBean.setOperatorID(user.getUser_id()+"");
logBean.setOperatorAccount(user.getUser_id()+"");
logBean.setOperatorName(user.getUser_name());
logBean.setOrganizationID(user.getOrg_code());
logBean.setOrganizationName(user.getFull_name());
}else{
ShiroUserEntity currentUser = UserUtil.getCurrentUser();
assert currentUser != null;
logBean.setOperatorID(currentUser.getId()+"");
logBean.setOperatorAccount(currentUser.getUserAccount()+"");
logBean.setOperatorName(currentUser.getName());
DeptEnum deptEnum = DeptEnum.getByCode(currentUser.getDept());
if (deptEnum!=null){
logBean.setOrganizationID(deptEnum.getOrg());
logBean.setOrganizationName(deptEnum.getTitle());
}
}
logBean.setProductID(productId);
logBean.setProductName(productName);
logBean.setLogID(UUID.randomUUID().toString());
logBean.setOperatorIdentity(1);
logBean.setOpTime(DateUtils.format(start, DateUtils.DEFAULT_DATE_PATTERN14));
logBean.setResTime(DateUtils.format(end, DateUtils.DEFAULT_DATE_PATTERN14));
logBean.setTerminalType(0);
logBean.setOpType(logTail.operateType().getCode());
logBean.setClientIp(request.getRequestURL().toString());
logBean.setClientPort(Objects.isNull(request.getRemotePort())?null:String.valueOf(request.getRemotePort()));
logBean.setURL(request.getRequestURI());
// logBean.setObjectParams();
logBean.setSessionID(request.getRequestedSessionId());
logBean.setFuncModuleName(logTail.module().getName()+"-"+logTail.name());
logBean.setObjectIP(request.getRequestURL().toString());
logBean.setObjectPort(Objects.isNull(request.getRemotePort())?null:String.valueOf(request.getRemotePort()));
// logBean.setOperateCondition();
logBean.setOperateCondition(getRequestParam(joinPoint));
// logBean.setTerminalID();
// logBean.setTerminalMac();
// logBean.setQuerySql();
String log = Stream.of(fields).map(e -> {
try {
return e.get(logBean);
} catch (IllegalAccessException ex) {
return "";
}
}).map(Util::getString).collect(Collectors.joining(Constants.SEPARATOR));
LogUtils.insertLog(log);
logRecordService.insertLog(logBean);
}
/**
* 从参数列表中获取userId参数,返回用户信息
*
* @param names
* @param args
* @return
*/
private UserMessage getUser(String[] names, Object[] args) {
UserMessage user = null;
for (int i = 0; i < names.length; i++) {
if ("userId".equals(names[i]) && args.length >= i+1) {
String userId = StringTools.getStringValue(args[i]);
List<UserMessage> userMessages = KoalClient.queryUserByIdcard(userId);
if (!CollectionUtils.isEmpty(userMessages)){
user = userMessages.get(0);
}
}
}
return user;
}
private String getRequestParam(JoinPoint pjp){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
if (HttpMethod.PUT.name().equals(request.getMethod()) || HttpMethod.POST.name().equals(request.getMethod())){
return getBodyContent(pjp);
} else {
Map<String,Object> map = getParamContent(request.getParameterMap());
return JSON.toJSONString(map);
}
}
private Map<String,Object> getParamContent(Map<String, String[]> params) {
Map<String,Object> resultMap = new HashMap<>();
try {
for (Map.Entry<String, String[]> entry : params.entrySet()) {
Object value = new Object();
if (entry.getValue() != null && entry.getValue().length == 1) {
value = entry.getValue()[0];
} else {
value = entry.getValue();
}
resultMap.put(entry.getKey(),value);
}
} catch (Exception e) {
LOG.error("日志记录解析param参数失败",e.getMessage());
resultMap.put("paramParse","参数解析失败");
}
return resultMap;
}
private String getBodyContent(JoinPoint pjp) {
String bodyContent = "";
try {
Object[] args = pjp.getArgs();
List arguments = new ArrayList();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments.add(args[i]);
}
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(arguments) && arguments.size() == 1) {
bodyContent = JSON.toJSONString(arguments.get(0));
} else {
bodyContent = JSON.toJSONString(arguments);
}
}catch (Exception e) {
LOG.error("日志记录解析body参数失败",e);
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("paramParse","参数解析失败");
}
return bodyContent;
}
}
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
/**
* 日志文件存储位置
* 日志保存成文本(log)格式,各数据之间通过 “|” 进行分割;存储位置为:
* Linux: /etc/log_audit/operator_logs
* Windows: C:/log_audit/operator_logs
*/
public class LogUtils {
public static void insertLog(String content){
String nowDay = DateUtil.getNowDay();
String dir="C:/log_audit/operator_logs";
String os = System.getProperty("os.name");
//Windows操作系统
if (os != null && os.toLowerCase().startsWith("windows")) {
dir="C:/log_audit/operator_logs";
} else if (os != null && os.toLowerCase().startsWith("linux")) {//Linux操作系统
dir="/etc/log_audit/operator_logs";
}
File fDir = new File(dir);
if (!fDir.exists()) {
fDir.mkdirs();
}
FileOutputStream outSTr = null;
BufferedOutputStream Buff = null;
StringBuffer write ;
try {
outSTr = new FileOutputStream(new File(dir+"\\jchn_operator_log_"+nowDay+".log"),true);
Buff = new BufferedOutputStream(outSTr);
write = new StringBuffer();
write.append(content);
write.append("\r\n");
Buff.write(write.toString().getBytes("UTF-8"));
Buff.flush();
Buff.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
Buff.close();
outSTr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
从上面方法可以看出,并没有记录当前操作执行的sql语句
如何获取当前操作的sql呢,我在网上查了一些资料
实现方法,主要借鉴 WangJi92/mybatis-sql-log
将上面借鉴的MybatisSqlCompletePrintInterceptor 中的方法整合到日志切面中
package com.shxp.project.sys.config;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Joiner;
import com.shxp.project.common.KoalClient;
import com.shxp.project.common.LogUtils;
import com.shxp.project.common.entity.vo.UserMessage;
import com.shxp.project.common.util.*;
import com.shxp.project.shiro.entity.ShiroUserEntity;
import com.shxp.project.sys.annotation.LogParam;
import com.shxp.project.sys.annotation.LogTail;
import com.shxp.project.sys.constants.Constants;
import com.shxp.project.sys.constants.DeptEnum;
import com.shxp.project.sys.entity.LogBean;
import com.shxp.project.sys.service.LogRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 为阿里神经元日志系统推送日志
* 使用{@link com.shxp.project.sys.annotation.LogTail}的方法将会打印操作日志,
* 如需要操作用户信息,需要将userId作为方法的第一个参数
*/
@Aspect
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
@Slf4j
public class LogTailAspect implements Interceptor, Ordered {
private static final Logger LOG = LoggerFactory.getLogger("operate");
private static Field[] fields;
@Resource
private LogRecordService logRecordService;
@Value("${product.id:310000000000}")
private String productId;
@Value("${product.name:机场打击整治非法客运管理}")
private String productName;
private Object targetis;
@Pointcut("execution(* com.shxp.project.*.controller..*.*(..))")
public void excudeService() {
}
static {
Class<LogBean> clazz = LogBean.class;
fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
}
}
@Around("@annotation(com.shxp.project.sys.annotation.LogTail)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Date start = new Date();
Object object = joinPoint.proceed();
Date end = new Date();
execute(joinPoint, object, start, end);
return object;
}
private void execute(ProceedingJoinPoint joinPoint, Object result, Date start, Date end) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
MethodSignature methodSignature = Optional.of(joinPoint)
.map(ProceedingJoinPoint::getSignature)
.map(e -> (MethodSignature) e)
.orElse(null);
LogTail logTail = Optional.of(methodSignature)
.map(MethodSignature::getMethod)
.map(e -> e.getAnnotation(LogTail.class))
.orElse(null);
Method method = methodSignature.getMethod();
Annotation[][] annotations = method.getParameterAnnotations();
Class[] classes = methodSignature.getParameterTypes();
String[] names = methodSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
LogBean logBean = new LogBean();
/**
* 获取用户信息
*/
UserMessage user = getUser(names, args);
if (Objects.nonNull(user)) {
logBean.setOperatorID(user.getUser_id()+"");
logBean.setOperatorAccount(user.getUser_id()+"");
logBean.setOperatorName(user.getUser_name());
logBean.setOrganizationID(user.getOrg_code());
logBean.setOrganizationName(user.getFull_name());
}else{
ShiroUserEntity currentUser = UserUtil.getCurrentUser();
assert currentUser != null;
logBean.setOperatorID(currentUser.getId()+"");
logBean.setOperatorAccount(currentUser.getUserAccount()+"");
logBean.setOperatorName(currentUser.getName());
DeptEnum deptEnum = DeptEnum.getByCode(currentUser.getDept());
if (deptEnum!=null){
logBean.setOrganizationID(deptEnum.getOrg());
logBean.setOrganizationName(deptEnum.getName());
}
}
logBean.setProductID(productId);
logBean.setProductName(productName);
logBean.setLogID(UUID.randomUUID().toString());
logBean.setOperatorIdentity(1);
logBean.setOpTime(DateUtils.format(start, DateUtils.DEFAULT_DATE_PATTERN14));
logBean.setResTime(DateUtils.format(end, DateUtils.DEFAULT_DATE_PATTERN14));
logBean.setTerminalType(0);
logBean.setOpType(logTail.operateType().getCode());
logBean.setClientIp(IpUtils.getIpAddr(request));
logBean.setClientPort(Objects.isNull(request.getRemotePort())?null:String.valueOf(request.getRemotePort()));
logBean.setURL(request.getRequestURI());
// logBean.setObjectParams();
logBean.setSessionID(request.getRequestedSessionId());
logBean.setFuncModuleName(logTail.module().getName()+"-"+logTail.name());
logBean.setObjectIP(IpUtils.getIpAddr(request));
logBean.setObjectPort(Objects.isNull(request.getRemotePort())?null:String.valueOf(request.getRemotePort()));
// logBean.setOperateCondition();
logBean.setOperateCondition(getRequestParam(joinPoint));
// logBean.setTerminalID();
// logBean.setTerminalMac();
logBean.setQuerySql(getSql(targetis));
String log = Stream.of(fields).map(e -> {
try {
return e.get(logBean);
} catch (IllegalAccessException ex) {
return "";
}
}).map(Util::getString).collect(Collectors.joining(Constants.SEPARATOR));
// 日志写入文件中
LogUtils.insertLog(log);
// 日志入库
logRecordService.insertLog(logBean);
}
/**
* 从参数列表中获取userId参数,返回用户信息
*
* @param names
* @param args
* @return
*/
private UserMessage getUser(String[] names, Object[] args) {
UserMessage user = null;
for (int i = 0; i < names.length; i++) {
if ("userId".equals(names[i]) && args.length >= i+1) {
String userId = StringTools.getStringValue(args[i]);
List<UserMessage> userMessages = KoalClient.queryUserByIdcard(userId);
if (!CollectionUtils.isEmpty(userMessages)){
user = userMessages.get(0);
}
}
}
return user;
}
private String getRequestParam(JoinPoint pjp){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
if (HttpMethod.PUT.name().equals(request.getMethod()) || HttpMethod.POST.name().equals(request.getMethod())){
return getBodyContent(pjp);
} else {
Map<String,Object> map = getParamContent(request.getParameterMap());
return JSON.toJSONString(map);
}
}
private Map<String,Object> getParamContent(Map<String, String[]> params) {
Map<String,Object> resultMap = new HashMap<>();
try {
for (Map.Entry<String, String[]> entry : params.entrySet()) {
Object value = new Object();
if (entry.getValue() != null && entry.getValue().length == 1) {
value = entry.getValue()[0];
} else {
value = entry.getValue();
}
resultMap.put(entry.getKey(),value);
}
} catch (Exception e) {
LOG.error("日志记录解析param参数失败",e.getMessage());
resultMap.put("paramParse","参数解析失败");
}
return resultMap;
}
private String getBodyContent(JoinPoint pjp) {
String bodyContent = "";
try {
Object[] args = pjp.getArgs();
List arguments = new ArrayList();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments.add(args[i]);
}
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(arguments) && arguments.size() == 1) {
bodyContent = JSON.toJSONString(arguments.get(0));
} else {
bodyContent = JSON.toJSONString(arguments);
}
}catch (Exception e) {
LOG.error("日志记录解析body参数失败",e);
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("paramParse","参数解析失败");
}
return bodyContent;
}
private Configuration configuration = null;
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
}
};
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
targetis = target;
long startTime = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
String sql = this.getSql(target);
long endTime = System.currentTimeMillis();
long sqlCost = endTime - startTime;
// log.info("SQL:{} 执行耗时={}", sql, sqlCost);
}
}
/**
* 获取sql
*
* @param target
* @return
* @throws IllegalAccessException
*/
private String getSql(Object target) {
try {
StatementHandler statementHandler = (StatementHandler) target;
BoundSql boundSql = statementHandler.getBoundSql();
if (configuration == null) {
final DefaultParameterHandler parameterHandler = (DefaultParameterHandler) statementHandler.getParameterHandler();
Field configurationField = ReflectionUtils.findField(parameterHandler.getClass(), "configuration");
ReflectionUtils.makeAccessible(configurationField);
this.configuration = (Configuration) configurationField.get(parameterHandler);
}
//替换参数格式化Sql语句,去除换行符
return formatSql(boundSql, configuration);
} catch (Exception e) {
log.warn("get sql error {}", target, e);
}
return "";
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 获取完整的sql实体的信息
*
* @param boundSql
* @return
*/
private String formatSql(BoundSql boundSql, Configuration configuration) {
String sql = boundSql.getSql();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
Object parameterObject = boundSql.getParameterObject();
// 输入sql字符串空判断
if (sql == null || sql.length() == 0) {
return "";
}
if (configuration == null) {
return "";
}
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// 美化sql
sql = beautifySql(sql);
/**
* @see DefaultParameterHandler 参考Mybatis 参数处理
*/
if (parameterMappings != null) {
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
String paramValueStr = "";
if (value instanceof String) {
paramValueStr = "'" + value + "'";
} else if (value instanceof Date) {
paramValueStr = "'" + DATE_FORMAT_THREAD_LOCAL.get().format(value) + "'";
} else {
paramValueStr = value + "";
}
// mybatis generator 中的参数不打印出来
if (!propertyName.contains("frch_criterion")) {
paramValueStr = "/*" + propertyName + "*/" + paramValueStr;
}
// java.lang.IllegalArgumentException: Illegal group reference
// https://github.com/WangJi92/mybatis-sql-log/issues/4
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(paramValueStr));
}
}
}
return sql;
}
/**
* 美化Sql
*/
private String beautifySql(String sql) {
sql = sql.replaceAll("[\\s\n ]+", " ");
return sql;
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
测试的时候有个小bug,他只能记录你这个方法中的最后执行的sql语句,在项目刚启动的时候,查询数据列表,有的字段需要字典翻译,查字典的方法如果在最后面,日志会记录查字典的sql,所以开发的时候要求他们字典查询放到列表查询前面,影响不大
日志实体类
import lombok.Data;
@Data
public class LogBean {
/**
* 应用系统的ID号,政务微信轻应用按照政务微信标准填写,其他应用系统按照4A平台标准填写
*/
private String ProductID;
/**
* 应用系统的中文名称
*/
private String ProductName;
/**
* 日志的ID号,在日志记录产生时生成,其格式和产生方式由应用系统自行决定
*/
private String LogID;
/**
* 操作人在应用中的ID号
*/
private String OperatorID;
/**
* 操作人居民身份证号,按照4A平台标准填写
*/
private String OperatorAccount;
/**
* 0.管理员;1.普通用户
*/
private Integer OperatorIdentity;
/**
* 操作人的真实姓名
*/
private String OperatorName;
/**
* 操作人的所在单位名称,按照4A平台标准填写
*/
private String OrganizationName;
/**
* 操作人所属单位的公安机关机构代码,按照4A平台标准填写
*/
private String OrganizationID;
/**
* 操作人操作后获得系统反馈响应的时间,
* 采用格式yyyyMMddHHmmssSSSS,24小时格式,精确到毫秒
*/
private String OpTime;
/**
* 操作人操作后获得系统反馈响应的时间,
* 采用格式yyyyMMddHHmmssSSSS,24小时格式,精确到毫秒
*/
private String ResTime;
/**
* 0.非移动终端;1.移动终端
*/
private Integer TerminalType;
/**
* 用户操作时所使用的信息处理终端的标识。
* 通过非移动终端如桌面终端进行操作的,其终端标识为其网络IP地址;
* 通过移动终端APP进行操作的,其终端标识为IMEI序列号;
* 通过政务微信轻应用进行操作的,置空
*/
private String TerminalID;
/**
* 通过移动终端APP接入的,记录其终端手机号码;通过政务微信轻应用接入的,置空
*/
private String TerminalNum;
/**
* 通过移动终端APP接入的,记录其终端MAC地址;通过政务微信轻应用接入的,置空
*/
private String TerminalMac;
/**
* 0:登录;1:查询;2:新增;3:修改;4:删除;5:退出
*/
private Integer OpType;
/**
* 操作类型为1-查询、3-修改、4-删除类型时,记录用户进行操作时的数据筛选条件,
* 填写数据操作SQL语句的where子句内容,
* 如:name=‘张三’;当操作类型为其他类型时,置空
*/
private String OperateCondition;
/**
* 操作人操作的结果,包括成功/失败。0:失败;1:成功
*/
private Integer OperateResult;
/**
* 当操作结果为失败时,可记录操作失败的原因代码
*/
private Integer ErrCode;
/**
* 客户端的IP地址,如果是服务端操作或系统生产帐号操作则填写
*/
private String ClientIp;
/**
* 客户端访问时的源端口。若操作由服务端或系统生产账号产生,置空
*/
private String ClientPort;
/**
* 操作目标的URL
*/
private String URL;
/**
* 当请求为HTTP GET时,填写URL后面的queryString内容;为POST时,填写body内容
*/
private String ObjectParams;
/**
* 用于标识应用系统产生的一次会话
*/
private String SessionID;
/**
* 操作人所操作的应用内的具体功能模块名称
*/
private String FuncModuleName;
/**
* 操作目标的IP地址,如目标对象在本机或本系统时,则目标IP与公开IP一致,否则取实际访问值
*/
private String ObjectIP;
/**
* 操作目标提供访问或服务的端口
*/
private String ObjectPort;
/**
* 当操作执行为SQL语句时,填写SQL语句,有多个SQL语句时应全部填写;当操作执行为XML等方式时,填写相对应数据
*/
}