aop 是一个强大的..,思路很简单,就是在进入到一个方法时,获取到进入前,中,后,环绕。这四个状态时的这个方法的属性。我们可以利用它来实现日志功能,可以保证日志模块到业务代码最小化的侵入(就只有一个注解)。在请求每次进到一个controller时,获取到它的请求方法,用户信息,异常等信息记录到数据库里。
1.第一步 自定义一个@Log注解,标记到你要记录日志的controller上面。我的注解有三个参数,分别是事件类型,操作类型,描述。可以在切面里面获取到他们分别标记在控制层上面的值。
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String eventType() default "";
String operationType() default "";
String description() default "";
}
2.第二部 标记到你要记录日志的controller上面,如下,参数可以自定义内容,在切面里面获取。
/**
* @Title gettq
* @Date 2019年8月7日 上午11:25:55
* @author liunn
* @Description 天气
* @param request
* @param entity
* @return
* @throws Exception
*/
@Log(eventType="1",operationType="3",description="天气")
@RequestMapping(value = "/gettq", method = { RequestMethod.POST })
public @ResponseBody JSONObject gettq(HttpServletRequest request,
@ModelAttribute(" entity ") Entity entity) throws Exception {
}
3.第三步,切面获取值,写入到数据库中
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sgcc.zcqsm.reshandover.annotation.Log;
import com.sgcc.zcqsm.reshandover.config.AppProperties;
import com.sgcc.zcqsm.reshandover.entity.PageEntity;
import com.sgcc.zcqsm.reshandover.service.IWebsService;
import com.sgcc.zcqsm.reshandover.service.LogService;
import com.sgcc.zcqsm.reshandover.util.DataUtil;
import com.sgcc.zcqsm.reshandover.util.ResponseInfo;
import com.sgcc.zcqsm.util.ContextHolderUtils;
import com.sgcc.zcqsm.util.JsonUtil;
import com.sgcc.zcqsm.util.ToolUtil;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@Aspect
@Order(5)
@Component
@EnableConfigurationProperties(AppProperties.class)
public class LogAspect {
// private Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private RedisTemplate<Object,Object> redisTemplate;
@Autowired
private LogService logService;
@Autowired
private IWebsService iWebsService;
@Autowired
ObjectMapper objectMapper;
@Autowired
private AppProperties appProperties;
private ThreadLocal<Date> startTime = new ThreadLocal<Date>();
@Pointcut("@annotation(com.sgcc.zcqsm.reshandover.annotation.Log)")
public void pointcut() {
}
/**
* 前置通知,在Controller层操作前拦截
*
* @param joinPoint 切入点
*/
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint) {
// logger.info("进入["+joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()+"]方法里面");
// 获取当前调用时间
startTime.set(new Date());
}
/**
* 正常情况返回
*
* @param joinPoint 切入点
* @param rvt 正常结果
*/
@AfterReturning(pointcut = "pointcut()")
public void doAfter(JoinPoint joinPoint) throws Exception {
//1正常
// logger.info("退出["+joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()+"]方法");
handleLog(joinPoint, null,1);
}
/**
* 异常信息拦截
*
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = "pointcut()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e) throws Exception {
//0 失败
// logger.info("退出["+joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()+"]方法");
handleLog(joinPoint, e,0);
}
@Async
private void handleLog(final JoinPoint joinPoint, final Exception e,int eventResultCode) throws Exception{
if(isOpen()) {
// 获得注解
Method method = getMethod(joinPoint);
Log log = getAnnotationLog(method);
if (log == null) {
return;
}
HttpServletRequest request = ToolUtil.getRequest();
String lrrName=request.getHeader("userId");
// Date now = new Date();
// // 操作数据库日志表
// ErpLog erpLog = new ErpLog();
// erpLog.setErrorCode(0);
// erpLog.setIsDeleted(0);
// // 请求信息
// erpLog.setType(ToolUtil.isAjaxRequest(request) ? "Ajax请求" : "普通请求");
// erpLog.setTitle(log.value());
// erpLog.setHost(request.getRemoteHost());
// erpLog.setUri(request.getRequestURI().toString());
erpLog.setHeader(request.getHeader(HttpHeaders.USER_AGENT));
// erpLog.setHttpMethod(request.getMethod());
// erpLog.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
//
//
// // 请求的方法参数值
// Object[] args = joinPoint.getArgs();
// // 请求的方法参数名称
// LocalVariableTableParameterNameDiscoverer u= new LocalVariableTableParameterNameDiscoverer();
// String[] paramNames = u.getParameterNames(method);
// if (args != null && paramNames != null) {
// StringBuilder params = new StringBuilder();
// params = handleParams(params, args, Arrays.asList(paramNames));
// erpLog.setParams(params.toString());
// }
// String retString = JsonUtil.bean2Json(rvt);
// erpLog.setResponseValue(retString.length() > 5000 ? JsonUtil.bean2Json("请求参数数据过长不与显示") : retString);
// if (e != null) {
// erpLog.setErrorCode(1);
// erpLog.setErrorMessage(e.getMessage());
// }
// Date stime = startTime.get();
// erpLog.setStartTime(stime);
// erpLog.setEndTime(now);
// erpLog.setExecuteTime(now.getTime() - stime.getTime());
// erpLog.setUsername(MySysUser.loginName());
// HashMap<String, String> browserMap = ToolUtil.getOsAndBrowserInfo(request);
// erpLog.setOperatingSystem(browserMap.get("os"));
// erpLog.setBrower(browserMap.get("browser"));
// erpLog.setId(IdUtil.simpleUUID());
// logService.insertSelective(erpLog);
if (e != null) {
iWebsService.saveCwrz(DataUtil.returnExceptionLx(e), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), "程序报错:"+DataUtil.returnExceptionInfo(e), lrrName);
}
//日志信息记录到数据库中
logService.saveLog(lrrName, Integer.parseInt(log.eventType()), Integer.parseInt(log.operationType()),eventResultCode, log.description(),request.getRemoteAddr());
}
}
/**
* 是否存在注解,如果存在就获取
*/
private Log getAnnotationLog(Method method) {
if (method != null) {
return method.getAnnotation(Log.class);
}
return null;
}
private Method getMethod(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method;
}
return null;
}
private StringBuilder handleParams(StringBuilder params, Object[] args, List paramNames) throws JsonProcessingException {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Map) {
Set set = ((Map) args[i]).keySet();
List list = new ArrayList();
List paramList = new ArrayList<>();
for (Object key : set) {
list.add(((Map) args[i]).get(key));
paramList.add(key);
}
return handleParams(params, list.toArray(), paramList);
} else {
if (args[i] instanceof Serializable) {
Class<?> aClass = args[i].getClass();
try {
aClass.getDeclaredMethod("toString", new Class[]{null});
// 如果不抛出NoSuchMethodException 异常则存在 toString 方法 ,安全的writeValueAsString ,否则 走 Object的 toString方法
params.append(" ").append(paramNames.get(i)).append(": ").append(objectMapper.writeValueAsString(args[i]));
} catch (NoSuchMethodException e) {
params.append(" ").append(paramNames.get(i)).append(": ").append(objectMapper.writeValueAsString(args[i].toString()));
}
} else if (args[i] instanceof MultipartFile) {
MultipartFile file = (MultipartFile) args[i];
params.append(" ").append(paramNames.get(i)).append(": ").append(file.getName());
} else {
params.append(" ").append(paramNames.get(i)).append(": ").append(args[i]);
}
}
}
return params;
}
private boolean isOpen() {
String logOpen=appProperties.getLogOpen();
if(getRedis("requestPrivateKey")==null||getRedis("requestPrivateKey")=="") {
String requestPrivateKey =appProperties.getFilterPwd();
setRedis("requestPrivateKey",requestPrivateKey);
}
if(getRedis("SessionOutTimeStr")==null||getRedis("SessionOutTimeStr")=="") {
String SessionOutTimeStr =appProperties.getSessionOutTime();
setRedis("SessionOutTimeStr",SessionOutTimeStr);
}
if(logOpen.equals("true")) {
return true;
}else {
return false;
}
}
private void setRedis(String rkey,Object ObjectStr) {
redisTemplate.opsForValue().set(rkey, ObjectStr, 1L, TimeUnit.DAYS);
}
private Object getRedis(String rkey) {
Object rObject =redisTemplate.opsForValue().get(rkey);
return rObject;
}
}
这里面 需要注意的是
@Pointcut("@annotation(com.sgcc.zcqsm.reshandover.annotation.Log)")
这个注解的值是Log自定义注解的位置,也就是它的包名加类名。
handleLog这个方法是实现日志写入数据库的地方,可以根据实际情况写日志属性。这里面分别对应异常日志,和业务日志。
这时候,日志就实现了。至于怎么写入数据库,持久层。这个简单,不会的话自行百度。
AppProperties是一个自定义的配置文件类。这个也不难。里面配置是否开启日志。sm2公钥。会话超时。
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Configuration
@ConfigurationProperties(prefix = "app", ignoreInvalidFields = false)
@PropertySource("classpath:app.properties")
@Component
public class AppProperties {
private String logOpen;
private String sessionOutTime;
private String filterPwd;
public String getLogOpen() {
return logOpen;
}
public void setLogOpen(String logOpen) {
this.logOpen = logOpen;
}
public String getSessionOutTime() {
return sessionOutTime;
}
public void setSessionOutTime(String sessionOutTime) {
this.sessionOutTime = sessionOutTime;
}
public String getFilterPwd() {
return filterPwd;
}
public void setFilterPwd(String filterPwd) {
this.filterPwd = filterPwd;
}
}
app.properties
app.logOpen=true
app.sessionOutTime=30
app.filterPwd=216c9f564c4b1891864b4b35d05db181864832802f6addf95e7651d28a988184
这里面有一些工具类。我把他们粘贴出来。
import com.google.common.collect.Maps;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
public class ToolUtil {
/**
* 获取客户端的ip信息
*
* @param request
* @return
*/
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknow".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
public static ServletRequestAttributes getRequestAttributes() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
/**
* 获取response
*/
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
/**
* 获取session
*/
public static HttpSession getSession() {
return getRequest().getSession();
}
/**
* 是否是Ajax异步请求
*/
public static boolean isAjaxRequest(HttpServletRequest request) {
String accept = request.getHeader("accept");
if (accept != null && accept.indexOf("application/json") != -1) {
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
return true;
}
String uri = request.getRequestURI();
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
return true;
}
String ajax = request.getParameter("__ajax");
if (StringUtils.inStringIgnoreCase(ajax, "json", "xml")) {
return true;
}
return false;
}
/**
* 获取操作系统,浏览器及浏览器版本信息
*
* @param request
* @return
*/
public static HashMap<String, String> getOsAndBrowserInfo(HttpServletRequest request) {
HashMap<String, String> map = Maps.newHashMap();
String browserDetails = request.getHeader("User-Agent");
String userAgent = browserDetails;
String user = userAgent.toLowerCase();
String os = "";
String browser = "";
//=================OS Info=======================
if (userAgent.toLowerCase().contains("windows")) {
os = "Windows";
} else if (userAgent.toLowerCase().contains("mac")) {
os = "Mac";
} else if (userAgent.toLowerCase().contains("x11")) {
os = "Unix";
} else if (userAgent.toLowerCase().contains("android")) {
os = "Android";
} else if (userAgent.toLowerCase().contains("iphone")) {
os = "IPhone";
} else {
os = "UnKnown, More-Info: " + userAgent;
}
//===============Browser===========================
if (user.contains("edge")) {
browser = (userAgent.substring(userAgent.indexOf("Edge")).split(" ")[0]).replace("/", "-");
} else if (user.contains("msie")) {
String substring = userAgent.substring(userAgent.indexOf("MSIE")).split(";")[0];
browser = substring.split(" ")[0].replace("MSIE", "IE") + "-" + substring.split(" ")[1];
} else if (user.contains("safari") && user.contains("version")) {
browser = (userAgent.substring(userAgent.indexOf("Safari")).split(" ")[0]).split("/")[0]
+ "-" + (userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1];
} else if (user.contains("opr") || user.contains("opera")) {
if (user.contains("opera")) {
browser = (userAgent.substring(userAgent.indexOf("Opera")).split(" ")[0]).split("/")[0]
+ "-" + (userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1];
} else if (user.contains("opr")) {
browser = ((userAgent.substring(userAgent.indexOf("OPR")).split(" ")[0]).replace("/", "-"))
.replace("OPR", "Opera");
}
} else if (user.contains("chrome")) {
browser = (userAgent.substring(userAgent.indexOf("Chrome")).split(" ")[0]).replace("/", "-");
} else if ((user.contains("mozilla/7.0")) || (user.contains("netscape6")) ||
(user.contains("mozilla/4.7")) || (user.contains("mozilla/4.78")) ||
(user.contains("mozilla/4.08")) || (user.contains("mozilla/3"))) {
browser = "Netscape-?";
} else if (user.contains("firefox")) {
browser = (userAgent.substring(userAgent.indexOf("Firefox")).split(" ")[0]).replace("/", "-");
} else if (user.contains("rv")) {
String IEVersion = (userAgent.substring(userAgent.indexOf("rv")).split(" ")[0]).replace("rv:", "-");
browser = "IE" + IEVersion.substring(0, IEVersion.length() - 1);
} else {
browser = "UnKnown, More-Info: " + userAgent;
}
map.put("os", os);
map.put("browser", browser);
return map;
}
}