springboot 实现注解获取操作日志

1.第一步加入pom依赖

<dependency>

  <groupId>org.springframework.boot</groupId>

  <artifactId>spring-boot-starter-aop</artifactId>

</dependency>

 

2.第二步加入自定义一个注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}


3.第三步提供四个工具类

3.1 第一个json转化工具类

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.StringWriter;
public class JsonUtil {
private static ObjectMapper mapper;

static {
mapper=SpringContextHolder.getBean(ObjectMapper.class);
}
public static String bean2Json(Object obj) {
try {
StringWriter sw = new StringWriter();
JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);


mapper.writeValue(gen, obj);
gen.close();

return sw.toString();
}catch (Exception e){
return null;
}

}

public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
try {
return mapper.readValue(jsonStr, objClass);
}
catch (Exception e){
return null;
}
}
}

3.2 第二个获取浏览器信息工具类

import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 {
public static final Logger LOGGER = LoggerFactory.getLogger(ToolUtil.class);



/**
* 获取客户端的ip信息
*
* @param request
* @return
*/
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
LOGGER.info("ipadd : " + 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();
}
LOGGER.info(" ip --> " + ip);
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;
}
}

3.3 第三个字符串处理工具类

 

public class StringUtils {
public static boolean isNullOrEmpty(String s) {
return s == null || "".equals(s);
}

public static boolean isBlank(CharSequence cs) {
int strLen;
if (cs != null && (strLen = cs.length()) != 0) {
for(int i = 0; i < strLen; ++i) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}

return true;
} else {
return true;
}
}

public static boolean isNotBlank(CharSequence cs) {
return !isBlank(cs);
}

/**
* 是否包含字符串
*
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs) {
if (str != null && strs != null) {
for (String s : strs) {
if (str.equalsIgnoreCase(trim(s))) {
return true;
}
}
}
return false;
}

/**
* 去空格
*/
public static String trim(String str) {
return (str == null ? "" : str.trim());
}
}

3.4 第四个获取实体bean处理工具

@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

private static ApplicationContext applicationContext = null;

private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);

/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}

/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}

/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}

/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (logger.isDebugEnabled()){
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}

/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}

/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}

/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}


4.第四步创建一个实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErpLog implements Serializable {

private String id;


/**
* 错误信息
*/

private String errorMessage;
/**
* 返回信息
*/

private String responseValue;
/**
* 操作结束时间
*/

private Date endTime;
/**
* 请求类型
*/

private String type;
/**
* 日志标题
*/

private String title;
/**
* 请求参数
*/

private String params;
/**
* 请求地址
*/

private String uri;
/**
* 操作浏览器
*/

private String brower;
/**
* 操作开始时间
*/

private Date startTime;
/**
* HTTP方法
*/

private String httpMethod;
/**
* 软删除
*/

private Integer isDeleted;
/**
* 请求方法
*/

private String classMethod;
/**
* 执行时间
*/

private Long executeTime;
/**
* 请求主机
*/

private String host;
/**
* 请求头
*/

private String header;
/**
* 操作系统
*/

private String operatingSystem;
/**
* 错误码
*/

private Integer errorCode;
/**
* 操作用户
*/

private String username;
}


5.第五步编写service层,dao层

service

public interface ErpLogService {
List<ErpLog> selectByExample(Object var1);

Integer softDeleteByID(String id);

void insertSelective(ErpLog erpLog);
}

service实现

@Service
public class ErpLogServiceImpl implements ErpLogService {
@Autowired
private ErpLogMapper erplogMapper;



@Override
public List<ErpLog> selectByExample(Object var1) {
// return erplogMapper.selectByExample(var1);
return null;
}

@Override
public Integer softDeleteByID(String id) {
// ErpLog erpLog = erplogMapper.selectByPrimaryKey(id);
// erpLog.setIsDeleted(1);
// return erplogMapper.updateByPrimaryKey(erpLog);
return null;
}

@Override
public void insertSelective(ErpLog erpLog) {
System.out.println(erpLog.toString());
}}

dao层

@Mapper
public interface ErpLogMapper extends BaseMapper<ErpLog> {
}


6.第六步编写核心日志处理业务

 

@Aspect
@Order(5)
@Component
public class LogAspect {

private Logger logger = LoggerFactory.getLogger(LogAspect.class);

@Autowired
private ErpLogService logService;

@Autowired
ObjectMapper objectMapper;

private ThreadLocal<Date> startTime = new ThreadLocal<Date>();

@Pointcut("@annotation(net.dgg.qds.share.annotation.Log)")
public void pointcut() {

}

/**
* 前置通知,在Controller层操作前拦截
*
* @param joinPoint 切入点
*/
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint) {
// 获取当前调用时间
startTime.set(new Date());
}

/**
* 正常情况返回
*
* @param joinPoint 切入点
* @param rvt 正常结果
*/
@AfterReturning(pointcut = "pointcut()", returning = "rvt")
public void doAfter(JoinPoint joinPoint, Object rvt) throws Exception {
handleLog(joinPoint, null, rvt);
}

/**
* 异常信息拦截
*
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = "pointcut()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e) throws Exception {
handleLog(joinPoint, e, null);
}

@Async
public void handleLog(final JoinPoint joinPoint, final Exception e, Object rvt) throws Exception{
// 获得注解
Method method = getMethod(joinPoint);
Log log = getAnnotationLog(method);
if (log == null) {
return;
}
Date now = new Date();
// 操作数据库日志表
ErpLog erpLog = new ErpLog();
erpLog.setErrorCode(0);
erpLog.setIsDeleted(0);
// 请求信息
HttpServletRequest request = ToolUtil.getRequest();
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("");
HashMap<String, String> browserMap = ToolUtil.getOsAndBrowserInfo(request);
erpLog.setOperatingSystem(browserMap.get("os"));
erpLog.setBrower(browserMap.get("browser"));
erpLog.setId("");
logService.insertSelective(erpLog);
}

/**
* 是否存在注解,如果存在就获取
*/
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;
}
}


7.最后在业务controller层加上@Log()注解就好

 

 


8.完成业务,打印结果,根据自己的业务存表,这里展示输出结果

ErpLog(id=, errorMessage=null, responseValue={"code":200,"message":"操作成功","data":{"id":"1234","accumulatedIncome":"5200","unsettledProfit":"2600","shareTimes":"2","orderNum":"2","shareProfit":"3000","recommendCustomers":"2","effectiveResources":"1","signatureNum":"1","recommendedRevenue":"600","userRole":"企帮客","userLevelName":"v1","roleCode":"qds_leader","applyState":"2","settledProfit":"2600","userRoleDescribe":null,"userCode":null,"userPhone":null,"recommendUsername":null,"recommendName":null,"levelCode":null},"errorData":{}}, endTime=Thu May 07 14:25:31 CST 2020, type=普通请求, title=查询用户推广中心信息接口, params= id: "1234", uri=/shareapi/get/promotion_center/information.do, brower=UnKnown, More-Info: Apache-HttpClient/4.5.10 (Java/11.0.6), startTime=Thu May 07 14:25:30 CST 2020, httpMethod=GET, isDeleted=0, classMethod=AllRevenueStatisticsController.getPromotionCenterInformation, executeTime=495, host=127.0.0.1, header=null, operatingSystem=UnKnown, More-Info: Apache-HttpClient/4.5.10 (Java/11.0.6), errorCode=0, username=)

 

 

posted @ 2020-05-07 15:08  历尽千帆归来任是少年  阅读(1691)  评论(0编辑  收藏  举报