分布式服务日志统一处理简单实现

一: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 {

}

 


 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值