♻️ Refactoring code. 重构日志管理支持请求参数记录

This commit is contained in:
lbw committed 2023-08-14 11:42:18 +08:00
1 parent 2efc65c832
commit 1653e11fc3
8 files changed
+218 -110

No files matched your search

@@ -22,9 +22,12 @@ package com.pig4cloud.pigx.common.log;
import com.pig4cloud.pigx.admin.api.feign.RemoteLogService;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
import com.pig4cloud.pigx.common.log.aspect.SysLogAspect;
import com.pig4cloud.pigx.common.log.config.PigXLogProperties;
import com.pig4cloud.pigx.common.log.event.SysLogListener;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -37,21 +40,19 @@ import org.springframework.scheduling.annotation.EnableAsync;
* 日志自动配置
*/
@EnableAsync
@Configuration
@AllArgsConstructor
@ConditionalOnWebApplication
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PigXLogProperties.class)
@ConditionalOnProperty(value = "pigx.log.enabled", matchIfMissing = true)
public class LogAutoConfiguration {
private final RemoteLogService remoteLogService;
@Bean
public SysLogListener sysLogListener() {
return new SysLogListener(remoteLogService);
public SysLogListener sysLogListener(PigXLogProperties logProperties, RemoteLogService remoteLogService) {
return new SysLogListener(remoteLogService, logProperties);
}
@Bean
public SysLogAspect sysLogAspect(ApplicationEventPublisher publisher, KeyStrResolver resolver) {
return new SysLogAspect(publisher, resolver);
public SysLogAspect sysLogAspect(KeyStrResolver resolver) {
return new SysLogAspect(resolver);
}
}
@@ -1,20 +1,17 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Copyright (c) 2018-2025, lengleng All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pigx.common.log.aspect;
@@ -22,6 +19,7 @@ package com.pig4cloud.pigx.common.log.aspect;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.log.event.SysLogEvent;
import com.pig4cloud.pigx.common.log.util.LogTypeEnum;
@@ -33,7 +31,6 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.expression.EvaluationContext;
/**
@@ -41,17 +38,15 @@ import org.springframework.expression.EvaluationContext;
*
* @author L.cm
*/
@Slf4j
@Aspect
@Slf4j
@RequiredArgsConstructor
public class SysLogAspect {
private final ApplicationEventPublisher publisher;
private final KeyStrResolver tenantKeyStrResolver;
@SneakyThrows
@Around("@annotation(sysLog)")
@SneakyThrows
public Object around(ProceedingJoinPoint point, SysLog sysLog) {
String strClassName = point.getTarget().getClass().getName();
String strMethodName = point.getSignature().getName();
@@ -68,30 +63,36 @@ public class SysLogAspect {
value = SysLogUtils.getValue(context, expression, String.class);
}
catch (Exception e) {
// SPEL 表达式异常,则获取 value 的值
// SPEL 表达式异常,获取 value 的值
log.error("@SysLog 解析SPEL {} 异常", expression);
}
}
SysLogDTO logDTO = SysLogUtils.getSysLog();
logDTO.setTitle(value);
SysLogDTO logVo = SysLogUtils.getSysLog();
logVo.setTitle(value);
// 获取请求body参数
if (StrUtil.isBlank(logVo.getParams())) {
logVo.setBody(point.getArgs());
}
// 发送异步日志事件
Long startTime = System.currentTimeMillis();
Object obj;
try {
obj = point.proceed();
}
catch (Exception e) {
logDTO.setLogType(LogTypeEnum.ERROR.getType());
logDTO.setException(e.getMessage());
logVo.setLogType(LogTypeEnum.ERROR.getType());
logVo.setException(e.getMessage());
throw e;
}
finally {
Long endTime = System.currentTimeMillis();
logDTO.setTime(endTime - startTime);
logDTO.setTenantId(Long.parseLong(tenantKeyStrResolver.key()));
publisher.publishEvent(new SysLogEvent(logDTO));
logVo.setTime(endTime - startTime);
logVo.setTenantId(Long.parseLong(tenantKeyStrResolver.key()));
SpringContextHolder.publishEvent(new SysLogEvent(logVo));
}
return obj;
}
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pigx.common.log.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* 日志配置类
*
* @author L.cm
*/
@Getter
@Setter
@ConfigurationProperties(PigXLogProperties.PREFIX)
public class PigXLogProperties {
public static final String PREFIX = "pigx.log";
/**
* 开启日志记录
*/
private boolean enabled = true;
/**
* 记录请求报文体
*/
private boolean requestEnabled = true;
/**
* 放行字段,password,mobile,idcard,phone
*/
@Value("${pigx.log.exclude-fields:password,mobile,idcard,phone}")
private List<String> excludeFields;
/**
* 请求报文最大存储长度
*/
private Integer maxLength = 2000;
}
@@ -1,35 +1,31 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Copyright (c) 2018-2025, lengleng All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pigx.common.log.event;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
/**
* @author lengleng 系统日志事件
*/
@Getter
@AllArgsConstructor
public class SysLogEvent {
public class SysLogEvent extends ApplicationEvent {
private final SysLogDTO sysLog;
public SysLogEvent(SysLogDTO source) {
super(source);
}
}
@@ -1,48 +1,89 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Copyright (c) 2018-2025, lengleng All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pigx.common.log.event;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import com.pig4cloud.pigx.admin.api.entity.SysLog;
import com.pig4cloud.pigx.admin.api.feign.RemoteLogService;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import lombok.AllArgsConstructor;
import com.pig4cloud.pigx.common.core.jackson.PigxJavaTimeModule;
import com.pig4cloud.pigx.common.log.config.PigXLogProperties;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Objects;
/**
* @author lengleng 异步监听日志事件
*/
@Slf4j
@AllArgsConstructor
public class SysLogListener {
@RequiredArgsConstructor
public class SysLogListener implements InitializingBean {
// new 一个 避免日志脱敏策略影响全局ObjectMapper
private final static ObjectMapper objectMapper = new ObjectMapper();
private final RemoteLogService remoteLogService;
private final PigXLogProperties logProperties;
@SneakyThrows
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event) {
SysLogDTO sysLog = event.getSysLog();
remoteLogService.saveLog(sysLog, SecurityConstants.FROM_IN);
SysLogDTO source = (SysLogDTO) event.getSource();
// json 格式刷参数放在异步中处理,提升性能
if (Objects.nonNull(source.getBody()) && logProperties.isRequestEnabled()) {
String params = objectMapper.writeValueAsString(source.getBody());
source.setParams(StrUtil.subPre(params, logProperties.getMaxLength()));
}
source.setBody(null);
remoteLogService.saveLog(source, SecurityConstants.FROM_IN);
}
@Override
public void afterPropertiesSet() {
objectMapper.addMixIn(Object.class, PropertyFilterMixIn.class);
String[] ignorableFieldNames = logProperties.getExcludeFields().toArray(new String[0]);
FilterProvider filters = new SimpleFilterProvider().addFilter("filter properties by name",
SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames));
objectMapper.setFilterProvider(filters);
objectMapper.registerModule(new PigxJavaTimeModule());
}
@JsonFilter("filter properties by name")
class PropertyFilterMixIn {
}
}
@@ -1,44 +1,46 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Copyright (c) 2018-2025, lengleng All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pigx.common.log.util;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.log.config.PigXLogProperties;
import jakarta.servlet.http.HttpServletRequest;
import lombok.experimental.UtilityClass;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
/**
@@ -53,35 +55,22 @@ public class SysLogUtils {
HttpServletRequest request = ((ServletRequestAttributes) Objects
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
SysLogDTO sysLog = new SysLogDTO();
sysLog.setCreateBy(getUsername());
sysLog.setLogType(LogTypeEnum.NORMAL.getType());
sysLog.setRemoteAddr(JakartaServletUtil.getClientIP(request));
sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
sysLog.setMethod(request.getMethod());
sysLog.setUserAgent(request.getHeader("user-agent"));
sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
sysLog.setRemoteAddr(JakartaServletUtil.getClientIP(request));
sysLog.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
sysLog.setCreateBy(getUsername());
sysLog.setServiceId(getClientId());
// get 参数脱敏
PigXLogProperties logProperties = SpringUtil.getBean(PigXLogProperties.class);
Map<String, String[]> paramsMap = MapUtil.removeAny(request.getParameterMap(),
ArrayUtil.toArray(logProperties.getExcludeFields(), String.class));
sysLog.setParams(HttpUtil.toParams(paramsMap));
return sysLog;
}
/**
* 获取客户端
* @return clientId
*/
private String getClientId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
}
Object principal = authentication.getPrincipal();
if (principal instanceof OAuth2AuthenticatedPrincipal) {
OAuth2AuthenticatedPrincipal auth2Authentication = (OAuth2AuthenticatedPrincipal) principal;
return MapUtil.getStr(auth2Authentication.getAttributes(), SecurityConstants.CLIENT_ID);
}
return null;
}
/**
* 获取用户名称
* @return username
@@ -126,4 +115,22 @@ public class SysLogUtils {
return context;
}
/**
* 获取客户端
* @return clientId
*/
private String getClientId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
}
Object principal = authentication.getPrincipal();
if (principal instanceof OAuth2AuthenticatedPrincipal) {
OAuth2AuthenticatedPrincipal auth2Authentication = (OAuth2AuthenticatedPrincipal) principal;
return MapUtil.getStr(auth2Authentication.getAttributes(), SecurityConstants.CLIENT_ID);
}
return null;
}
}
@@ -1,2 +0,0 @@
org.springframework.boot.env.EnvironmentPostProcessor=\
com.pig4cloud.pigx.common.log.init.ApplicationLoggerInitializer
@@ -68,6 +68,11 @@ public class SysLogDTO {
*/
private String params;
/**
* 参数重写成object
*/
private Object body;
/**
* 执行时间
*/