♻️ 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.admin.api.feign.RemoteLogService;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver; import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
import com.pig4cloud.pigx.common.log.aspect.SysLogAspect; 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 com.pig4cloud.pigx.common.log.event.SysLogListener;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -37,21 +40,19 @@ import org.springframework.scheduling.annotation.EnableAsync;
* 日志自动配置 * 日志自动配置
*/ */
@EnableAsync @EnableAsync
@Configuration @Configuration(proxyBeanMethods = false)
@AllArgsConstructor @EnableConfigurationProperties(PigXLogProperties.class)
@ConditionalOnWebApplication @ConditionalOnProperty(value = "pigx.log.enabled", matchIfMissing = true)
public class LogAutoConfiguration { public class LogAutoConfiguration {
private final RemoteLogService remoteLogService;
@Bean @Bean
public SysLogListener sysLogListener() { public SysLogListener sysLogListener(PigXLogProperties logProperties, RemoteLogService remoteLogService) {
return new SysLogListener(remoteLogService); return new SysLogListener(remoteLogService, logProperties);
} }
@Bean @Bean
public SysLogAspect sysLogAspect(ApplicationEventPublisher publisher, KeyStrResolver resolver) { public SysLogAspect sysLogAspect(KeyStrResolver resolver) {
return new SysLogAspect(publisher, 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 * http://www.apache.org/licenses/LICENSE-2.0
* 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)
* *
* 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; 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 cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO; import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver; 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.annotation.SysLog;
import com.pig4cloud.pigx.common.log.event.SysLogEvent; import com.pig4cloud.pigx.common.log.event.SysLogEvent;
import com.pig4cloud.pigx.common.log.util.LogTypeEnum; 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.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
/** /**
@@ -41,17 +38,15 @@ import org.springframework.expression.EvaluationContext;
* *
* @author L.cm * @author L.cm
*/ */
@Slf4j
@Aspect @Aspect
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class SysLogAspect { public class SysLogAspect {
private final ApplicationEventPublisher publisher;
private final KeyStrResolver tenantKeyStrResolver; private final KeyStrResolver tenantKeyStrResolver;
@SneakyThrows
@Around("@annotation(sysLog)") @Around("@annotation(sysLog)")
@SneakyThrows
public Object around(ProceedingJoinPoint point, SysLog sysLog) { public Object around(ProceedingJoinPoint point, SysLog sysLog) {
String strClassName = point.getTarget().getClass().getName(); String strClassName = point.getTarget().getClass().getName();
String strMethodName = point.getSignature().getName(); String strMethodName = point.getSignature().getName();
@@ -68,30 +63,36 @@ public class SysLogAspect {
value = SysLogUtils.getValue(context, expression, String.class); value = SysLogUtils.getValue(context, expression, String.class);
} }
catch (Exception e) { catch (Exception e) {
// SPEL 表达式异常,则获取 value 的值 // SPEL 表达式异常,获取 value 的值
log.error("@SysLog 解析SPEL {} 异常", expression); log.error("@SysLog 解析SPEL {} 异常", expression);
} }
} }
SysLogDTO logDTO = SysLogUtils.getSysLog(); SysLogDTO logVo = SysLogUtils.getSysLog();
logDTO.setTitle(value); logVo.setTitle(value);
// 获取请求body参数
if (StrUtil.isBlank(logVo.getParams())) {
logVo.setBody(point.getArgs());
}
// 发送异步日志事件 // 发送异步日志事件
Long startTime = System.currentTimeMillis(); Long startTime = System.currentTimeMillis();
Object obj; Object obj;
try { try {
obj = point.proceed(); obj = point.proceed();
} }
catch (Exception e) { catch (Exception e) {
logDTO.setLogType(LogTypeEnum.ERROR.getType()); logVo.setLogType(LogTypeEnum.ERROR.getType());
logDTO.setException(e.getMessage()); logVo.setException(e.getMessage());
throw e; throw e;
} }
finally { finally {
Long endTime = System.currentTimeMillis(); Long endTime = System.currentTimeMillis();
logDTO.setTime(endTime - startTime); logVo.setTime(endTime - startTime);
logDTO.setTenantId(Long.parseLong(tenantKeyStrResolver.key())); logVo.setTenantId(Long.parseLong(tenantKeyStrResolver.key()));
publisher.publishEvent(new SysLogEvent(logDTO)); SpringContextHolder.publishEvent(new SysLogEvent(logVo));
} }
return obj; 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 * http://www.apache.org/licenses/LICENSE-2.0
* 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)
* *
* 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; package com.pig4cloud.pigx.common.log.event;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO; import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import lombok.AllArgsConstructor; import org.springframework.context.ApplicationEvent;
import lombok.Getter;
/** /**
* @author lengleng 系统日志事件 * @author lengleng 系统日志事件
*/ */
@Getter public class SysLogEvent extends ApplicationEvent {
@AllArgsConstructor
public class SysLogEvent {
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 * http://www.apache.org/licenses/LICENSE-2.0
* 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)
* *
* 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; 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.dto.SysLogDTO;
import com.pig4cloud.pigx.admin.api.entity.SysLog;
import com.pig4cloud.pigx.admin.api.feign.RemoteLogService; import com.pig4cloud.pigx.admin.api.feign.RemoteLogService;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants; 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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import java.util.Objects;
/** /**
* @author lengleng 异步监听日志事件 * @author lengleng 异步监听日志事件
*/ */
@Slf4j @Slf4j
@AllArgsConstructor @RequiredArgsConstructor
public class SysLogListener { public class SysLogListener implements InitializingBean {
// new 一个 避免日志脱敏策略影响全局ObjectMapper
private final static ObjectMapper objectMapper = new ObjectMapper();
private final RemoteLogService remoteLogService; private final RemoteLogService remoteLogService;
private final PigXLogProperties logProperties;
@SneakyThrows
@Async @Async
@Order @Order
@EventListener(SysLogEvent.class) @EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event) { public void saveSysLog(SysLogEvent event) {
SysLogDTO sysLog = event.getSysLog(); SysLogDTO source = (SysLogDTO) event.getSource();
remoteLogService.saveLog(sysLog, SecurityConstants.FROM_IN);
// 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 * http://www.apache.org/licenses/LICENSE-2.0
* 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)
* *
* 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; package com.pig4cloud.pigx.common.log.util;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.JakartaServletUtil; import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO; import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants; 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 lombok.experimental.UtilityClass;
import org.springframework.core.StandardReflectionParameterNameDiscoverer; import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -53,35 +55,22 @@ public class SysLogUtils {
HttpServletRequest request = ((ServletRequestAttributes) Objects HttpServletRequest request = ((ServletRequestAttributes) Objects
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
SysLogDTO sysLog = new SysLogDTO(); SysLogDTO sysLog = new SysLogDTO();
sysLog.setCreateBy(getUsername());
sysLog.setLogType(LogTypeEnum.NORMAL.getType()); sysLog.setLogType(LogTypeEnum.NORMAL.getType());
sysLog.setRemoteAddr(JakartaServletUtil.getClientIP(request));
sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI())); sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
sysLog.setMethod(request.getMethod()); sysLog.setMethod(request.getMethod());
sysLog.setUserAgent(request.getHeader("user-agent")); sysLog.setRemoteAddr(JakartaServletUtil.getClientIP(request));
sysLog.setParams(HttpUtil.toParams(request.getParameterMap())); sysLog.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
sysLog.setCreateBy(getUsername());
sysLog.setServiceId(getClientId()); 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 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 * @return username
@@ -126,4 +115,22 @@ public class SysLogUtils {
return context; 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; private String params;
/**
* 参数重写成object
*/
private Object body;
/** /**
* 执行时间 * 执行时间
*/ */