一:pom文件添加
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二:定义获取用户详情和插入日志的fegin UaaUserServiceFeign,UaaLogServiceFeign
实例代码如下:
/**
* @ClassName: UaaUserService
* @Description: TODO
* @author: wangzhaowen:ydyt16
* @UpdateDate: 2019/04/13 17:45:24
*/
@FeignClient("${user.feign.name}")
@RequestMapping("/service/")
public interface UaaUserServiceFeign {
/**
* 查询用户详情
* @param userId
* @throws ServiceException
*/
@GetMapping("/user/detail/byUserId")
UaaUser detailUaaUser(@RequestParam("userId") Long userId)throws ServiceException;
}
/**
* @ClassName: UaaLogServiceFeign
* @Description: 日志远程调用处理接口
* @author: wangzhaowen:ydyt16
* @UpdateDate: 2019/04/13 20:51:19
*/
@FeignClient("${log.feign.name}")
@RequestMapping("/service/")
public interface UaaLogServiceFeign {
/**
* 添加操作日志
*
* @return
* @throws Exception
*/
@PostMapping("/log/operation/insert_info")
Long insertOperationLog(@RequestBody UaaOperationLogWithBLOBs dataLog) throws ServiceException;
}
定义LogService接口
package com.hcloud.log;
import javax.servlet.http.HttpServletRequest;
import com.hcloud.entity.DataEntity;
import com.hcloud.exception.ServiceException;
/**
* @ClassName: LogServvice
* @Description: 日志服务接口
* @author: wangzhaowen:ydyt16
* @UpdateDate: 2019/04/13 17:05:10
*/
public interface LogServvice {
/**
*
* @Title: save
* @Description: 保存常规日志
* @param Log 日志对象
* @throws ServiceException
* @return: void
*/
void save(DataEntity Log) throws ServiceException;
/**
*
* @Title: save
* @Description: 保存异常日志
* @param request
* @param response
* @param handler
* @param ex
* @return: void
*/
void save(HttpServletRequest request, Exception ex)throws ServiceException;
/**
*
* @Title: saveOperationLog
* @Description: 保存操作日志
* @param request
* @return: void
*/
void saveOperationLog(HttpServletRequest request);
}
定义抽象类
/**
* @ClassName: AbstractLogService
* @Description: 日志抽象处理类
* @author: wangzhaowen:ydyt16
* @UpdateDate: 2019/04/13 09:55:18
*/
public abstract class AbstractLogService implements LogServvice {
/**
* 记录数据库最大字符长度
*/
private static final int WIRTE_DB_MAX_LENGTH = 1500;
/**
* 日志打印对象
*/
protected Logger log = LoggerFactory.getLogger(getClass());
/**
* @Title: saveExceptionLog
* @Description: 异常日志异步存储处理
* @param req
* @param ex
* @return: void
*/
@Override
public void save(HttpServletRequest request, Exception ex) {
final Long userId = getUserId(request);//需要同步 请求完后 request会销毁,有可能获取不到用户信息
ThreadPoolUtil.executeIoJob(new Runnable() {
@Override
public void run() {
try {
// 保存错误日志到数据库中
UaaOperationLogWithBLOBs logBean = new UaaOperationLogWithBLOBs();
if(request.getAttribute(LogAspect.LOG_ENTITY) != null) {
logBean = (UaaOperationLogWithBLOBs) request.getAttribute(LogAspect.LOG_ENTITY);;
}
String exceptionMessage = "错误异常: " + ex.getClass().getSimpleName() + ",错误描述:" + ex.getMessage();
if (StringUtil.isNotEmpty(exceptionMessage)) {
if (exceptionMessage.length() > WIRTE_DB_MAX_LENGTH) {
exceptionMessage = exceptionMessage.substring(0, WIRTE_DB_MAX_LENGTH);
}
}
// 获取用户
UaaUser uaaUser = (UaaUser) getUser(userId);
if (uaaUser != null && StringUtil.isNotEmpty(uaaUser.getId().toString())) {
logBean.setCompanyId(uaaUser.getCompanyId());
logBean.setManagerNo(uaaUser.getManagerNo());
logBean.setUserId(uaaUser.getId());
}
logBean.setLogcontent(exceptionMessage);
logBean.setLogLevel(OperationLogLevel.EXCEPTION_LOG.getValue());
logBean.setOperateType(OperationLogType.EXCEPTION.getValue());
save(logBean);
} catch (Exception e) {
log.warn("异常日志入库异常:{}", e);
}
}
});
}
@Autowired
private TokenStore tokenStore;
/**
*
* @Title: saveOperationLog
* @Description: 保存操作日志
* @param request
* @param mothodName
* @return: void
*/
public void saveOperationLog(HttpServletRequest request) {
String uri=request.getRequestURI();
final Long userId = getUserId(request);//需要同步 请求完后 request会销毁,有可能获取不到用户信息
UaaOperationLogWithBLOBs temp=null;
if (request.getAttribute(LogAspect.LOG_ENTITY) != null) {
temp = (UaaOperationLogWithBLOBs) request.getAttribute(LogAspect.LOG_ENTITY);
}
final UaaOperationLogWithBLOBs uaaOperationLogWithBLOBs=temp;
log.debug("url:{},maches:{}",uri,exclution);
if(isSvae(uri)) {
ThreadPoolUtil.executeIoJob(new Runnable() {
@Override
public void run() {
UaaOperationLogExtend operationLogExtend = getoLogExtend(uri);
if(uaaOperationLogWithBLOBs != null) {
if(userId!=null) {
UaaUser u = (UaaUser) getUser(userId);
if (u != null && StringUtils.isNotEmpty(u.getId().toString())) {
uaaOperationLogWithBLOBs.setCompanyId(u.getCompanyId());
uaaOperationLogWithBLOBs.setManagerNo(u.getManagerNo());
uaaOperationLogWithBLOBs.setUserId(u.getId());
} else {
log.warn("根据userID:{}获取用户的信息为空",userId);
}
}else {
log.warn("无法获取登录用户的ID");
}
uaaOperationLogWithBLOBs.setTranceId(TraceContext.traceId());
uaaOperationLogWithBLOBs.setLogLevel(OperationLogLevel.NORMAL_LOG.getValue());
if (operationLogExtend != null ) {
uaaOperationLogWithBLOBs.setLogcontent(operationLogExtend.getLogcontent());
uaaOperationLogWithBLOBs.setOperateType(operationLogExtend.getOperateType().shortValue());
}
save(uaaOperationLogWithBLOBs);
log.debug("保存日志.成功!.");
}
}
});
}
else {
log.debug("url{}不记录日志:",uri);
}
}
/**
* @fieldName: operatioMap
* @fieldType: Map<String,List<UaaOperation>>
* @Description: 基础权限信息 ,表 uaa_operation 中的信息
*/
private static Map <String,List<UaaOperation>> operatioMap = null;
/**
* @fieldName: logExtendMap
* @fieldType: Map<Long,List<UaaOperationLogExtend>>
* @Description: 基础日志扩充信息 ,表 uaa_operation_log_extend 中的信息
*/
private static Map <Long,List<UaaOperationLogExtend>> logExtendMap = null;
/**
*
* @Title: getoLogExtend
* @Description: 获取日志扩张信息
* @param url
* @return
* @return: UaaOperationLogExtend
*/
public UaaOperationLogExtend getoLogExtend(String url) {
UaaOperationLogExtend le=new UaaOperationLogExtend();
if(operatioMap == null) {
List<UaaOperation> listOperation=getAllOperation();
operatioMap = listOperation.stream().collect(
Collectors.groupingBy(UaaOperation::getUrl));
}
if(logExtendMap == null) {
List<UaaOperationLogExtend> logExtendList= getAllLogExtend();
logExtendMap=logExtendList.stream().collect(
Collectors.groupingBy(UaaOperationLogExtend::getOperationId));
}
if(operatioMap.get(url)!=null) {
UaaOperation operation=operatioMap.get(url).get(0);//若 url有重复 取第一个
if(logExtendMap.get(operation.getId())!=null) {//数据库中查询
le=logExtendMap.get(operation.getId()).get(0);
log.debug("logExtend信息:{}",le);
}else {
le.setLogcontent(operation.getOperationName());
le.setId(-1L);
le.setOperateType(8);
log.debug("logExtend信息:{}",le);
}
}else {
le.setLogcontent(url);
le.setId(-1L);
le.setOperateType(8);
log.warn("此URL:{} 未定义权限信息",url);
}
return le;
};
/**
*
* @Title: getAllLogExtend
* @Description: 远程获取 地址为: GetMapping("/lists/operation/byMap") 服务为log-service
* @return
* @return: List<UaaOperationLogExtend>
*/
protected abstract List<UaaOperationLogExtend> getAllLogExtend();
/**
*
* @Title: getAllOperation
* @Description: 定义Feign 远程获取 接口地址为 GetMapping("/operation/allUOperation/list") 远程服务为user-service
* @return
* @return: List<UaaOperation>
*/
protected abstract List<UaaOperation> getAllOperation();
/**
*
* @Title: getUser
* @Description: 获取当前登录用户
* @param userId
* @return
* @return: UaaUser
*/
protected abstract DataEntity getUser(Long userId);
/**
*
* @Title: getUserId
* @Description: 获取登录用户的id
* @param tokenStore 登录token
* @param request 请求对象
* @return
* @return: Long
*/
protected Long getUserId(HttpServletRequest request) {
String authString = request.getHeader("Authorization");
log.debug("Authorization:{}",authString);
if (authString != null && authString.length() >= 7) {
String token = authString.substring(7).trim();
OAuth2Authentication authorization = tokenStore.readAuthentication(token);
if (authorization != null) {
@SuppressWarnings("rawtypes")
LinkedHashMap userMap = (LinkedHashMap) authorization.getDetails();
if (userMap != null && userMap.get("userid") != null) {
Integer iUserId = (Integer) userMap.get("userid");
return iUserId.longValue();
}
}
}
return null;
}
@Value("${log.exclution.uri}")
private String exclution;
protected boolean isSvae(String mached) {
boolean flag = true;
if(!StringUtils.isEmpty(exclution)) {
for (String lmd : exclution.split(",")) {
if (mached.contains(lmd)) {
flag = false;
break;
}
}
}
return flag;
}
实现日志处理类LogServiceImpl 实例代码如下:
package com.yst.log.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hcloud.entity.DataEntity;
import com.hcloud.exception.ServiceException;
import com.yst.log.domain.UaaOperationLogExtend;
import com.yst.log.domain.UaaOperationLogWithBLOBs;
import com.yst.log.feign.user.UaaUserService;
import com.yst.log.service.AbstractLogService;
import com.yst.log.service.UaaOperationLogExtendService;
import com.yst.log.service.UaaOperationLogService;
import com.yst.user.domain.UaaOperation;
import com.yst.user.domain.UaaUser;
/**
* @ClassName: LogInterceptorImpl
* @Description: 具体日志数据封装实现类
* @author: wangzhaowen:ydyt16
* @UpdateDate: 2019/04/17 12:24:23
*/
@Service("LogInterceptorImpl")
public class LogServiceImpl extends AbstractLogService{
/**
* @fieldName: uaaUserServiceFeign
* @fieldType: UaaUserServiceFeign
* @Description: 远程用户查询服务接口
*/
@Autowired
private UaaUserService uaaUserService;
/**
* @fieldName: uaaLogServiceFeign
* @fieldType: UaaLogServiceFeign
* @Description: 本地日志处理接口
*/
@Autowired
private UaaOperationLogService uaaOperationLogService;
/**
* @fieldName: uaaOperationLogExtendService
* @fieldType: UaaOperationLogExtendService
* @Description: 本地扩充日志信息查询接口
*/
@Autowired
private UaaOperationLogExtendService uaaOperationLogExtendService;
@Override
public UaaUser getUser(Long userId) {
return uaaUserService.detailUaaUser(userId);
}
@Override
public void save(DataEntity Log) throws ServiceException {
uaaOperationLogService.insertOperationLog((UaaOperationLogWithBLOBs) Log);
}
@Override
protected List<UaaOperationLogExtend> getAllLogExtend() {
List<UaaOperationLogExtend> logExtendList=uaaOperationLogExtendService.selectByMap(null);
return logExtendList;
}
@Override
protected List<UaaOperation> getAllOperation() {
List<UaaOperation> listOperation=uaaUserService.getAllOperation();
return listOperation;
}
}
定义Aspect
package com.yst.log.config;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
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.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hcloud.log.LogServvice;
import com.hcloud.utils.BrowserUtils;
import com.hcloud.utils.IpUtil;
import com.yst.log.domain.UaaOperationLogWithBLOBs;
import com.yst.user.enums.ClientType;
import io.swagger.annotations.ApiOperation;
/**
* @ClassName: LogAspect
* @Description: 日志处理切面
* @author: wangzhaowen:ydyt16
* @UpdateDate: 2019/04/16 20:37:03
*/
@Aspect
@Component
public class LogAspect {
/**
* 缓存方法调用参数和返回值
*/
public static final String LOG_ENTITY="LOG_ENTITY";
/**
* 日志打印对象
*/
protected Logger log = LoggerFactory.getLogger(getClass());
/**
* 日志服务处理接口
*/
@Autowired
LogServvice logServvice;
//设置切入点:这里直接拦截被@RestController注解的类
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
public void pointcut() {
}
/**
* 切面方法,记录日志
* @return
* @throws Throwable
*/
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//利用RequestContextHolder获取requst对象
ServletRequestAttributes requestAttr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
HttpServletRequest request=requestAttr.getRequest();
String url = request.getRequestURI();
long beginTime = System.currentTimeMillis();//1、开始时间
//访问目标方法的参数 可动态改变参数值
Signature signature = joinPoint.getSignature();
String methodName = signature.getName();
if (signature instanceof MethodSignature) {
MethodSignature methodSign = (MethodSignature) signature;
Method method = methodSign.getMethod();
InitBinder initBinder = AnnotationUtils.getAnnotation(method, InitBinder.class);
// 方法名获取
if ("initBinder".equals(methodName) || initBinder != null)// 参数验证方法直接跳过
{
return joinPoint.proceed();
}
ApiOperation apiOperation = AnnotationUtils.getAnnotation(method, ApiOperation.class);
if (null != apiOperation) {
log.debug("方法描述:{}", apiOperation.value());
}
Object[] args = joinPoint.getArgs();
log.debug("参数:{}", args);
ObjectMapper objectMapper = new ObjectMapper();
String param = objectMapper.writeValueAsString(args);
log.debug("请求方法:{}, 请求参数: {}", methodName, param);
// 可能在反向代理请求进来时,获取的IP存在不正确行 这里直接摘抄一段来自网上获取ip的代码
String ip = IpUtil.getIpAddr(request);
// 获取浏览器
String broswer = BrowserUtils.checkBrowse(request);
log.debug("请求ip:{},broswer:{}", ip, broswer);
log.debug("开始计时: {} URI: {}", new SimpleDateFormat(
"hh:mm:ss.SSS").format(beginTime),url);
// 调用实际方法
Object object = joinPoint.proceed();
String result = objectMapper.writeValueAsString(object);
log.debug("方法返回: {}", objectMapper.writeValueAsString(object));
// 缓存返回值
request.setAttribute(LOG_ENTITY, initLogBean(param, result, ip, broswer));
logServvice.saveOperationLog(request);
// 打印JVM信息。
if (log.isDebugEnabled())
{
long endTime = System.currentTimeMillis(); //2、结束时间
log.debug("计时结束:{} 耗时毫秒{} URI: {} 最大内存: {}m 已分配内存: {}m 已分配内存中的剩余空间: {}m 最大可用内存: {}m",
new SimpleDateFormat("hh:mm:ss.SSS").format(endTime),
(endTime - beginTime),
request.getRequestURI(),
Runtime.getRuntime().maxMemory() / 1024 / 1024,
Runtime.getRuntime().totalMemory() / 1024 / 1024,
Runtime.getRuntime().freeMemory() / 1024 / 1024,
(Runtime.getRuntime().maxMemory()
- Runtime.getRuntime().totalMemory() + Runtime.getRuntime()
.freeMemory()) / 1024 / 1024);
}
return object;
}
return joinPoint.proceed();
}
/**
* 指定拦截器规则;也可直接使用within(@org.springframework.web.bind.annotation.RestController
* *) 这样简单点 可以通用
*
* @param 异常对象
*/
@AfterThrowing(pointcut="pointcut()",throwing="e")
public void afterThrowable(Throwable e) {
log.error("切面发生了异常:", e);
//这里可以做个统一异常处理
}
/**
* 初始化日志基础信息
*/
private UaaOperationLogWithBLOBs initLogBean( String paramData,String result,String ip ,String broswer) {
UaaOperationLogWithBLOBs uaaOperationLogWithBLOBs = new UaaOperationLogWithBLOBs();
if (StringUtils.isNotEmpty(broswer)) {
uaaOperationLogWithBLOBs.setBroswer(broswer);
uaaOperationLogWithBLOBs.setClientType(ClientType.PC.getValue());
}
uaaOperationLogWithBLOBs.setTranceId(TraceContext.traceId());
// 服务接口参数
uaaOperationLogWithBLOBs.setInterfaceParams(paramData);
uaaOperationLogWithBLOBs.setNote(ip);
uaaOperationLogWithBLOBs.setOperateTime(new Date());
uaaOperationLogWithBLOBs.setInterfaceReturnResult(result);
return uaaOperationLogWithBLOBs;
}
}
注入bean:CommonExceptionAdvice
代码如下:
/**
*
* @Title: commonExceptionAdvice
* @Description: 注入全局处理器
* @param LogServvice 日志处理具体实现类
* @return
* @return: CommonExceptionAdvice
*/
@Bean
public CommonExceptionAdvice commonExceptionAdvice(LogServvice LogServvice) {
return new CommonExceptionAdvice(LogServvice);
}
五:配置文件添加配置
配置文件添加配置
log.feign.name=log-service ##配置日志feign服务名
log.exclution.uri=list,get ##过滤接口关键字 包含list,get的接口不做记录
六:启动类加注解
实例:
....
@ComponentScan(basePackages={"com.yst.user","com.yst.log.config"}) //com.yst.log.config为日志Aspect位置
....
public class UserApplication {
}